SafeDispatch/MotoRepeaterCore/Wireline.cs

2685 lines
129 KiB
C#
Raw Normal View History

2024-02-22 16:43:59 +00:00
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
}
}