SafeDispatch/MotoRepeaterCore/Wireline.cs
2024-02-22 18:43:59 +02:00

2685 lines
129 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.Runtime.Caching;
using SafeMobileLib;
namespace MotoRepeater
{
public class Wireline
{
// all available operations
public enum Operations
{
WL_REGISTRATION_REQUEST = 0x01,
WL_REGISTRATION_STATUS = 0x02,
WL_REGISTRATION_GENERAL_OPS = 0x03,
WL_PROTOCOL_VERSION_QUERY = 0x04,
WL_PROTOCOL_VERSION_QUERY_RESPONSE = 0x05,
WL_CHNL_STATUS = 0x11,
WL_CHNL_STATUS_QUERY = 0x12,
WL_VC_CHNL_CTRL_REQUEST = 0x13,
WL_VC_CHNL_CTRL_STATUS = 0x16,
WL_VC_CSBK_CALL = 0x17,
WL_VC_VOICE_START = 0x18,
WL_VC_VOICE_END_BURST = 0X19,
WL_VC_CALL_SESSION_STATUS = 0x20,
WL_VC_VOICE_BURST = 0x21,
WL_VC_PRIVACY_BURST = 0X22
};
private static byte[] AuthenticationID = new byte[] { 0x01, 0x02, 0x00, 0x6E };
private static byte[] VendorKey = new byte[] { 0x85, 0x8a, 0x5e, 0xa6, 0x8c, 0xf9, 0xa9, 0x7f, 0x49, 0xc7,
0x10, 0x87, 0xe7, 0xf2, 0xb4, 0x64, 0xd5, 0x9c, 0x52, 0x5d };
private static byte[] AuthenticationKey = new byte[] { 0x00 }; // set by the user in the repeater
private static byte[] AuthenticationSignatureKey = new byte[40];
/* System variables */
private Thread wirelineHandlerThread;
public int majorWirelineProtocolVersion = 3;
public byte wirelineProtocolVersion = 0x00;
private bool isWirelineRegistered = false;
private SlotNumber currentSlot = SlotNumber.BothSlots;
private LinkEstablishment le;
/* CRT CALL VARIABLES */
private CSBKCall crtCSBKCallStausSlot1 = new CSBKCall();
private CSBKCall crtCSBKCallStausSlot2 = new CSBKCall();
// store the previous burst type in order to add the missing frames
private int previousBrustTypeSlot1 = 0x06;
private int previousBrustTypeSlot2 = 0x06;
private byte currentWirelineProtocolVersion = 0x00;
private byte oldestWirelineProtocolVersion = 0x00;
private MemoryCache mem = new MemoryCache("MotoTRBOCache");
public Wireline(LinkEstablishment linkEstablishment)
{
this.le = linkEstablishment;
le.OnRegistrationResponseReceived += new LinkEstablishment.RegistrationResponseDEl(le_OnRegistrationResponseReceived);
le.OnWirelineMessageReceived += new LinkEstablishment.WirelineMessageReceivedDEl(le_OnWirelineMessageReceived);
this.OnRadioStatusChanged += new RadioStatusChangedDEl(Wireline_OnRadioStatusChanged);
this.OnCSBKCallReceived += new CSBKCallReceivedDEl(Wireline_OnCSBKCallReceived);
this.OnChannelControlStatusReceived += new ChannelControlStatusReceivedDel(Wireline_OnChannelControlStatusReceived);
this.OnCallSesionStatusReceived += new CallSessionStatusReceivedDEl(Wireline_OnCallSesionStatusReceived);
SetCurrentAndOldestWirelineProtocolVersion();
GenerateAuthenticationSignatureKey();
/* TEST WITH THE VALUES FROM MOTOROLA
AuthenticationID = new byte[] { 0x01, 0x01, 0x00, 0x2A };
VendorKey = new byte[] { 0xcf, 0xd0, 0x25, 0xcb, 0x75, 0x60, 0xdb, 0x13, 0x5d, 0x70,
0x2a, 0x20, 0x1e, 0x65, 0xd0, 0x30, 0x9f, 0xd2, 0xb3, 0xb2 };
//AuthenticationKey = new byte[] { 0xAB, 0xCD }; // set by the user
//AuthenticationSignatureKey = new byte[40];
GenerateAuthenticationSignatureKey();
byte[] pdu;
byte[] restp = AddWirelineSignatureKey(
pdu = new byte[] { 0xb2, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x94, 0xb8, 0xcf, 0x00, 0x00, 0x01, 0x00, 0x01,
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x04, 0x04}
);
//*/
}
/* When a new Wireline Message is received by the Link Establishment */
void le_OnWirelineMessageReceived(byte[] received, string peerIP, Int32 peerPort)
{
try
{
decode(received, peerIP, peerPort);
}
catch (Exception ex)
{
Console.Write("\n\n " + ex.ToString());
}
}
void le_OnRegistrationResponseReceived(bool isValid)
{
if (isValid)
{
Utils.WriteLine("LE Registered. Starting Wireline registration...");
List<RegEntry> regEntries = new List<RegEntry>();
regEntries.Add(new RegEntry(AddressType.AllIndividualCall, 1, 16777215));
regEntries.Add(new RegEntry(AddressType.AllTalkgroupCall, 1, 16777215));
regEntries.Add(new RegEntry(AddressType.AllLocalTalkgroupsCall, 1, 16777215));
regEntries.Add(new RegEntry(AddressType.AllWideTakgroupsCall, 1, 16777215));
//reg.regEntriesList.Add(new RegEntry(AddressType.GroupCall, 1, 1024));
Register(le.GetRegisteredSlots(), regEntries);
//le.SendLEMessage(createWirelineMessage(Operations.WL_REGISTRATION_REQUEST, reg));
}
}
// set the current and oldest wireline Protocol Version
private void SetCurrentAndOldestWirelineProtocolVersion()
{
// clear previous values
currentWirelineProtocolVersion = 0x00;
oldestWirelineProtocolVersion = 0x00;
// create two 'bit' arrays for System ID and Protocol Version
bool[] systemID = new bool[6];
bool[] protocolVers = new bool[10];
bool[] oldestProtocolVers = new bool[10];
switch (le.systemType)
{
case MotoRepeater.LinkEstablishment.SystemType.IP_SITE_CONNECT:
{
systemID = new bool[] { false, false, false, false, false, true };
break;
}
case MotoRepeater.LinkEstablishment.SystemType.CAPACITY_PLUS:
{
systemID = new bool[] { false, false, false, false, true, false };
break;
}
case MotoRepeater.LinkEstablishment.SystemType.LINKED_CAPACITY_PLUS:
{
systemID = new bool[] { false, false, false, true, false, false };
break;
}
}
switch (le.releaseNumber)
{
case MotoRepeater.LinkEstablishment.SystemReleaseNumber.R23A:
{
majorWirelineProtocolVersion = 3;
wirelineProtocolVersion = 0x0C; // 0b00000100
break;
}
case MotoRepeater.LinkEstablishment.SystemReleaseNumber.R23:
case MotoRepeater.LinkEstablishment.SystemReleaseNumber.R22:
{
majorWirelineProtocolVersion = 1;
wirelineProtocolVersion = 0x04; // 0b00001100
break;
}
}
currentWirelineProtocolVersion = wirelineProtocolVersion;
oldestWirelineProtocolVersion = 0x04;
}
// function which will generate the authentication Signature KEY which is used as HMAC-SHA1 key for
// generating the Authentication Signature bytes which are added to each Call Control Wireline Messages
private void GenerateAuthenticationSignatureKey()
{
List<byte> authSign = new List<byte>();
authSign.AddRange(VendorKey);
// padd with zeros
for (int i = 0; i < 20 - AuthenticationKey.Length; i++)
{
authSign.Add(0x00);
}
foreach (byte by in AuthenticationKey)
authSign.Add(by);
// save the authentication signature key which is compute from the Vendor Key and Authentication Key
AuthenticationSignatureKey = authSign.ToArray();
}
/// <summary>
/// Returns the byte array which needs to be send, already containing the wireline authentication signature
/// </summary>
/// <param name="wirelineMessage">the byte array which will be used used for genereting the Wireline Signature Key</param>
/// <returns>A byte array containing the original byte array followed by the Authentication ID [Motorola Provided] and
/// the Authentication Signature Key [Generated using HMACSha1 algoritm]</returns>
private byte[] AddWirelineSignatureKey(byte[] wirelineMessage)
{
// encrypts the wireline messages using Authentication Signature as the key
System.Security.Cryptography.HMACSHA1 hmacSha1 = new System.Security.Cryptography.HMACSHA1(AuthenticationSignatureKey);
hmacSha1.ComputeHash(wirelineMessage);
// trim the authentication signature to only 10 bytes
byte[] truncatedAuthenticationSignature = new byte[10];
for (int i = 0; i < 10; i++)
truncatedAuthenticationSignature[i] = hmacSha1.Hash[i];
// add Authentication Key and Authentication Signature to the end of the wireline message
List<byte> resp = new List<byte>(wirelineMessage);
resp.AddRange(AuthenticationID);
resp.AddRange(truncatedAuthenticationSignature);
// return the complete wireline message
return resp.ToArray();
}
#region WIRELINE MESSAGES
/// <summary>
/// Wireline registration procedure. This procedure creats the wireline message which will be sent to the
/// repeater in order to create the wireline registration
/// </summary>
/// <param name="slot">The slot on which the wireline registration is required</param>
/// <param name="regEntriesList">The list of entries for which the call messages will be received and parsed</param>
public void Register(SlotNumber slot, List<RegEntry> regEntriesList)
{
// create a wireline registration object and populate it with the required values
WirelineRegistration reg = new WirelineRegistration();
reg.slotNumber = slot;
reg.wirelineChannelStatus = Wireline.WirelineChannelStatus.NotRegisteredWLStatus;
// copy all registry
foreach (RegEntry regEntry in regEntriesList)
reg.regEntriesList.Add(regEntry);
// add Authentication Signature to the wireline message
byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_REGISTRATION_REQUEST, reg));
// send wireline message to the repeater
SendWirelineMessage(toSend);
}
/// <summary>
/// Deregister a prior registration on a desired slot
/// </summary>
/// <param name="slot">The slot on which the wireline registration was done</param>
public void DeRegister(SlotNumber slot)
{
WirelineQueryRegistration queryReg = new WirelineQueryRegistration();
queryReg.regOpCode = RegistrationOperationCode.DeRegister;
queryReg.slotNumber = slot;
// add Authentication Signature to the wireline message
byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_REGISTRATION_GENERAL_OPS, queryReg));
// send wireline message to the repeater
SendWirelineMessage(toSend);
}
// query a prior registration to find out the Identifier of the registration
public void QueryRegistration(SlotNumber slot)
{
WirelineQueryRegistration queryReg = new WirelineQueryRegistration();
queryReg.regOpCode = RegistrationOperationCode.QueryRegStatus;
queryReg.slotNumber = slot;
// add Authentication Signature to the wireline message
byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_REGISTRATION_GENERAL_OPS, queryReg));
SendWirelineMessage(toSend);
}
// check what protocol version is supported by the repeater
public void ProtocolVersionQuery()
{
// add Authentication Signature to the wireline message
byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_PROTOCOL_VERSION_QUERY, null));
SendWirelineMessage(toSend);
}
// request status from the repeater about the rest channel or conventional channel
// depending on the system type
public void ChannelStatusReq(SlotNumber slot)
{
CSBKCall call = new CSBKCall();
call.slotNumber = slot;
// add Authentication Signature to the wireline message
byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_CHNL_STATUS_QUERY, call));
SendWirelineMessage(toSend);
}
#region ENABLE/DISABLE
/// <summary>
/// Enable a radio on a specific slot
/// </summary>
/// <param name="radioID">The radio ID of the unit which needs to be enabled</param>
/// <param name="slot">Slot number on which the request will be sent</param>
public void EnableRadioOnSlot(Int64 radioID, SlotNumber slot)
{
CSBKCall call = new CSBKCall();
call.slotNumber = slot;
call.callType = CallType.RadioUninhibitRequest;
call.sourceID = le.myselfPeer.peerID;
call.targetID = radioID;
// add Authentication Signature to the wireline message
byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call));
SendWirelineMessage(toSend);
}
/// <summary>
/// Enable a radio on the current slot
/// </summary>
/// <param name="radioID">The radio ID of the unit which needs to be enabled</param>
public void EnableRadio(Int64 radioID)
{
EnableRadioOnSlot(radioID, GetCurrentSlot());
}
/// <summary>
/// Disable a radio on a specific slot
/// </summary>
/// <param name="radioID">The radio ID of the unit which needs to be disabled</param>
/// <param name="slot">Slot number on which the request will be sent</param>
public void DisableRadioOnSlot(Int64 radioID, SlotNumber slot)
{
CSBKCall call = new CSBKCall();
call.slotNumber = slot;
call.callType = CallType.RadioInhibitRequest;
call.sourceID = le.myselfPeer.peerID;
call.targetID = radioID;
// add Authentication Signature to the wireline message
byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call));
// if thread allready exists I should send the commnad only once
if (mem.Contains(String.Format("{0}.{1}", radioID, slot)))
{
// get the thread and reset the counter to 0
//Thread running = (Thread)mem.Get(String.Format("{0}.{1}", radioID, slot));
return;
}
// create a thread which will resend the message after 3 seconds
Thread p = new Thread(() =>
{
// save the message which needs to be sent
byte[] toSendd = (byte[])toSend.Clone();
Int64 radioIDD = radioID;
SlotNumber slott = slot;
int count = 0;
// run this procedure for three times or while the answer is received from the repeater
while (mem.Contains(String.Format("{0}.{1}", radioID, slot)))
{
Thread.CurrentThread.IsBackground = true;
// send wireline message each 3 seconds
if (count++ % 30 == 0)
{
//Utils.WriteLine(String.Format("Thread {0}.{1} is alive", radioID, slot));
SendWirelineMessage(toSendd);
}
Thread.Sleep(100); // sleep 100 ms
}
//Utils.WriteLine(String.Format("Thread {0}.{1} ended", radioID, slot));
});
p.Name = String.Format("{0}.{1}", radioID, slot);
p.Start();
// add thread to the memory cache and set live duration to 10 seconds
mem.Add(p.Name, p, new CacheItemPolicy() { AbsoluteExpiration = DateTime.UtcNow.AddSeconds(10) });
}
/// <summary>
/// Disable a radio on the current slot
/// </summary>
/// <param name="radioID">The radio ID of the unit which needs to be disabled</param>
public void DisableRadio(Int64 radioID)
{
DisableRadioOnSlot(radioID, GetCurrentSlot());
}
/// <summary>
/// Event Callback for when a radio changes its status to Enable or Disable.
/// This callback will have to remove the thread that sends the Inhibit [disable] request from memCache
/// </summary>
/// <param name="radioID">The radio which changed its status</param>
/// <param name="slot">The slot on which the radio had reported</param>
/// <param name="isEnabled">The radio state. Will be true if the radio is Enabled or false otherwise</param>
void Wireline_OnRadioStatusChanged(long radioID, Wireline.SlotNumber slot, bool isEnabled)
{
// close the thread which sends the Inhibit [disble] command, if exists
if (!isEnabled && mem.Contains(String.Format("{0}.{1}", radioID, slot)))
{
mem.Remove(String.Format("{0}.{1}", radioID, slot));
}
}
/// <summary>
/// Acknowledge an emergency received from a field radio on the current selected slot
/// </summary>
/// <param name="radioID">The field radio which had sent the emergency</param>
public void EmergencyAck(Int64 radioID)
{
EmergencyACKOnSlot(radioID, GetCurrentSlot());
}
/// <summary>
/// Acknowledge an emergency received from a field radio
/// </summary>
/// <param name="radioID">The field radio which had sent the emergency</param>
/// <param name="slot">The slot on which the request will be sent</param>
public void EmergencyACKOnSlot(Int64 radioID, SlotNumber slot)
{
// response to Emergency with Emerg ACK
CSBKCall csbkCall = new CSBKCall();
csbkCall.slotNumber = slot;
csbkCall.sourceID = le.myselfPeer.peerID;
csbkCall.targetID = radioID;
csbkCall.callType = CallType.EmergencyCSBKAlertResponse;
this.SendWirelineMessage(AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, csbkCall)));
}
#endregion
#region CALL
/// <summary>
/// Initiate a private call on the specified slot
/// </summary>
/// <param name="radioID">The radio ID of the field station which needs to be called</param>
/// <param name="slot">Slot number on which the request will be sent</param>
public void InitiatePrivateCallOnSlot(Int64 radioID, SlotNumber slot)
{
CSBKCall call = new CSBKCall();
call.callType = CallType.PrivateCallRequest;
call.preambleDuration = PreambleDuration.BurstDurationMS;
call.preambleValue = 0x03;
call.slotNumber = slot;
call.sourceID = (slot == SlotNumber.Slot1 ? le.myselfPeer.slot1ID : le.myselfPeer.slot2ID);
call.targetID = radioID;
if ((slot == SlotNumber.Slot1 && crtCSBKCallStausSlot1 != null) ||
(slot == SlotNumber.Slot2 && crtCSBKCallStausSlot2 != null))
{
// save current call parameters
crtCSBKCallStausSlot1 = call;
// add Authentication Signature to the wireline message
byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call));
SendWirelineMessage(toSend);
}
}
public void InitiatePrivateCallOnSlotAfterPreamble(Int64 radioID, SlotNumber slot)
{
CSBKCall call = new CSBKCall();
call.callType = CallType.PrivateCallResponse;
call.preambleDuration = PreambleDuration.BurstDurationMS;
call.preambleValue = 0x03;
call.slotNumber = slot;
call.sourceID = (slot == SlotNumber.Slot1 ? le.myselfPeer.slot1ID : le.myselfPeer.slot2ID);
call.targetID = radioID;
call.callAttributes = CallAttributes.SmartAfterPreamble;
if ((slot == SlotNumber.Slot1 && crtCSBKCallStausSlot1 != null) ||
(slot == SlotNumber.Slot2 && crtCSBKCallStausSlot2 != null))
{
// save current call parameters
crtCSBKCallStausSlot1 = call;
// add Authentication Signature to the wireline message
byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call));
SendWirelineMessage(toSend);
}
}
/// <summary>
/// Terminate a private call on the specified slot
/// </summary>
/// <param name="radioID">The radio ID of the field station which needs to end the call</param>
/// <param name="slot">Slot number on which the request will be sent</param>
public void EndPrivateCallOnSlot(Int64 radioID, SlotNumber slot)
{
CSBKCall call = new CSBKCall();
call.callType = CallType.PrivateCallRequest;
call.preambleDuration = PreambleDuration.BurstDurationMS;
call.preambleValue = 0x03;
call.slotNumber = slot;
call.sourceID = slot == SlotNumber.Slot1 ? le.myselfPeer.slot1ID : le.myselfPeer.slot2ID;
call.targetID = radioID;
// add Authentication Signature to the wireline message
byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call));
SendWirelineMessage(toSend);
}
/// <summary>
/// Initiate a group call on the specified slot
/// </summary>
/// <param name="groupID">The group ID of the field stations which needs to be called</param>
/// <param name="slot">Slot number on which the request will be sent</param>
public void InitiateGroupCallOnSlot(Int64 groupID, SlotNumber slot)
{
CSBKCall call = new CSBKCall();
call.callType = CallType.GroupVoiceCall;
call.preambleDuration = PreambleDuration.BurstDurationMS;
call.preambleValue = 0x03;
call.slotNumber = slot;
call.sourceID = slot == SlotNumber.Slot1 ? le.myselfPeer.slot1ID : le.myselfPeer.slot2ID;
call.targetID = groupID;
// save current call parameters
crtCSBKCallStausSlot1 = call;
// add Authentication Signature to the wireline message
byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call));
SendWirelineMessage(toSend);
}
/// <summary>
/// Terminate a group call on the specified slot
/// </summary>
/// <param name="groupID">The group ID of the field stations which needs to end the call</param>
/// <param name="slot">Slot number on which the request will be sent</param>
public void EndGroupCallOnSlot(Int64 groupID, SlotNumber slot)
{
CSBKCall call = new CSBKCall();
call.callType = CallType.GroupVoiceCall;
call.preambleDuration = PreambleDuration.BurstDurationMS;
call.preambleValue = 0x03;
call.slotNumber = slot;
call.sourceID = slot == SlotNumber.Slot1 ? le.myselfPeer.slot1ID : le.myselfPeer.slot2ID;
call.targetID = groupID;
// add Authentication Signature to the wireline message
byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call));
SendWirelineMessage(toSend);
}
/// <summary>
/// Initiate an all call on the specified slot
/// </summary>
/// <param name="slot">Slot number on which the request will be sent</param>
public void InitiateAllCallOnSlot(SlotNumber slot)
{
CSBKCall call = new CSBKCall();
call.callType = CallType.AllCall;
call.preambleDuration = PreambleDuration.BurstDurationMS;
call.preambleValue = 0x03;
call.slotNumber = slot;
call.sourceID = slot == SlotNumber.Slot1 ? le.myselfPeer.slot1ID : le.myselfPeer.slot2ID;
call.targetID = 16777215;
// save current call parameters
crtCSBKCallStausSlot1 = call;
// add Authentication Signature to the wireline message
byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call));
SendWirelineMessage(toSend);
}
/// <summary>
/// Terminate an all call on the specified slot
/// </summary>
/// <param name="slot">Slot number on which the request will be sent</param>
public void EndAllCallOnSlot(SlotNumber slot)
{
CSBKCall call = new CSBKCall();
call.callType = CallType.AllCall;
call.preambleDuration = PreambleDuration.BurstDurationMS;
call.preambleValue = 0x03;
call.slotNumber = slot;
call.sourceID = slot == SlotNumber.Slot1 ? le.myselfPeer.slot1ID : le.myselfPeer.slot2ID;
call.targetID = 16777215;
// add Authentication Signature to the wireline message
byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call));
SendWirelineMessage(toSend);
}
/// <summary>
/// Event Callback for a channel control status received. The event is triggered each time a call changes
/// it's status, this is equal to channel status had changed
/// </summary>
/// <param name="sourceID">The radio ID of the field radio which had initiated the call</param>
/// <param name="targetID">The radio ID of the field radio which had been called</param>
/// <param name="callType">The type of call which is in progress/was made</param>
/// <param name="chnCtrlStatus">The status of the channel </param>
/// <param name="slot">The slot on which the call was made</param>
void Wireline_OnChannelControlStatusReceived(Int64 callID, CallType callType, ChannelControlStatus chnCtrlStatus, SlotNumber slot)
{
// call has been accepted and I can send voice as from the docs/Smart Wireshark
if (chnCtrlStatus == ChannelControlStatus.SmartInProgress)
{
Thread p = new Thread(new ThreadStart(CSBKVoiceCall));
p.Start();
}
}
/// <summary>
/// Event Callback for a call session status received. The event is triggered each time a call is transmited,
/// declined etc.
/// </summary>
/// <param name="sourceID">The radio ID of the field radio which had initiated the call</param>
/// <param name="targetID">The radio ID of the field radio which had been called</param>
/// <param name="callType">The type of call which is in progress/was made</param>
/// <param name="callSessionStatus">The status of the call on Transportation Layer</param>
/// <param name="slot">The slot on which the call was made</param>
void Wireline_OnCallSesionStatusReceived(Int64 sourceID, Int64 targertID, CallType callType, CallSessionStatus callSessionStatus, SlotNumber slot)
{
// get csbkCall for current slot number
CSBKCall crtCSBKCall = (slot == SlotNumber.Slot1 ? crtCSBKCallStausSlot1 : crtCSBKCallStausSlot2);
// I should resend the call request because the call is hanged as a result of a CSBK Preamble recieved
if (callSessionStatus == CallSessionStatus.CallHang && crtCSBKCall.isCSBKPreambleReceived)
{
switch (crtCSBKCall.callType)
{
// initiate the all call on selected slot
case CallType.AllCall:
InitiateAllCallOnSlot(slot);
break;
// initiate the group call on selected slot
case CallType.GroupVoiceCall:
InitiateGroupCallOnSlot(crtCSBKCall.targetID, slot);
break;
// initiate the private call on selected slot
case CallType.PrivateCallRequest:
InitiatePrivateCallOnSlotAfterPreamble(crtCSBKCall.targetID, slot);
break;
}
crtCSBKCall.isCSBKPreambleReceived = false;
}
}
/// <summary>
///
/// </summary>
/// <param name="callID"></param>
/// <param name="isAccepted"></param>
private void Wireline_OnCSBKCallReceived(Int64 sourceID, Int64 targetID, CallType callType, SlotNumber slot)
{
// get csbkCall for current slot number
CSBKCall crtCSBKCall = (slot == SlotNumber.Slot1 ? crtCSBKCallStausSlot1 : crtCSBKCallStausSlot2);
// flag that the Preamble CSBK was received and I can now resend the cmd when the hang status is received
crtCSBKCall.isCSBKPreambleReceived = true;
}
private void CSBKVoiceCall()
{
for(int i=0; i<30; i++)
{
CSBKCall csbkCall = new CSBKCall();
csbkCall.slotNumber = SlotNumber.Slot1;
// add Authentication Signature to the wireline message
byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_VOICE_BURST, csbkCall));
Thread.Sleep(100);
SendWirelineMessage(toSend);
}
}
#endregion
// return the current slot number
public SlotNumber GetCurrentSlot()
{
return currentSlot;
}
public SlotNumber GetSlotNumber(int slotNumber)
{
switch (slotNumber)
{
case 1: return SlotNumber.Slot1;
case 2: return SlotNumber.Slot2;
case 3: return SlotNumber.BothSlots;
case 0:
{
if (le.systemType == LinkEstablishment.SystemType.IP_SITE_CONNECT)
return SlotNumber.SiteIPAddress;
else if (le.systemType == LinkEstablishment.SystemType.CAPACITY_PLUS)
return SlotNumber.RestChannel;
else
return SlotNumber.BothSlots;
}
default: return SlotNumber.BothSlots;
}
}
#endregion
private Int32 csbkCallParam1 = 0x7bee;
private Int32 csbkCallParam2 = 0x3178;
private Int32 csbkCallParam3 = 0x94;
private Int32 csbkCallParam4 = 0x2A;
private static int val = 0x01;
private byte[] createWirelineMessage(Operations operation, Object parameters)
{
List<byte> responseList = new List<byte>();
// opCode
responseList.Add(0xB2);
// ID of the sending peer = me
responseList.Add(BitConverter.GetBytes(le.myselfPeer.peerID)[3]);
responseList.Add(BitConverter.GetBytes(le.myselfPeer.peerID)[2]);
responseList.Add(BitConverter.GetBytes(le.myselfPeer.peerID)[1]);
responseList.Add(BitConverter.GetBytes(le.myselfPeer.peerID)[0]);
// increment registration PDU number
if (operation == Operations.WL_REGISTRATION_REQUEST ||
operation == Operations.WL_REGISTRATION_GENERAL_OPS)
WirelineRegistration.registrationPduID++;
byte[] response = { };
switch (operation)
{
#region WL_REGISTRATION_REQUEST
case Operations.WL_REGISTRATION_REQUEST:
{
WirelineRegistration wireReg = null;
try
{
// get the paramerets in the correct form
wireReg = (WirelineRegistration)parameters;
}
catch (Exception)
{
// THROW AN EXCEPTION
return new byte[0];
}
/* Wireline Operation Code*/
responseList.Add((byte)Operations.WL_REGISTRATION_REQUEST);
/* registrationSlotNumber - Only for de-register operation in SS/IPSC mode */
if (le.systemType == LinkEstablishment.SystemType.LINKED_CAPACITY_PLUS)
responseList.Add((byte)0x00);
else
responseList.Add((byte)wireReg.slotNumber);
/* registrationPduID - The identifier of the registration PDU [Uint32] */
responseList.Add(BitConverter.GetBytes(WirelineRegistration.registrationPduID)[3]);
responseList.Add(BitConverter.GetBytes(WirelineRegistration.registrationPduID)[2]);
responseList.Add(BitConverter.GetBytes(WirelineRegistration.registrationPduID)[1]);
responseList.Add(BitConverter.GetBytes(WirelineRegistration.registrationPduID)[0]);
/* registrationID - The identifier associated with the registration profile in this PDU. [Uint16] */
responseList.Add(BitConverter.GetBytes(wireReg.registrationID)[1]);
responseList.Add(BitConverter.GetBytes(wireReg.registrationID)[0]);
/* wirelineStatusRegistration - Registration flag for Wireline Channel Status [Uint8] */
responseList.Add((byte)wireReg.wirelineChannelStatus);
/* numberOfRegistrationEntries - Number of registration entries in this registration message [Uint8] */
responseList.Add((byte)wireReg.regEntriesList.Count);
foreach (RegEntry entity in wireReg.regEntriesList)
{
/* AddressType - Address type of the call being registered in this entry */
responseList.Add((byte)entity.addressType);
/* addressRangeStart - Start of radio/group addresses (CAI ID) range in this entry [Uint32] */
responseList.Add(BitConverter.GetBytes(entity.addressRangeStart)[3]);
responseList.Add(BitConverter.GetBytes(entity.addressRangeStart)[2]);
responseList.Add(BitConverter.GetBytes(entity.addressRangeStart)[1]);
responseList.Add(BitConverter.GetBytes(entity.addressRangeStart)[0]);
/* addressRangeEnd - Start of radio/group addresses (CAI ID) range in this entry [Uint32] */
responseList.Add(BitConverter.GetBytes(entity.addressRangeEnd)[3]);
responseList.Add(BitConverter.GetBytes(entity.addressRangeEnd)[2]);
responseList.Add(BitConverter.GetBytes(entity.addressRangeEnd)[1]);
responseList.Add(BitConverter.GetBytes(entity.addressRangeEnd)[0]);
/* Reserved field */
responseList.Add((byte)0x80);
/* CSBK Attributes - Attributes of CSBK Registration in this entry */
responseList.Add((byte)entity.csbkAttributes);
/* Reserved field */
responseList.Add((byte)0x00);
}
/* Current or Accepted Wireline Protocol Version */
responseList.Add(currentWirelineProtocolVersion);
responseList.Add(oldestWirelineProtocolVersion);
// convert the list of bytes to a byte array
response = new byte[responseList.Count];
for (int i = 0; i < responseList.Count; i++)
response[i] = responseList[i];
break;
}
#endregion
#region WL_REGISTRATION_GENERAL_OPS
case Operations.WL_REGISTRATION_GENERAL_OPS:
{
WirelineQueryRegistration queryReg = null;
try
{
// get the paramerets in the correct form
queryReg = (WirelineQueryRegistration)parameters;
}
catch (Exception)
{
// THROW AN EXCEPTION
return new byte[0];
}
/* Wireline Operation Code*/
responseList.Add((byte)Operations.WL_REGISTRATION_GENERAL_OPS);
/* registrationSlotNumber - Only for de-register operation in SS/IPSC mode */
if(le.systemType == LinkEstablishment.SystemType.IP_SITE_CONNECT && queryReg.regOpCode == RegistrationOperationCode.DeRegister)
responseList.Add((byte)queryReg.slotNumber);
else
responseList.Add((byte)0x00);
/* registrationPduID - The identifier of the registration PDU [Uint32] */
responseList.Add(BitConverter.GetBytes(queryReg.registrationPduID)[3]);
responseList.Add(BitConverter.GetBytes(queryReg.registrationPduID)[2]);
responseList.Add(BitConverter.GetBytes(queryReg.registrationPduID)[1]);
responseList.Add(BitConverter.GetBytes(queryReg.registrationPduID)[0]);
/* registrationOperationOpcode - The registration Operation Code [Uint8] */
responseList.Add((byte)queryReg.regOpCode);
/* Current or Accepted Wireline Protocol Version */
responseList.Add(currentWirelineProtocolVersion);
responseList.Add(oldestWirelineProtocolVersion);
// convert the list of bytes to a byte array
response = new byte[responseList.Count];
for (int i = 0; i < responseList.Count; i++)
response[i] = responseList[i];
break;
}
#endregion
#region WL_PROTOCOL_VERSION_QUERY
case Operations.WL_PROTOCOL_VERSION_QUERY:
{
/* Wireline Operation Code*/
responseList.Add((byte)Operations.WL_PROTOCOL_VERSION_QUERY);
/* Reserved field */
responseList.Add((byte)0x00);
/* query ID - ID of the protocol version query PDU. Response from the repeater
* pertaining to this message has the same Query ID value */
responseList.Add(BitConverter.GetBytes(queryID)[3]);
responseList.Add(BitConverter.GetBytes(queryID)[2]);
responseList.Add(BitConverter.GetBytes(queryID)[1]);
responseList.Add(BitConverter.GetBytes(queryID)[0]);
//increment query id
queryID++;
/* Current or Accepted Wireline Protocol Version */
responseList.Add(currentWirelineProtocolVersion);
responseList.Add(oldestWirelineProtocolVersion);
// convert the list of bytes to a byte array
response = new byte[responseList.Count];
for (int i = 0; i < responseList.Count; i++)
response[i] = responseList[i];
break;
}
#endregion
#region WL_CHNL_STATUS_QUERY
case Operations.WL_CHNL_STATUS_QUERY:
{
CSBKCall call = null;
try
{
// get the paramerets in the correct form
call = (CSBKCall)parameters;
}
catch (Exception)
{
// THROW AN EXCEPTION
return new byte[0];
}
/* Wireline Operation Code*/
responseList.Add((byte)Operations.WL_CHNL_STATUS_QUERY);
/* slot number - Specifies the channel/slot reporting the channel status.*/
responseList.Add((byte)call.slotNumber);
/* Current or Accepted Wireline Protocol Version */
responseList.Add(currentWirelineProtocolVersion);
responseList.Add(oldestWirelineProtocolVersion);
// convert the list of bytes to a byte array
response = new byte[responseList.Count];
for (int i = 0; i < responseList.Count; i++)
response[i] = responseList[i];
break;
}
#endregion
#region WL_VC_CHNL_CTRL_REQUEST
/*
* This message is sent from the third party applications to the repeater peer to initial the
* CSBK call or send CSBK call response
*/
case Operations.WL_VC_CHNL_CTRL_REQUEST:
{
CSBKCall call = null;
try
{
// get the paramerets in the correct form
call = (CSBKCall)parameters;
}
catch (Exception )
{
// THROW AN EXCEPTION
return new byte[0];
}
/* Wireline Operation Code*/
responseList.Add((byte)Operations.WL_VC_CHNL_CTRL_REQUEST);
/* slot number - Specifies the channel/slot reporting the channel status */
responseList.Add((byte)call.slotNumber);
/* call ID */
responseList.Add(BitConverter.GetBytes(call.callID)[3]);
responseList.Add(BitConverter.GetBytes(call.callID)[2]);
responseList.Add(BitConverter.GetBytes(call.callID)[1]);
responseList.Add(BitConverter.GetBytes(call.callID)[0]);
/* callType - The type of call */
responseList.Add((byte)call.callType);
/* source ID - The ID of the subscriber that initiated the call */
responseList.Add(BitConverter.GetBytes(call.sourceID)[3]);
responseList.Add(BitConverter.GetBytes(call.sourceID)[2]);
responseList.Add(BitConverter.GetBytes(call.sourceID)[1]);
responseList.Add(BitConverter.GetBytes(call.sourceID)[0]);
/* target ID - The ID of the subscriber that initiated the call */
responseList.Add(BitConverter.GetBytes(call.targetID)[3]);
responseList.Add(BitConverter.GetBytes(call.targetID)[2]);
responseList.Add(BitConverter.GetBytes(call.targetID)[1]);
responseList.Add(BitConverter.GetBytes(call.targetID)[0]);
/* accessCriteria - The priority type to access the channel */
responseList.Add((byte)call.accessCriteria);
/* callAttributes - General Call Attributes */
responseList.Add((byte)call.callAttributes);
/* Reserved field */
responseList.Add((byte)0x00);
/* preambleDuration - Only available for CSBK Call.
* The numbers of Preamble bursts send before transmission. */
if (call.preambleDuration.ToString().Contains("BurstDurationMS"))
responseList.Add((byte)call.preambleValue);
else
responseList.Add((byte)0x00);
/* Reserved field */
responseList.Add((byte)0x00);
responseList.Add((byte)0x00);
/* CSBK Arguments - Only available for CSBK call
* The arguments encoded in the DMR CSBK burst [Uint64] */
responseList.Add((byte)0x00);
responseList.Add((byte)0x00); // reserved
responseList.Add((byte)0x00);
responseList.Add((byte)0x00);
responseList.Add((byte)0x00);
responseList.Add((byte)0x00);
responseList.Add((byte)0x00);
responseList.Add((byte)0x00);
/*
responseList.Add(BitConverter.GetBytes(call.targetID)[2]);
responseList.Add(BitConverter.GetBytes(call.targetID)[1]);
responseList.Add(BitConverter.GetBytes(call.targetID)[0]);
responseList.Add(BitConverter.GetBytes(call.sourceID)[2]);
responseList.Add(BitConverter.GetBytes(call.sourceID)[1]);
responseList.Add(BitConverter.GetBytes(call.sourceID)[0]);
*/
/* Current or Accepted Wireline Protocol Version */
responseList.Add(currentWirelineProtocolVersion);
responseList.Add(oldestWirelineProtocolVersion);
// convert the list of bytes to a byte array
response = new byte[responseList.Count];
for (int i = 0; i < responseList.Count; i++)
response[i] = responseList[i];
break;
}
#endregion
#region WL_VC_VOICE_BURST
/*
* This message is sent from the third party applications to the repeater peer containing
* all the voice data [I hope: pray]
*/
case Operations.WL_VC_VOICE_BURST:
{
CSBKCall crtCSBKCall = null;
try
{
// get the paramerets in the correct form
crtCSBKCall = (CSBKCall)parameters;
// set current csbk call values depending on the slot
crtCSBKCall = (crtCSBKCall.slotNumber == SlotNumber.Slot1 ? crtCSBKCallStausSlot1 : crtCSBKCallStausSlot2);
}
catch (Exception)
{
// THROW AN EXCEPTION
return new byte[0];
}
/* Wireline Operation Code*/
responseList.Add((byte)Operations.WL_VC_VOICE_BURST);
/* slot number - Specifies the channel/slot reporting the channel status */
responseList.Add((byte)crtCSBKCall.slotNumber);
/* call ID */
responseList.Add(BitConverter.GetBytes(crtCSBKCall.crtCallID)[3]);
responseList.Add(BitConverter.GetBytes(crtCSBKCall.crtCallID)[2]);
responseList.Add(BitConverter.GetBytes(crtCSBKCall.crtCallID)[1]);
responseList.Add(BitConverter.GetBytes(crtCSBKCall.crtCallID)[0]);
/* callType - The type of call */
responseList.Add((byte)crtCSBKCall.callType);
/* source ID - The ID of the subscriber that initiated the call */
responseList.Add(BitConverter.GetBytes(crtCSBKCall.sourceID)[3]);
responseList.Add(BitConverter.GetBytes(crtCSBKCall.sourceID)[2]);
responseList.Add(BitConverter.GetBytes(crtCSBKCall.sourceID)[1]);
responseList.Add(BitConverter.GetBytes(crtCSBKCall.sourceID)[0]);
/* target ID - The ID of the subscriber that initiated the call */
responseList.Add(BitConverter.GetBytes(crtCSBKCall.targetID)[3]);
responseList.Add(BitConverter.GetBytes(crtCSBKCall.targetID)[2]);
responseList.Add(BitConverter.GetBytes(crtCSBKCall.targetID)[1]);
responseList.Add(BitConverter.GetBytes(crtCSBKCall.targetID)[0]);
/* RESERVED ? ? ? */
responseList.Add(0xA0);
/* RESERVED */
responseList.Add(0x00);
/* callAttributes or Preamble Duration */
responseList.Add((byte)crtCSBKCall.callAttributes);
/* Reserved field */
responseList.Add((byte)0x5D);
/* CSBK Field Param 1 */
responseList.Add(BitConverter.GetBytes(csbkCallParam1)[1]);
responseList.Add(BitConverter.GetBytes(csbkCallParam1)[0]);
csbkCallParam1++;
/* CSBK Field Param 2 */
responseList.Add(BitConverter.GetBytes(csbkCallParam2)[1]);
responseList.Add(BitConverter.GetBytes(csbkCallParam2)[0]);
/* CSBK Field Param 3 */
responseList.Add(BitConverter.GetBytes(csbkCallParam3)[0]);
csbkCallParam3 = csbkCallParam3 + 0x02;
/* CSBK Field Param 4 */
responseList.Add(BitConverter.GetBytes(csbkCallParam4)[0]);
csbkCallParam4 = csbkCallParam4 - 0x20;
/* CSBK Arguments - Only available for CSBK call
* The arguments encoded in the DMR CSBK burst [Uint64] */
responseList.Add((byte)0x00);
responseList.Add((byte)0x00); // reserved
responseList.Add((byte)0x00);
responseList.Add((byte)0x00);
/* Seqeuence number */
responseList.Add((byte)(val++ % 6));
/* 10 RESERVED Fields */
responseList.Add((byte)0x00);
responseList.Add((byte)0x00);
responseList.Add((byte)0x00);
responseList.Add((byte)0x00);
responseList.Add((byte)0x00);
responseList.Add((byte)0x00);
responseList.Add((byte)0x00);
responseList.Add((byte)0x00);
responseList.Add((byte)0x00);
responseList.Add((byte)0x00);
/* add 19 bytes for voice ??? */
responseList.AddRange(new byte[] { 0xb8, 0x34, 0xb3, 0x87, 0xa2, 0xa2, 0xb6, 0x0e, 0x29, 0x8b, 0x99, 0x80, 0xcd, 0x83, 0xb0, 0x39, 0x94, 0x59, 0xc0 });
/* RESERVED */
responseList.Add(0x00);
responseList.Add(0x00);
/* Current or Accepted Wireline Protocol Version */
responseList.Add(currentWirelineProtocolVersion);
responseList.Add(oldestWirelineProtocolVersion);
// convert the list of bytes to a byte array
response = new byte[responseList.Count];
for (int i = 0; i < responseList.Count; i++)
response[i] = responseList[i];
break;
}
#endregion
}
return response;
}
public void decode(byte[] toDecode, string peerIP, Int32 peerPort)
{
int crtPosition = 0;
byte opCode = toDecode[crtPosition++];
// first opCode is for a CSBK Call message
//if (opCode != 0xB2)
// return;
/* get peerID */
byte[] remotePeerID = new byte[4];
remotePeerID[3] = toDecode[crtPosition++];
remotePeerID[2] = toDecode[crtPosition++];
remotePeerID[1] = toDecode[crtPosition++];
remotePeerID[0] = toDecode[crtPosition++];
Int64 peerID = BitConverter.ToUInt32(remotePeerID, 0);
byte wirelineOpCode = toDecode[crtPosition++];
Utils.WriteLine("");
Utils.WriteLine(String.Format("»»» {0}", GetMessageType(wirelineOpCode)), ConsoleColor.Yellow);
//Utils.WriteLine(Utils.printBytesArray(toDecode));
switch (wirelineOpCode)
{
#region WL_REGISTRATION_STATUS
case (byte)Operations.WL_REGISTRATION_STATUS:
{
if (wirelineProtocolVersion >= 1)
{
// identifier of the registration PDU [Uint32]
byte[] registrationPduID = new byte[4];
registrationPduID[3] = toDecode[crtPosition++];
registrationPduID[2] = toDecode[crtPosition++];
registrationPduID[1] = toDecode[crtPosition++];
registrationPduID[0] = toDecode[crtPosition++];
Int64 registrPduID = BitConverter.ToUInt32(registrationPduID, 0);
// identifier associated with the registration profile in this PDU [Uint16]
byte[] registrationIDSlot1 = new byte[2];
registrationIDSlot1[1] = toDecode[crtPosition++];
registrationIDSlot1[0] = toDecode[crtPosition++];
// identifier associated with the registration profile in this PDU [Uint16]
byte[] registrationIDSlot2 = new byte[2];
registrationIDSlot2[1] = toDecode[crtPosition++];
registrationIDSlot2[0] = toDecode[crtPosition++];
// registration status [Uint8]
byte registrationStatus = toDecode[crtPosition++];
// registration status code [Uint8]
byte registrationStatusCode = toDecode[crtPosition++];
// Current or Accepted Wireline Protocol Version [Uint8]
byte currentWirelineProtocolVersion = toDecode[crtPosition++];
// Oldest Wireline Protocol Version [Uint8]
byte oldestWirelineProtocolVersion = toDecode[crtPosition++];
Utils.WriteLine(String.Format("\tRegistration Status: {0}", GetRegistrationStatus(registrationStatus)), ConsoleType.WIRELINE);
Utils.WriteLine(String.Format("\tRegistration Status Code: {0}", GetRegistrationStatusCode(registrationStatusCode)), ConsoleType.WIRELINE);
// do not display and trigger nothing if the registered PDU is empty
if (!(registrPduID > 0))
break;
if (BitConverter.ToInt16(registrationIDSlot1, 0) != 0)
{
Utils.WriteLine(String.Format("\tSlot 1 Registered"), ConsoleType.WIRELINE);
OnRegistrationReceived(true, SlotNumber.Slot1);
}
if (BitConverter.ToInt16(registrationIDSlot2, 0) != 0)
{
Utils.WriteLine(String.Format("\tSlot 2 Registered"), ConsoleType.WIRELINE);
OnRegistrationReceived(true, SlotNumber.Slot2);
}
}
break;
}
#endregion
#region WL_REGISTRATION_GENERAL_OPS
case (byte)Operations.WL_REGISTRATION_GENERAL_OPS:
{
if (wirelineProtocolVersion >= 1)
{
// identifier associated with the registration profile in this PDU [Uint16]
int registrationSlot = toDecode[crtPosition++];
// identifier of the registration PDU [Uint32]
byte[] registrationPduID = new byte[4];
registrationPduID[3] = toDecode[crtPosition++];
registrationPduID[2] = toDecode[crtPosition++];
registrationPduID[1] = toDecode[crtPosition++];
registrationPduID[0] = toDecode[crtPosition++];
Int64 registrPduID = BitConverter.ToUInt32(registrationPduID, 0);
// registration operation code [Uint8]
byte registrationOperationCode = toDecode[crtPosition++];
// Current or Accepted Wireline Protocol Version [Uint8]
byte currentWirelineProtocolVersion = toDecode[crtPosition++];
// Oldest Wireline Protocol Version [Uint8]
byte oldestWirelineProtocolVersion = toDecode[crtPosition++];
Utils.WriteLine("RegSlot " + registrationSlot + " | OpCode " + registrationOperationCode + " [" + registrPduID + "]", ConsoleType.WIRELINE);
}
break;
}
#endregion
#region WL_PROTOCOL_VERSION_QUERY_RESPONSE
/*
* This message is sent from the repeater peer to the third party application as a response
* to the WL_PROTOCOL_VERSION_QUERY. The repeater always responds with its
* current and oldest version irrespective of the version numbers in the Wireline Protocol
* Version Query.
*/
case (byte)Operations.WL_PROTOCOL_VERSION_QUERY_RESPONSE:
{
if (wirelineProtocolVersion >= 3)
{
// reserved field
Int64 reserved = toDecode[crtPosition++];
// queryID - ID of the protocol version query PDU.
// Response from the repeater pertaining to this message
// has the same Query ID value [Uint32]
byte[] queryID = new byte[4];
queryID[3] = toDecode[crtPosition++];
queryID[2] = toDecode[crtPosition++];
queryID[1] = toDecode[crtPosition++];
queryID[0] = toDecode[crtPosition++];
Int64 querryID = BitConverter.ToUInt32(queryID, 0);
// Current Wireline Protocol Version [Uint8]
byte currentWirelineProtocolVersion = toDecode[crtPosition++];
// Oldest Wireline Protocol Version [Uint8]
byte oldestWirelineProtocolVersion = toDecode[crtPosition++];
}
break;
}
#endregion
#region WL_CHNL_STATUS
/*
* This message is sent from the repeater peer to the third party applications to report the
* conventional channel status of SS/IPSC system or rest channel status of CPP/LCP
* system. This message is only sent out by the first repeater peer, which receives the call
* over the air from the radio or receives the call over the network from the third party
* application.
* Only the third party application who has subscribed the wireline channel status during
* wireline registration can receive this message.
* A repeater peer sends out a Wireline Channel Status in one of the following situations:
* 1) Upon receiving a Wireline Channel Status Query from the third party application.
* 2) The register channel status of SS/IPSC system or rest channel status of
* CPC/LCP system is updated.
* In MOTOTRBO 2.2, the data revert repeaters in CPP/LCP system didnt send Wireline
* Channel Status PDU.
*/
case (byte)Operations.WL_CHNL_STATUS:
{
if (wirelineProtocolVersion >= 1)
{
// Slot number - Specifies the channel/slot reporting the channel status.[Uint8]
byte slotNumber = toDecode[crtPosition++];
// statusPduID - The ID of the status PDU
// uniquely identifies the
// channel status PDU. [Uint32]
byte[] statusPduID = new byte[4];
statusPduID[3] = toDecode[crtPosition++];
statusPduID[2] = toDecode[crtPosition++];
statusPduID[1] = toDecode[crtPosition++];
statusPduID[0] = toDecode[crtPosition++];
Int64 statusPDUID = BitConverter.ToUInt32(statusPduID, 0);
// Only available in IPSC modes. The channel status information. [Uint8]
byte conventionalChannelStatus = toDecode[crtPosition++];
// Only available in CPP/LCP modes. The rest channel status information. [Uint8]
byte restChannelStatus = toDecode[crtPosition++];
// Call type [Uint8]
byte typeOfCall = toDecode[crtPosition++];
// Current Wireline Protocol Version [Uint8]
byte currentWirelineProtocolVersion = toDecode[crtPosition++];
// Oldest Wireline Protocol Version [Uint8]
byte oldestWirelineProtocolVersion = toDecode[crtPosition++];
Utils.WriteLine(String.Format("\t{0}", GetSlotNumberName(slotNumber, le.systemType)), ConsoleType.WIRELINE);
Utils.WriteLine(String.Format("\tConventional Channel Status: {0}", GetChannelStatus(conventionalChannelStatus)), ConsoleType.WIRELINE);
Utils.WriteLine(String.Format("\tRest Channel Status: {0}", GetChannelStatus(restChannelStatus)), ConsoleType.WIRELINE);
Utils.WriteLine(String.Format("\tCall Type: {0}", GetCallType(typeOfCall)), ConsoleType.WIRELINE);
}
break;
}
#endregion
#region WL_VC_CHNL_CTRL_STATUS
/*
* This message is sent from the repeater peer to the third party applications to response
* the Wireline Call Channel Control Request.
*/
case (byte)Operations.WL_VC_CHNL_CTRL_STATUS:
{
if (wirelineProtocolVersion >= 1)
{
// Slot number - Specifies the channel/slot reporting the channel status.[Uint8]
byte slotNumber = toDecode[crtPosition++];
// call ID - The ID of the call
byte[] callID = new byte[4];
callID[3] = toDecode[crtPosition++];
callID[2] = toDecode[crtPosition++];
callID[1] = toDecode[crtPosition++];
callID[0] = toDecode[crtPosition++];
Int64 calllID = BitConverter.ToUInt32(callID, 0);
// The type of the call [Uint8]
byte callType = toDecode[crtPosition++];
// chnCtrlstatus - Channel control Status [Uint8]
byte chnCtrlstatus = toDecode[crtPosition++];
// DeclineReasonCode - The reason for decline the call control request [Uint8]
byte declineReasonCode = toDecode[crtPosition++];
// Current Wireline Protocol Version [Uint8]
byte currentWirelineProtocolVersion = toDecode[crtPosition++];
// Oldest Wireline Protocol Version [Uint8]
byte oldestWirelineProtocolVersion = toDecode[crtPosition++];
Utils.WriteLine(String.Format("\tSlot {0}",GetSlotNumber(slotNumber)), ConsoleType.WIRELINE);
Utils.WriteLine(String.Format("\tCall Type: {0}", GetCallType(callType)), ConsoleType.WIRELINE);
Utils.WriteLine(String.Format("\tChannel Ctrl Status: {0}", GetChannelControlStatus(chnCtrlstatus)), ConsoleType.WIRELINE);
if ((SlotNumber)slotNumber == SlotNumber.Slot1)
crtCSBKCallStausSlot1.chnCtrlstatus = (ChannelControlStatus)chnCtrlstatus;
else
crtCSBKCallStausSlot2.chnCtrlstatus = (ChannelControlStatus)chnCtrlstatus;
OnChannelControlStatusReceived(calllID, (CallType)callType, (ChannelControlStatus)chnCtrlstatus, (SlotNumber) slotNumber);
}
break;
}
#endregion
#region WL_VC_CSBK_CALL
/*
* This message is sent from the repeater peer to the third party application to send the
* CSBK request or response message
*/
case (byte)Operations.WL_VC_CSBK_CALL:
{
if (wirelineProtocolVersion >= 1)
{
// Slot number - Specifies the channel/slot reporting the channel status.[Uint8]
byte slotNumber = toDecode[crtPosition++];
// call ID - The ID of the call
byte[] callID = new byte[4];
callID[3] = toDecode[crtPosition++];
callID[2] = toDecode[crtPosition++];
callID[1] = toDecode[crtPosition++];
callID[0] = toDecode[crtPosition++];
Int64 calllID = BitConverter.ToUInt32(callID, 0);
// The type of the call [Uint8]
byte callType = toDecode[crtPosition++];
// source ID - The ID of the subscriber that initiated the call
byte[] sourceID = new byte[4];
sourceID[3] = toDecode[crtPosition++];
sourceID[2] = toDecode[crtPosition++];
sourceID[1] = toDecode[crtPosition++];
sourceID[0] = toDecode[crtPosition++];
Int64 sourceeID = BitConverter.ToUInt32(sourceID, 0);
// target ID - The ID of the subscriber ot talk-group to which the call is targeted
byte[] targetID = new byte[4];
targetID[3] = toDecode[crtPosition++];
targetID[2] = toDecode[crtPosition++];
targetID[1] = toDecode[crtPosition++];
targetID[0] = toDecode[crtPosition++];
Int64 targettID = BitConverter.ToUInt32(targetID, 0);
// reserved [Uint8]
byte reserved = toDecode[crtPosition++];
// MFID - The DMR Feature Set ID / Manufacturers ID [Uint8]
byte mfid = toDecode[crtPosition++];
// CSBK Arguments - The reason for decline the call control request [Uint8]
byte[] csbkArguments = new byte[8];
csbkArguments[7] = toDecode[crtPosition++];
csbkArguments[6] = toDecode[crtPosition++];
csbkArguments[5] = toDecode[crtPosition++];
csbkArguments[4] = toDecode[crtPosition++];
csbkArguments[3] = toDecode[crtPosition++];
csbkArguments[2] = toDecode[crtPosition++];
csbkArguments[1] = toDecode[crtPosition++];
csbkArguments[0] = toDecode[crtPosition++];
// rawRssiValue - The raw RSSI value for each burst
byte[] rawRssiValue = new byte[2];
rawRssiValue[1] = toDecode[crtPosition++];
rawRssiValue[0] = toDecode[crtPosition++];
// Current Wireline Protocol Version [Uint8]
byte currentWirelineProtocolVersion = toDecode[crtPosition++];
// Oldest Wireline Protocol Version [Uint8]
byte oldestWirelineProtocolVersion = toDecode[crtPosition++];
Utils.WriteLine(String.Format("\t{0}", GetSlotNumberName(slotNumber, le.systemType)), ConsoleType.WIRELINE);
Utils.WriteLine(String.Format("\tCall Type: {0}", GetCallType(callType)), ConsoleType.WIRELINE);
Utils.WriteLine(String.Format("\tSource ID: {0}", sourceeID), ConsoleType.WIRELINE);
Utils.WriteLine(String.Format("\tTarget ID: {0}", targettID), ConsoleType.WIRELINE);
switch (callType)
{
case (byte)CallType.RadioInhibitResponse:
{
// eg.
// 0xB2 0x0 0x0 0x0 0x2 0x17 0x1 0x0 0x0 0x0 0x65 0x4A 0x0 0x0 0x0 0x6F 0x0 0x0
// 0x0 0x1 0x0 0x0 0x10 0x0 0xFF 0x0 0x0 0x1 0x0 0x0 0x6F 0x3B 0x60 0xC 0x4
OnRadioStatusChanged(sourceeID, GetSlotNumber(slotNumber), false);
break;
}
case (byte)CallType.RadioUninhibitResponse:
{
// eg.
// 0xB2 0x0 0x0 0x0 0x2 0x17 0x1 0x0 0x0 0x0 0x4C 0x4C 0x0 0x0 0x0 0x65 0x0 0x0
// 0x0 0x1 0x0 0x0 0x10 0x0 0xFE 0x0 0x0 0x1 0x0 0x0 0x65 0x3B 0x69 0xC 0x4
OnRadioStatusChanged(sourceeID, GetSlotNumber(slotNumber), true);
break;
}
case (byte)CallType.EmergencyCSBKAlertRequest:
{
OnEmergencyChanged(sourceeID, GetSlotNumber(slotNumber));
break;
}
case (byte)CallType.AllCall:
case (byte)CallType.GroupVoiceCall:
case (byte)CallType.PrivateCallRequest:
{
OnCSBKCallReceived(sourceeID, targettID, (CallType)callType, (SlotNumber)slotNumber);
break;
}
}
}
break;
}
#endregion
#region WL_VC_VOICE_START
/*
* This message is sent by the repeater peer to indicate the beginning of a voice stream.
*/
case (byte)Operations.WL_VC_VOICE_START:
{
if (wirelineProtocolVersion >= 1)
{
// Slot number - Specifies the channel/slot reporting the channel status.[Uint8]
byte slotNumber = toDecode[crtPosition++];
// call ID - The ID of the call
byte[] callID = new byte[4];
callID[3] = toDecode[crtPosition++];
callID[2] = toDecode[crtPosition++];
callID[1] = toDecode[crtPosition++];
callID[0] = toDecode[crtPosition++];
Int64 calllID = BitConverter.ToUInt32(callID, 0);
// The type of the call [Uint8]
byte callType = toDecode[crtPosition++];
CallType callTypee = GetCallType(callType);
// source ID - The ID of the subscriber that initiated the call
byte[] sourceID = new byte[4];
sourceID[3] = toDecode[crtPosition++];
sourceID[2] = toDecode[crtPosition++];
sourceID[1] = toDecode[crtPosition++];
sourceID[0] = toDecode[crtPosition++];
Int64 sourceeID = BitConverter.ToUInt32(sourceID, 0);
// target ID - The ID of the subscriber ot talk-group to which the call is targeted
byte[] targetID = new byte[4];
targetID[3] = toDecode[crtPosition++];
targetID[2] = toDecode[crtPosition++];
targetID[1] = toDecode[crtPosition++];
targetID[0] = toDecode[crtPosition++];
Int64 targettID = BitConverter.ToUInt32(targetID, 0);
// callAttributes [Uint8] - Call General attributes
byte callAttributes = toDecode[crtPosition++];
// reserved [Uint8]
byte reserved = toDecode[crtPosition++];
// MFID [Uint8] - The DMR Feature Set ID / Manufacturers ID
byte mfid = toDecode[crtPosition++];
// service option [Uint8] - DMR LC Service Option
byte serviceOption = toDecode[crtPosition++];
// Current Wireline Protocol Version [Uint8]
byte currentWirelineProtocolVersion = toDecode[crtPosition++];
// Oldest Wireline Protocol Version [Uint8]
byte oldestWirelineProtocolVersion = toDecode[crtPosition++];
Utils.WriteLine(String.Format("\t{0}", GetSlotNumberName(slotNumber, le.systemType)), ConsoleType.WIRELINE);
Utils.WriteLine(String.Format("\tCall Type: {0}", GetCallType(callType)), ConsoleType.WIRELINE);
Utils.WriteLine(String.Format("\tSource ID: {0}", sourceeID), ConsoleType.WIRELINE);
Utils.WriteLine(String.Format("\tTarget ID: {0}", targettID), ConsoleType.WIRELINE);
if (callTypee == CallType.AllCall || callTypee == CallType.GroupVoiceCall
|| callTypee == CallType.PrivateVoiceCall || callTypee == CallType.EmergencyVoiceCall)
OnCallStatusReceived(sourceeID, targettID, ConvertCallType4MessageBus(callTypee),
ConvertCallSessionStatus4MessageBus(CallSessionStatus.CallInProgress), GetSlotNumber(slotNumber));
}
break;
}
#endregion
#region WL_VC_VOICE_END_BURST
/*
* This message is sent by the repeater peer to indicate the beginning of a voice stream.
*/
case (byte)Operations.WL_VC_VOICE_END_BURST:
{
if (wirelineProtocolVersion >= 1)
{
// Slot number - Specifies the channel/slot reporting the channel status.[Uint8]
byte slotNumber = toDecode[crtPosition++];
// call ID - The ID of the call
byte[] callID = new byte[4];
callID[3] = toDecode[crtPosition++];
callID[2] = toDecode[crtPosition++];
callID[1] = toDecode[crtPosition++];
callID[0] = toDecode[crtPosition++];
Int64 calllID = BitConverter.ToUInt32(callID, 0);
// The type of the call [Uint8]
byte callType = toDecode[crtPosition++];
CallType callTypee = GetCallType(callType);
// source ID - The ID of the subscriber that initiated the call
byte[] sourceID = new byte[4];
sourceID[3] = toDecode[crtPosition++];
sourceID[2] = toDecode[crtPosition++];
sourceID[1] = toDecode[crtPosition++];
sourceID[0] = toDecode[crtPosition++];
Int64 sourceeID = BitConverter.ToUInt32(sourceID, 0);
// target ID - The ID of the subscriber ot talk-group to which the call is targeted
byte[] targetID = new byte[4];
targetID[3] = toDecode[crtPosition++];
targetID[2] = toDecode[crtPosition++];
targetID[1] = toDecode[crtPosition++];
targetID[0] = toDecode[crtPosition++];
Int64 targettID = BitConverter.ToUInt32(targetID, 0);
// callAttributes [Uint8] - Call General attributes
byte[] rtpInformationField = new byte[12];
rtpInformationField[11] = toDecode[crtPosition++];
rtpInformationField[10] = toDecode[crtPosition++];
rtpInformationField[9] = toDecode[crtPosition++];
rtpInformationField[8] = toDecode[crtPosition++];
rtpInformationField[7] = toDecode[crtPosition++];
rtpInformationField[6] = toDecode[crtPosition++];
rtpInformationField[5] = toDecode[crtPosition++];
rtpInformationField[4] = toDecode[crtPosition++];
rtpInformationField[3] = toDecode[crtPosition++];
rtpInformationField[2] = toDecode[crtPosition++];
rtpInformationField[1] = toDecode[crtPosition++];
rtpInformationField[0] = toDecode[crtPosition++];
// burst type [Uint8] - DMR voice burst type Must be Voice Terminator
byte burstType = toDecode[crtPosition++];
// reserved [Uint8]
byte reserved = toDecode[crtPosition++];
// MFID [Uint8] - The DMR Feature Set ID / Manufacturers ID
byte mfid = toDecode[crtPosition++];
// service option [Uint8] - DMR LC Service Option
byte serviceOption = toDecode[crtPosition++];
// Current Wireline Protocol Version [Uint8]
byte currentWirelineProtocolVersion = toDecode[crtPosition++];
// Oldest Wireline Protocol Version [Uint8]
byte oldestWirelineProtocolVersion = toDecode[crtPosition++];
//Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\t{0}", GetSlotNumberName(slotNumber, le.systemType)));
//Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\tCall Type: {0}", GetCallType(callType)));
//Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\tSource ID: {0}", sourceeID));
//Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\tTarget ID: {0}", targettID));
Utils.WriteLine(String.Format("\tBurst Type: {0}", GetBurstType(burstType)), ConsoleType.WIRELINE);
/*
if (callTypee == CallType.AllCall || callTypee == CallType.GroupVoiceCall || callTypee == CallType.PrivateCallRequest)
OnCallStatusReceived(sourceeID, targettID, ConvertCallType4MessageBus(callTypee),
ConvertCallSessionStatus4MessageBus(CallSessionStatus.CallInProgress), GetSlotNumber(slotNumber));
*/
}
break;
}
#endregion
#region WL_VC_CALL_SESSION_STATUS
/*
* This message is sent from repeater peer to the third party application to inform the call
* session status. Only the repeater peer, which receives the call over the air from the
* radio or receives the call from the third party application over the network interface,
* sends out the WL_VC_CALL_SESSION_STATUS message
*/
case (byte)Operations.WL_VC_CALL_SESSION_STATUS:
{
if (wirelineProtocolVersion >= 1)
{
// Slot number - Specifies the channel/slot reporting the channel status.[Uint8]
byte slotNumber = toDecode[crtPosition++];
// call ID - The ID of the call
byte[] callID = new byte[4];
callID[3] = toDecode[crtPosition++];
callID[2] = toDecode[crtPosition++];
callID[1] = toDecode[crtPosition++];
callID[0] = toDecode[crtPosition++];
Int64 calllID = BitConverter.ToUInt32(callID, 0);
// The type of the call [Uint8]
byte callType = toDecode[crtPosition++];
// source ID - The ID of the subscriber that initiated the call
byte[] sourceID = new byte[4];
sourceID[3] = toDecode[crtPosition++];
sourceID[2] = toDecode[crtPosition++];
sourceID[1] = toDecode[crtPosition++];
sourceID[0] = toDecode[crtPosition++];
Int64 sourceeID = BitConverter.ToUInt32(sourceID, 0);
// target ID - The ID of the subscriber ot talk-group to which the call is targeted
byte[] targetID = new byte[4];
targetID[3] = toDecode[crtPosition++];
targetID[2] = toDecode[crtPosition++];
targetID[1] = toDecode[crtPosition++];
targetID[0] = toDecode[crtPosition++];
Int64 targettID = BitConverter.ToUInt32(targetID, 0);
// reserved [Uint32]
byte reserved = toDecode[crtPosition++];
reserved = toDecode[crtPosition++];
reserved = toDecode[crtPosition++];
reserved = toDecode[crtPosition++];
// MFIcallSessionStatusD - Call Session Stats [Uint8]
byte callSessionStatus = toDecode[crtPosition++];
// Current Wireline Protocol Version [Uint8]
byte currentWirelineProtocolVersion = toDecode[crtPosition++];
// Oldest Wireline Protocol Version [Uint8]
byte oldestWirelineProtocolVersion = toDecode[crtPosition++];
Utils.WriteLine(String.Format("\t{0}", GetSlotNumberName(slotNumber, le.systemType)));
Utils.WriteLine(String.Format("\tCall Type: {0}", GetCallType(callType)));
Utils.WriteLine(String.Format("\tSource ID: {0}", sourceeID));
Utils.WriteLine(String.Format("\tTarget ID: {0}", targettID));
Utils.WriteLine(String.Format("\tCall Session Status: {0}", GetCallSessionStatus(callSessionStatus)));
CallSessionStatus callStatus = (callSessionStatus == (byte)CallSessionStatus.CallEnd ? CallSessionStatus.CallEnd : CallSessionStatus.CallHang);
// broadcast the call status only if in hangtime or ended
if(callStatus == CallSessionStatus.CallEnd || callStatus == CallSessionStatus.CallHang)
{
if (callType == (byte)CallType.AllCall || callType == (byte)CallType.GroupVoiceCall
|| callType == (byte)CallType.PrivateVoiceCall || callType == (byte)CallType.EmergencyVoiceCall)
OnCallStatusReceived(sourceeID, targettID, ConvertCallType4MessageBus((CallType)callType),
ConvertCallSessionStatus4MessageBus(callStatus), GetSlotNumber(slotNumber, le.systemType));
/*
if (callType == (byte)CallType.GroupVoiceCall)
OnCallStatusReceived(sourceeID, targettID, ConvertCallType4MessageBus(CallType.GroupVoiceCall),
ConvertCallSessionStatus4MessageBus(callStatus), GetSlotNumber(slotNumber, le.systemType));
else if (callType == (byte)CallType.PrivateVoiceCall)
OnCallStatusReceived(sourceeID, targettID, ConvertCallType4MessageBus(CallType.PrivateVoiceCall),
ConvertCallSessionStatus4MessageBus(callStatus), GetSlotNumber(slotNumber, le.systemType));
else if (callType == (byte)CallType.AllCall)
OnCallStatusReceived(sourceeID, targettID, ConvertCallType4MessageBus(CallType.AllCall),
ConvertCallSessionStatus4MessageBus(callStatus), GetSlotNumber(slotNumber, le.systemType));
else if (callType == (byte)CallType.EmergencyVoiceCall)
OnCallStatusReceived(sourceeID, targettID, ConvertCallType4MessageBus(CallType.EmergencyVoiceCall),
ConvertCallSessionStatus4MessageBus(callStatus), GetSlotNumber(slotNumber, le.systemType));
*/
}
// fire event for call session status received
OnCallSesionStatusReceived(sourceeID, targettID, (CallType)callType, (CallSessionStatus)callSessionStatus, (SlotNumber) slotNumber);
}
break;
}
#endregion
#region WL_VC_VOICE_BURST
/*
* This message is used by the Repeater Peer to send Voice Burst to the third party
* application.
*/
case (byte)Operations.WL_VC_VOICE_BURST:
{
if (wirelineProtocolVersion >= 1)
{
// Slot number - Specifies the channel/slot reporting the channel status.[Uint8]
byte slotNumber = toDecode[crtPosition++];
// call ID - The ID of the call
byte[] callID = new byte[4];
callID[3] = toDecode[crtPosition++];
callID[2] = toDecode[crtPosition++];
callID[1] = toDecode[crtPosition++];
callID[0] = toDecode[crtPosition++];
Int64 calllID = BitConverter.ToUInt32(callID, 0);
// The type of the call [Uint8]
byte callType = toDecode[crtPosition++];
CallType callTypee = GetCallType(callType);
// source ID - The ID of the subscriber that initiated the call
byte[] sourceID = new byte[4];
sourceID[3] = toDecode[crtPosition++];
sourceID[2] = toDecode[crtPosition++];
sourceID[1] = toDecode[crtPosition++];
sourceID[0] = toDecode[crtPosition++];
Int64 sourceeID = BitConverter.ToUInt32(sourceID, 0);
// target ID - The ID of the subscriber ot talk-group to which the call is targeted
byte[] targetID = new byte[4];
targetID[3] = toDecode[crtPosition++];
targetID[2] = toDecode[crtPosition++];
targetID[1] = toDecode[crtPosition++];
targetID[0] = toDecode[crtPosition++];
Int64 targettID = BitConverter.ToUInt32(targetID, 0);
// callAttributes [Uint8] - Call General attributes
byte callAttributes = toDecode[crtPosition++];
// reserved [Uint8]
byte reserved = toDecode[crtPosition++];
// rptInformationField [Uint8 * 12] - RPT Header Information
byte[] rtpInformationField = new byte[12];
rtpInformationField[11] = toDecode[crtPosition++];
rtpInformationField[10] = toDecode[crtPosition++];
rtpInformationField[9] = toDecode[crtPosition++];
rtpInformationField[8] = toDecode[crtPosition++];
rtpInformationField[7] = toDecode[crtPosition++];
rtpInformationField[6] = toDecode[crtPosition++];
rtpInformationField[5] = toDecode[crtPosition++];
rtpInformationField[4] = toDecode[crtPosition++];
rtpInformationField[3] = toDecode[crtPosition++];
rtpInformationField[2] = toDecode[crtPosition++];
rtpInformationField[1] = toDecode[crtPosition++];
rtpInformationField[0] = toDecode[crtPosition++];
// burst type [Uint8] - DMR voice burst type Must be Voice Terminator
byte burstType = toDecode[crtPosition++];
// reserved [Uint8]
byte reserved3 = toDecode[crtPosition++];
// MFID [Uint8] - The DMR Feature Set ID / Manufacturers ID
byte mfid = toDecode[crtPosition++];
// service option [Uint8] - DMR LC Service Option
byte serviceOption = toDecode[crtPosition++];
// algorithmID [Uint8] - Algorithm ID
byte algorithmID = toDecode[crtPosition++];
// keyID [Uint8] - ID of the Privacy Key
byte keyID = toDecode[crtPosition++];
// iv [Uint8] - Initialization Vector
byte[] iv = new byte[4];
iv[3] = toDecode[crtPosition++];
iv[2] = toDecode[crtPosition++];
iv[1] = toDecode[crtPosition++];
iv[0] = toDecode[crtPosition++];
// ambe [Uint8 * 20] - The AMBE voice encoded frames
byte[] ambe = new byte[20];
ambe[0] = toDecode[crtPosition++];
ambe[1] = toDecode[crtPosition++];
ambe[2] = toDecode[crtPosition++];
ambe[3] = toDecode[crtPosition++];
ambe[4] = toDecode[crtPosition++];
ambe[5] = toDecode[crtPosition++];
ambe[6] = toDecode[crtPosition++];
ambe[7] = toDecode[crtPosition++];
ambe[8] = toDecode[crtPosition++];
ambe[9] = toDecode[crtPosition++];
ambe[10] = toDecode[crtPosition++];
ambe[11] = toDecode[crtPosition++];
ambe[12] = toDecode[crtPosition++];
ambe[13] = toDecode[crtPosition++];
ambe[14] = toDecode[crtPosition++];
ambe[15] = toDecode[crtPosition++];
ambe[16] = toDecode[crtPosition++];
ambe[17] = toDecode[crtPosition++];
ambe[18] = toDecode[crtPosition++];
ambe[19] = toDecode[crtPosition++];
// add ambe burst to file
//DecodeAMBE2(ambe, GetBurstType(burstType), (SlotNumber)slotNumber);
// rawRssiValue [Uint16] - Only for WL outbound: The raw RSSI value for each voice burst
byte[] rawRssiValue = new byte[2];
rawRssiValue[1] = toDecode[crtPosition++];
rawRssiValue[0] = toDecode[crtPosition++];
// Current Wireline Protocol Version [Uint8]
byte currentWirelineProtocolVersion = toDecode[crtPosition++];
// Oldest Wireline Protocol Version [Uint8]
byte oldestWirelineProtocolVersion = toDecode[crtPosition++];
Utils.WriteLine(String.Format("\t{0}", GetSlotNumberName(slotNumber, le.systemType)), ConsoleType.WIRELINE);
//Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\tCall Type: {0}", GetCallType(callType)));
//Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\tSource ID: {0}", sourceeID));
//Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\tTarget ID: {0}", targettID));
//Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\tCurrent Version: {0}", currentWirelineProtocolVersion));
//Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\tOldest Version: {0}", oldestWirelineProtocolVersion));
Utils.WriteLine(String.Format("\tBurst Type: {0}", GetBurstType(burstType)), ConsoleType.WIRELINE);
// verify if a voice burst was skyped and send an empty one if yes
int skipped = 0;
// set the previous bursty type for this slot number
int prevWorkingBurstType = (slotNumber == (byte)SlotNumber.Slot1 ? previousBrustTypeSlot1 : previousBrustTypeSlot2);
if (burstType < prevWorkingBurstType)
skipped = Math.Abs(burstType + 6 - prevWorkingBurstType) % 6 - 1;
else
skipped = Math.Abs(burstType - prevWorkingBurstType % 6) - 1;
if (skipped > 0)
{
for (int i = 0; i < skipped; i++)
{
// frame 1
OnAudioFrameReceived(new byte[7], (Wireline.SlotNumber)slotNumber);
// frame 2
OnAudioFrameReceived(new byte[7], (Wireline.SlotNumber)slotNumber);
// frame 3
OnAudioFrameReceived(new byte[7], (Wireline.SlotNumber)slotNumber);
}
}
// trigger event for voice frame received
List<byte[]> voiceFrames = AMBE.Rate34PrepareForEncoding(ambe);
foreach (byte[] by in voiceFrames)
OnAudioFrameReceived(by, (Wireline.SlotNumber)slotNumber);
// save previous burst type for specified slot
if (slotNumber == (byte)SlotNumber.Slot1)
previousBrustTypeSlot1 = burstType;
else
previousBrustTypeSlot2 = burstType;
}
break;
}
#endregion
#region WL_VC_PRIVACY_BURST
/*
* This message is use by the Repeater Peer to send Privacy Voice Burst to the third party
* application.
*/
case (byte)Operations.WL_VC_PRIVACY_BURST:
{
if (wirelineProtocolVersion >= 1)
{
// Slot number - Specifies the channel/slot reporting the channel status.[Uint8]
byte slotNumber = toDecode[crtPosition++];
// call ID - The ID of the call
byte[] callID = new byte[4];
callID[3] = toDecode[crtPosition++];
callID[2] = toDecode[crtPosition++];
callID[1] = toDecode[crtPosition++];
callID[0] = toDecode[crtPosition++];
Int64 calllID = BitConverter.ToUInt32(callID, 0);
// The type of the call [Uint8]
byte callType = toDecode[crtPosition++];
CallType callTypee = GetCallType(callType);
// source ID - The ID of the subscriber that initiated the call
byte[] sourceID = new byte[4];
sourceID[3] = toDecode[crtPosition++];
sourceID[2] = toDecode[crtPosition++];
sourceID[1] = toDecode[crtPosition++];
sourceID[0] = toDecode[crtPosition++];
Int64 sourceeID = BitConverter.ToUInt32(sourceID, 0);
// target ID - The ID of the subscriber ot talk-group to which the call is targeted
byte[] targetID = new byte[4];
targetID[3] = toDecode[crtPosition++];
targetID[2] = toDecode[crtPosition++];
targetID[1] = toDecode[crtPosition++];
targetID[0] = toDecode[crtPosition++];
Int64 targettID = BitConverter.ToUInt32(targetID, 0);
// rptInformationField [Uint8 * 12] - RPT Header Information
byte[] rtpInformationField = new byte[12];
rtpInformationField[11] = toDecode[crtPosition++];
rtpInformationField[10] = toDecode[crtPosition++];
rtpInformationField[9] = toDecode[crtPosition++];
rtpInformationField[8] = toDecode[crtPosition++];
rtpInformationField[7] = toDecode[crtPosition++];
rtpInformationField[6] = toDecode[crtPosition++];
rtpInformationField[5] = toDecode[crtPosition++];
rtpInformationField[4] = toDecode[crtPosition++];
rtpInformationField[3] = toDecode[crtPosition++];
rtpInformationField[2] = toDecode[crtPosition++];
rtpInformationField[1] = toDecode[crtPosition++];
rtpInformationField[0] = toDecode[crtPosition++];
// burst type [Uint8] - DMR Voice burst Type Must be Privacy Header
byte burstType = toDecode[crtPosition++];
// reserved [Uint8]
byte reserved = toDecode[crtPosition++];
// MFID [Uint8] - The DMR Feature Set ID / Manufacturers ID
byte mfid = toDecode[crtPosition++];
// service option [Uint8] - DMR LC Service Option
byte serviceOption = toDecode[crtPosition++];
// algorithmID [Uint8] - Algorithm ID
byte algorithmID = toDecode[crtPosition++];
// keyID [Uint8] - ID of the Privacy Key
byte keyID = toDecode[crtPosition++];
// iv [Uint8] - Initialization Vector
byte iv = toDecode[crtPosition++];
// Current Wireline Protocol Version [Uint8]
byte currentWirelineProtocolVersion = toDecode[crtPosition++];
// Oldest Wireline Protocol Version [Uint8]
byte oldestWirelineProtocolVersion = toDecode[crtPosition++];
Utils.WriteLine(String.Format("\t{0}", GetSlotNumberName(slotNumber, le.systemType)), ConsoleType.WIRELINE);
Utils.WriteLine(String.Format("\tCall Type: {0}", GetCallType(callType)), ConsoleType.WIRELINE);
Utils.WriteLine(String.Format("\tSource ID: {0}", sourceeID), ConsoleType.WIRELINE);
Utils.WriteLine(String.Format("\tTarget ID: {0}", targettID), ConsoleType.WIRELINE);
Utils.WriteLine(String.Format("\tBurst Type: {0}", GetBurstType(burstType)), ConsoleType.WIRELINE);
if (callTypee == CallType.AllCall || callTypee == CallType.GroupVoiceCall
|| callTypee == CallType.PrivateVoiceCall || callTypee == CallType.EmergencyVoiceCall)
OnCallStatusReceived(sourceeID, targettID, ConvertCallType4MessageBus(callTypee),
ConvertCallSessionStatus4MessageBus(CallSessionStatus.CallInProgress), GetSlotNumber(slotNumber));
}
break;
}
#endregion
default:
{
Utils.WriteLine(String.Format("Unknown Wireline Message: 0x{0:X} [{1}:{2}]", wirelineOpCode, peerIP, peerPort), ConsoleType.WIRELINE);
Utils.WriteLine(Utils.PrintBytesArray(toDecode), ConsoleType.WIRELINE);
break;
}
}
}
#region WIRELINE SENDING
/* Send a Wireline message to the desired peer */
public void SendWirelineMessage(byte[] message, String peerIP, Int32 peerUDPPort)
{
if (message.Length < 1)
return;
try
{
le.udpClient.Client.SendTo(message, new IPEndPoint(IPAddress.Parse(peerIP), peerUDPPort));
Utils.WriteLine(String.Format("««« {0,-34} [{1}]", GetMessageType(message[5]), peerIP), ConsoleType.WIRELINE); // + ":" + peerUDPPort);
//Utils.WriteLine(ConsoleType.WIRELINE, Utils.printBytesArray(message));
}
catch (Exception ex)
{
Utils.WriteLine("Wireline Thread Send exception: " + ex.ToString(), ConsoleType.WIRELINE);
}
}
/* Send a Wireline message to the current Master Peer */
public void SendWirelineMessage(byte[] message)
{
SendWirelineMessage(message, le.masterPeer.ipAddress, le.masterPeer.port);
}
/* Get a string that represents the Message Type based on the opCode */
private string GetMessageType(byte wirelineOpCode)
{
switch (wirelineOpCode)
{
case (byte)Operations.WL_REGISTRATION_REQUEST: return Operations.WL_REGISTRATION_REQUEST.ToString();
case (byte)Operations.WL_REGISTRATION_STATUS: return Operations.WL_REGISTRATION_STATUS.ToString();
case (byte)Operations.WL_REGISTRATION_GENERAL_OPS: return Operations.WL_REGISTRATION_GENERAL_OPS.ToString();
case (byte)Operations.WL_PROTOCOL_VERSION_QUERY: return Operations.WL_PROTOCOL_VERSION_QUERY.ToString();
case (byte)Operations.WL_PROTOCOL_VERSION_QUERY_RESPONSE: return Operations.WL_PROTOCOL_VERSION_QUERY_RESPONSE.ToString();
case (byte)Operations.WL_CHNL_STATUS: return Operations.WL_CHNL_STATUS.ToString();
case (byte)Operations.WL_CHNL_STATUS_QUERY: return Operations.WL_CHNL_STATUS_QUERY.ToString();
case (byte)Operations.WL_VC_CHNL_CTRL_REQUEST: return Operations.WL_VC_CHNL_CTRL_REQUEST.ToString();
case (byte)Operations.WL_VC_CHNL_CTRL_STATUS: return Operations.WL_VC_CHNL_CTRL_STATUS.ToString();
case (byte)Operations.WL_VC_CSBK_CALL: return Operations.WL_VC_CSBK_CALL.ToString();
case (byte)Operations.WL_VC_VOICE_START: return Operations.WL_VC_VOICE_START.ToString();
case (byte)Operations.WL_VC_VOICE_END_BURST: return Operations.WL_VC_VOICE_END_BURST.ToString();
case (byte)Operations.WL_VC_CALL_SESSION_STATUS: return Operations.WL_VC_CALL_SESSION_STATUS.ToString();
case (byte)Operations.WL_VC_VOICE_BURST: return Operations.WL_VC_VOICE_BURST.ToString();
case (byte)Operations.WL_VC_PRIVACY_BURST: return Operations.WL_VC_PRIVACY_BURST.ToString();
default: return "Unknown Wireline Message";
}
}
#endregion
#region WIRELINE CALL ENUMS
public enum AccessCriteria
{
PoliteAccess = 0x01
};
public enum CallAttributes
{
ForwardCSBK = 0x20,
DoNotForwardCSBK = 0x00,
SmartPTT = 0x80,
SmartAfterPreamble = 0xA0
};
public enum PreambleDuration
{
MinDuration = 0x00,
MaxDuration = 0x80,
BurstDurationMS = 0x3C
};
public enum CallSessionStatus
{
CallHang = 0x0A,
CallEnd = 0x0B,
CallInProgress = 0x09,
RESERVED = 0x00
};
public enum CallType
{
PreamblePrivateCSBKCall = 0x32,
PreambleGroupCSBKCall = 0x33,
PreambleEmergencyCall = 0x34,
EmergencyCSBKAlertRequest = 0x40,
EmergencyCSBKAlertResponse = 0x41,
EmergencyVoiceCall = 0x42,
PrivateCallRequest = 0x43,
PrivateCallResponse = 0x44,
CallAlertRequest = 0x45,
CallAlertResponse = 0x46,
RadioCheckRequest = 0x47,
RadioCheckResponse = 0x48,
RadioInhibitRequest = 0x49,
RadioInhibitResponse = 0x4A,
RadioUninhibitRequest = 0x4B,
RadioUninhibitResponse = 0x4C,
RadioRemoteMonitorRequest = 0x4D,
RadioRemoteMonitorResponse = 0x4E,
GroupVoiceCall = 0x4F,
PrivateVoiceCall = 0x50,
AllCall = 0x53,
Reserved = 0x54,
Other = 0x55,
IPConsoleRadioUninhibitRequest = 0x56,
IPConsoleRadioInhibitRequest = 0x57,
IPConsoleRadioUninhibitResponse = 0x58,
IPConsoleRadioInhibitResponse = 0x59,
GroupPhoneCall = 0x5A,
PrivatePhoneCall = 0x5B,
PhoneAllCall = 0x5C,
CallAlertNackResponse = 0x83,
RadioMonitorNackResponse = 0x84,
RadioInhibitUninhibitNackResponse = 0x85
};
public enum ChannelStatus
{
Active = 0x01,
Idle = 0x02,
SlotBlocked = 0x0A,
SlotUnblocked = 0x0B,
BusyRestChannel = 0x10,
RestChannelIdleAvailable = 0x11, // works for idle and available
LocalGroupCallsNotAllowed = 0x12,
LocalGroupCallsAllowed = 0x13,
RestChannelBlocked = 0x14
};
public enum ChannelControlStatus
{
Received = 0x01,
Transmiting = 0x02,
TransmissionSuccessful = 0x03,
SmartInProgress = 0x04,
Declined = 0x05
};
public enum SlotNumber
{
Slot1 = 0x01,
Slot2 = 0x02,
BothSlots = 0x03,
RestChannel = 0x00, // only in CPC
SiteIPAddress = 0x00, // only in IPSC
Reserved = 0xFF
};
public enum RegistrationStatusCode
{
CFSNotEnabled = 0x01,
RegistrationEntriesExceded = 0x02,
IncompatibleVersion = 0x03
};
public enum RegistrationStatus
{
Successful = 0x00,
Unsuccessful = 0x01
};
public enum RegistrationOperationCode
{
QueryRegStatus = 0x01,
DeRegister = 0x02
};
public enum WirelineChannelStatus
{
RegisteredWLStatus = 0x80,
NotRegisteredWLStatus = 0x00
};
public enum AddressType
{
IndividualCall = 0x01,
GroupCall = 0x02,
AllIndividualCall = 0x03,
AllTalkgroupCall = 0x04,
AllWideTakgroupsCall = 0x05,
AllLocalTalkgroupsCall = 0x06
};
public enum CSBKAttributes
{
// bit 0
VoiceCallNotInterruptible = 0x00,
VoiceCallInterruptible = 0x01,
// bit 1
ClearCall = 0x00,
PrivacyCall = 0x02,
// bit 2-3
ClearCall23 = 0x00,
BasicPrivacy = 0x01,
EnhancedPrivacy = 0x02,
// bit 6
CSBKService = 0x00,
CSBKMonitorOnly = 0x40,
// bit 7
CSBKNotRegisteredService = 0x00,
CSBKRegisteredService = 0x80
}
public enum VoiceAttributes
{
NormalService = 0x00,
VoiceMonitorOnly = 0x01,
NotRegisteredVoiceService = 0x00,
RegisterVoiceService = 0x01
};
public enum BurstType
{
VoiceBurstA = 0x01,
VoiceBurstB = 0x02,
VoiceBurstC = 0x03,
VoiceBurstD = 0x04,
VoiceBurstE = 0x05,
VoiceBurstF = 0x06,
VoiceTerminator = 0x07,
PrivacyHeader = 0x08,
Unknown = 0x00
}
public enum ServiceOption
{
// bit 3 - Only for group call
NonBroadcastService = 0x00,
BroadcastService = 0x04,
// bit 7
NonEmergencyService = 0x00,
BroadcastService7 = 0x80
}
public enum PrivacyType
{
Clear = 0x00,
Basic = 0x01,
Enhanced = 0x02
};
public enum ManufacturerID
{
StandardFeature = 0x00,
MotorolaSolutionProprietaryFeature = 0x10,
}
private string GetRegistrationStatus(int registrationStatus)
{
switch(registrationStatus)
{
case (int)RegistrationStatus.Successful: return "Successful";
case (int)RegistrationStatus.Unsuccessful: return "Unsuccessful";
default: return "RESERVED";
}
}
private string GetRegistrationStatusCode(int registrationStatusCode)
{
switch(registrationStatusCode)
{
case (int)RegistrationStatusCode.CFSNotEnabled: return "CFS Not Enabled";
case (int)RegistrationStatusCode.IncompatibleVersion: return "Incompatible Version";
case (int)RegistrationStatusCode.RegistrationEntriesExceded: return "Registration Entries Exceded";
case (int)0x00: return "Registered";
default: return "RESERVED";
}
}
private int ConvertCallType4MessageBus(CallType type)
{
switch (type)
{
case CallType.GroupVoiceCall: return (int)SafeMobileLib.CallType.GROUPCALL;
case CallType.PrivateVoiceCall: return (int)SafeMobileLib.CallType.PRIVATECALL;
case CallType.AllCall: return (int)SafeMobileLib.CallType.ALLCALL;
case CallType.EmergencyVoiceCall: return (int)SafeMobileLib.CallType.EMERGENCYCALL;
default: return 0;
}
}
private string GetCallType(int type)
{
switch (type)
{
case (int)CallType.CallAlertNackResponse: return "CallAlertNackResponse";
case (int)CallType.CallAlertRequest: return "CallAlertRequest";
case (int)CallType.CallAlertResponse: return "CallAlertResponse";
case (int)CallType.EmergencyCSBKAlertRequest: return "EmergencyCSBKAlertRequest";
case (int)CallType.EmergencyCSBKAlertResponse: return "EmergencyCSBKAlertResponse";
case (int)CallType.EmergencyVoiceCall: return "EmergencyVoiceCall";
case (int)CallType.GroupVoiceCall: return "GroupVoiceCall";
case (int)CallType.PrivateVoiceCall: return "PrivateVoiceCall";
case (int)CallType.IPConsoleRadioInhibitRequest: return "IPConsoleRadioInhibitRequest";
case (int)CallType.IPConsoleRadioInhibitResponse: return "IPConsoleRadioInhibitResponse";
case (int)CallType.IPConsoleRadioUninhibitRequest: return "IPConsoleRadioUninhibitRequest";
case (int)CallType.IPConsoleRadioUninhibitResponse: return "IPConsoleRadioUninhibitResponse";
case (int)CallType.AllCall: return "AllCall";
case (int)CallType.Other: return "Other";
case (int)CallType.GroupPhoneCall: return "GroupPhoneCall";
case (int)CallType.PrivatePhoneCall: return "PrivatePhoneCall";
case (int)CallType.PreambleEmergencyCall: return "PreambleEmergencyCall";
case (int)CallType.PreambleGroupCSBKCall: return "PreambleGroupCSBKCall";
case (int)CallType.PreamblePrivateCSBKCall: return "PreamblePrivateCSBKCall";
case (int)CallType.RadioCheckRequest: return "RadioCheckRequest";
case (int)CallType.RadioCheckResponse: return "RadioCheckResponse";
case (int)CallType.RadioInhibitRequest: return "RadioInhibitRequest";
case (int)CallType.RadioInhibitResponse: return "RadioInhibitResponse";
case (int)CallType.RadioInhibitUninhibitNackResponse: return "RadioInhibitUninhibitNackResponse";
case (int)CallType.RadioMonitorNackResponse: return "RadioMonitorNackResponse";
case (int)CallType.RadioRemoteMonitorRequest: return "RadioRemoteMonitorRequest";
case (int)CallType.RadioRemoteMonitorResponse: return "RadioRemoteMonitorResponse";
case (int)CallType.RadioUninhibitRequest: return "RadioUninhibitRequest";
case (int)CallType.RadioUninhibitResponse: return "RadioUninhibitResponse";
default:
{
if ((type >= 0x00 && type <= 0x31) || (type >= 0x35 && type <= 0x3F) || (type >= 0x4F && type <= 0x54)
|| (type >= 0x5A && type <= 0x5C) || (type >= 0x86 && type <= 0xFF))
return "RESERVED";
else
return "Unknown call type";
}
}
}
private CallType GetCallType(byte type)
{
switch (type)
{
case (byte)CallType.AllCall: return CallType.AllCall;
case (byte)CallType.GroupVoiceCall: return CallType.GroupVoiceCall;
case (byte)CallType.PrivateVoiceCall: return CallType.PrivateVoiceCall;
case (byte)CallType.PrivateCallRequest: return CallType.PrivateCallRequest;
case (byte)CallType.PrivateCallResponse: return CallType.PrivateCallResponse;
case (byte)CallType.GroupPhoneCall: return CallType.GroupPhoneCall;
case (byte)CallType.PrivatePhoneCall: return CallType.PrivatePhoneCall;
case (byte)CallType.PhoneAllCall: return CallType.PhoneAllCall;
case (byte)CallType.CallAlertNackResponse: return CallType.CallAlertNackResponse;
case (byte)CallType.CallAlertRequest: return CallType.CallAlertRequest;
case (byte)CallType.CallAlertResponse: return CallType.CallAlertResponse;
case (byte)CallType.EmergencyCSBKAlertRequest: return CallType.EmergencyCSBKAlertRequest;
case (byte)CallType.EmergencyCSBKAlertResponse: return CallType.EmergencyCSBKAlertResponse;
case (byte)CallType.IPConsoleRadioInhibitRequest: return CallType.IPConsoleRadioInhibitRequest;
case (byte)CallType.IPConsoleRadioInhibitResponse: return CallType.IPConsoleRadioInhibitResponse;
case (byte)CallType.IPConsoleRadioUninhibitRequest: return CallType.IPConsoleRadioUninhibitRequest;
case (byte)CallType.IPConsoleRadioUninhibitResponse: return CallType.IPConsoleRadioUninhibitResponse;
case (byte)CallType.Other: return CallType.Other;
case (byte)CallType.PreambleEmergencyCall: return CallType.PreambleEmergencyCall;
case (byte)CallType.PreambleGroupCSBKCall: return CallType.PreambleGroupCSBKCall;
case (byte)CallType.PreamblePrivateCSBKCall: return CallType.PreamblePrivateCSBKCall;
case (byte)CallType.RadioCheckRequest: return CallType.RadioCheckRequest;
case (byte)CallType.RadioCheckResponse: return CallType.RadioCheckResponse;
case (byte)CallType.RadioInhibitRequest: return CallType.RadioInhibitRequest;
case (byte)CallType.RadioInhibitResponse: return CallType.RadioInhibitResponse;
case (byte)CallType.RadioInhibitUninhibitNackResponse: return CallType.RadioInhibitUninhibitNackResponse;
case (byte)CallType.RadioMonitorNackResponse: return CallType.RadioMonitorNackResponse;
case (byte)CallType.RadioRemoteMonitorRequest: return CallType.RadioRemoteMonitorRequest;
case (byte)CallType.RadioRemoteMonitorResponse: return CallType.RadioRemoteMonitorResponse;
case (byte)CallType.RadioUninhibitRequest: return CallType.RadioUninhibitRequest;
case (byte)CallType.RadioUninhibitResponse: return CallType.RadioUninhibitResponse;
case (byte)CallType.EmergencyVoiceCall: return CallType.EmergencyVoiceCall;
default:
{
if ((type >= 0x00 && type <= 0x31) || (type >= 0x35 && type <= 0x3F) || (type >= 0x4F && type <= 0x54)
|| (type >= 0x5A && type <= 0x5C) || (type >= 0x86 && type <= 0xFF))
return CallType.Reserved;
else
return CallType.Reserved;
}
}
}
private BurstType GetBurstType(byte type)
{
switch (type)
{
case (byte)BurstType.PrivacyHeader: return BurstType.PrivacyHeader;
case (byte)BurstType.VoiceBurstA: return BurstType.VoiceBurstA;
case (byte)BurstType.VoiceBurstB: return BurstType.VoiceBurstB;
case (byte)BurstType.VoiceBurstC: return BurstType.VoiceBurstC;
case (byte)BurstType.VoiceBurstD: return BurstType.VoiceBurstD;
case (byte)BurstType.VoiceBurstE: return BurstType.VoiceBurstE;
case (byte)BurstType.VoiceBurstF: return BurstType.VoiceBurstF;
case (byte)BurstType.VoiceTerminator: return BurstType.VoiceTerminator;
default: return BurstType.Unknown;
}
}
private string GetChannelStatus(int channelStatus)
{
switch (channelStatus)
{
case (int)ChannelStatus.Active: return "Active";
case (int)ChannelStatus.Idle: return "Idle";
case (int)ChannelStatus.SlotBlocked: return "SlotBlocked";
case (int)ChannelStatus.SlotUnblocked: return "SlotUnblocked";
case (int)ChannelStatus.BusyRestChannel: return "BusyRestChannel";
case (int)ChannelStatus.RestChannelIdleAvailable: return "RestChannelIdleAvailable";
case (int)ChannelStatus.LocalGroupCallsNotAllowed: return "LocalGroupCallsBotAllowed";
case (int)ChannelStatus.LocalGroupCallsAllowed: return "LocalGroupCallsAllowed";
case (int)ChannelStatus.RestChannelBlocked: return "RestChannelBlocked";
default:
{
if (channelStatus == 0x00 || (channelStatus >= 0x03 && channelStatus <= 0x09) ||
(channelStatus >= 0x0C && channelStatus <= 0x0F) || (channelStatus >= 0x15 && channelStatus <= 0xFF))
return "RESERVED";
else
return "Unknown Channel Control Status";
}
}
}
private string GetSlotNumberName(int slotNumber, MotoRepeater.LinkEstablishment.SystemType systemType)
{
switch (slotNumber)
{
case (int)SlotNumber.Slot1: return "Slot1";
case (int)SlotNumber.Slot2: return "Slot2";
case 0x00:
{
switch (systemType)
{
case MotoRepeater.LinkEstablishment.SystemType.IP_SITE_CONNECT: return "RESERVED";
case MotoRepeater.LinkEstablishment.SystemType.CAPACITY_PLUS: return "RestChannel";
case MotoRepeater.LinkEstablishment.SystemType.LINKED_CAPACITY_PLUS: return "SiteIPAddress";
default: return "RESERVED";
}
}
default: return "RESERVED";
}
}
private SlotNumber GetSlotNumber(int slotNumber, MotoRepeater.LinkEstablishment.SystemType systemType)
{
switch (slotNumber)
{
case (int)SlotNumber.Slot1: return SlotNumber.Slot1;
case (int)SlotNumber.Slot2: return SlotNumber.Slot2;
case 0x00:
{
switch (systemType)
{
case MotoRepeater.LinkEstablishment.SystemType.IP_SITE_CONNECT: return SlotNumber.Reserved;
case MotoRepeater.LinkEstablishment.SystemType.CAPACITY_PLUS: return SlotNumber.RestChannel;
case MotoRepeater.LinkEstablishment.SystemType.LINKED_CAPACITY_PLUS: return SlotNumber.SiteIPAddress;
default: return SlotNumber.Reserved;
}
}
default: return SlotNumber.Reserved;
}
}
private int ConvertCallSessionStatus4MessageBus(CallSessionStatus status)
{
switch (status)
{
case CallSessionStatus.CallInProgress: return (int)CallStatus.INPROGRESS;
case CallSessionStatus.CallHang: return (int)CallStatus.HANGTIME;
case CallSessionStatus.CallEnd: return (int)CallStatus.ENDED;
default: return 0;
}
}
private CallSessionStatus GetCallSessionStatus(int callSessionStatus)
{
switch (callSessionStatus)
{
case (int)CallSessionStatus.CallInProgress: return CallSessionStatus.CallInProgress;
case (int)CallSessionStatus.CallHang: return CallSessionStatus.CallHang;
case (int)CallSessionStatus.CallEnd: return CallSessionStatus.CallEnd;
default: return CallSessionStatus.RESERVED;
}
}
private string GetChannelControlStatus(int chCtrlStatus)
{
switch (chCtrlStatus)
{
case (int)ChannelControlStatus.Received: return "Received";
case (int)ChannelControlStatus.Transmiting: return "Transmiting";
case (int)ChannelControlStatus.TransmissionSuccessful: return "TransmissionSuccessful";
case (int)ChannelControlStatus.Declined: return "Declined";
default: return "RESERVED";
}
}
#endregion
#region WIRELINE CLASSES
private static Int64 queryID = 0x01;
public class CSBKCall
{
public SlotNumber slotNumber = SlotNumber.Slot1;
public Int64 callID
{
get { return _callID; }
}
private static Int64 _callID = 0x131313;
public CallType callType = CallType.PreamblePrivateCSBKCall;
public Int64 sourceID = 0;
public Int64 targetID = 0;
public AccessCriteria accessCriteria = AccessCriteria.PoliteAccess;
public CallAttributes callAttributes = CallAttributes.SmartPTT;
public PreambleDuration preambleDuration = PreambleDuration.MinDuration;
public Int16 preambleValue = 0x03;
// added for current CSBK Call Status
public ChannelControlStatus chnCtrlstatus = ChannelControlStatus.Declined;
public Int64 crtCallID = 0;
public Boolean isCSBKPreambleReceived = false;
public CSBKCall() { _callID++; }
}
public class WirelineRegistration
{
public SlotNumber slotNumber = SlotNumber.Slot1;
public static Int64 registrationPduID = BitConverter.ToInt32(new byte[] { 0x61, 0x01, 0x15, 0x43 }, 0);
public readonly Int64 registrationID = 0x1;
public RegistrationOperationCode regOpCode = RegistrationOperationCode.QueryRegStatus;
public WirelineChannelStatus wirelineChannelStatus = WirelineChannelStatus.NotRegisteredWLStatus;
public int numberOfRegistrationEntries = 0;
public List<RegEntry> regEntriesList = new List<RegEntry>();
public WirelineRegistration() { }
// add a new entry for which the status will be broadcasted
public void AddRegEntry(RegEntry entry)
{
this.numberOfRegistrationEntries++;
regEntriesList.Add(entry);
}
}
public class RegEntry
{
public AddressType addressType = AddressType.AllIndividualCall;
public Int64 addressRangeStart = 0;
public Int64 addressRangeEnd = 0;
public CSBKAttributes csbkAttributes = CSBKAttributes.CSBKRegisteredService;
public RegEntry(AddressType addressType, Int64 startID, Int64 endID)
{
this.addressType = addressType;
this.addressRangeStart = startID;
this.addressRangeEnd = endID;
}
}
public class WirelineQueryRegistration
{
public SlotNumber slotNumber = SlotNumber.Slot1;
public Int64 registrationPduID = 0;
public RegistrationOperationCode regOpCode = RegistrationOperationCode.QueryRegStatus;
public WirelineQueryRegistration() { }
}
#endregion
#region EVENT DELEGATES
public delegate void RadioStatusChangedDEl(Int64 radioID, SlotNumber slot, Boolean isEnabled);
public event RadioStatusChangedDEl OnRadioStatusChanged;
public delegate void EmergencyReceivedDEl(Int64 radioID, SlotNumber slot);
public event EmergencyReceivedDEl OnEmergencyChanged;
public delegate void RegistrationReceivedDEl(Boolean isValid, SlotNumber slot);
public event RegistrationReceivedDEl OnRegistrationReceived;
public delegate void CallStatusReceivedDEl(Int64 sourceID, Int64 targertID, int callType, int callStatus, SlotNumber slot);
public event CallStatusReceivedDEl OnCallStatusReceived;
private delegate void ChannelControlStatusReceivedDel(Int64 callID, CallType callType, ChannelControlStatus chnCtrlStatus, SlotNumber slot);
private event ChannelControlStatusReceivedDel OnChannelControlStatusReceived;
public delegate void CallSessionStatusReceivedDEl(Int64 sourceID, Int64 targertID, CallType callType, CallSessionStatus callSessionStatus, SlotNumber slot);
public event CallSessionStatusReceivedDEl OnCallSesionStatusReceived;
public delegate void CSBKCallReceivedDEl(Int64 sourceID, Int64 targetID, CallType callType, SlotNumber slot);
public event CSBKCallReceivedDEl OnCSBKCallReceived;
public delegate void AudioFrameReceivedDEl(byte[] audioFrame, SlotNumber slot);
public event AudioFrameReceivedDEl OnAudioFrameReceived;
#endregion
}
}