using System; using SafeMobileLib; using MotoTRBO_XNL_Cmd; using MotoTRBO_GW; using System.ComponentModel; using System.Threading; using NAudio.Wave; namespace MotoTrbo_GW { public class RadioComHandler : INotifyPropertyChanged { private bool connected = false; private RADIO_STATUS RadioStatus; public RADIO_STATUS RadioStatusProp { get { return RadioStatus; } set { RadioStatus = value; RadioStatusChanged("RadioStatusProp"); } } public event PropertyChangedEventHandler PropertyChanged; private int radioID, radioIMEI,radioNetworkID; private string radioIP; private int INdevId, OUTdevID; private UdpMulticast udp4VoiceSend; private UdpMulticast udp4VoiceRecv; private RadioConnection radioCon; private System.Threading.Timer tCheckPTT; private System.Threading.Timer tCheckSTATUS; bool speakersOn = false; bool PTTbussy = false; private int voice_port = 17234; #region LINX #if LINXB public int CallType { get { return callType; } set { callType = value; } } /// /// Returns true if the base station is in PTT, else returns false /// public bool IsPTTbussy { get { return PTTbussy; } } /// /// Returns the radio id of the attached mobile radio /// public int RadioID { get { return radioID; } } private LINX.LinxCall _linxCallInfo; /// /// Gets or sets information about linx call /// public LINX.LinxCall LinxCallInfo { get { return _linxCallInfo; } set { _linxCallInfo = value; } } public bool GroupCallInitiatedByRadio { get; set; } private const int MS_TO_WAIT_FOR_LINX_CALL_SETUP = 3000; private System.Timers.Timer _timerPrivatePTTToLinx = null; #endif #endregion bool monitorRequest = false; int monitorID = 0; private int CALLER_ID = 0; private int CALL_TYPE = 0; private int callType=0; private string callSeqID="0.0"; private string voiceMulticastIP4Send; private string voiceMulticastIP4Recv; private int sw_zone_nr=0, sw_channel_nr=0; private static int id = 0; private int id_local = 0; private Thread listenThread; public BufferedWaveProvider buffWaveProvider = null; // voice settings region private AudioManager _audioManager = null; public int OutDevIDProperty { get { return OUTdevID; } } public int InDevIDProperty { get { return INdevId; } } // protected void RadioStatusChanged(string name) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { PropertyChangedEventArgs e =new PropertyChangedEventArgs(name); handler(this, e); } } /// /// HAndles messageBus->GW com & GW->radio com /// /// /// /// /// /// /// public RadioComHandler(RadioConnection rc,int radioID, string radioIP,int radioNetworkID, int radioIMEI, int INdevID, int OUTdevID) { id++; id_local = id; SafeMobileLib.Utils.WriteLine(id_local + "Constructor for radioConHandler with ID:" + radioID); RadioStatusProp = RADIO_STATUS.OFF; this.radioIP = radioIP; this.radioNetworkID = radioNetworkID; this.radioIMEI = radioIMEI; this.radioID = radioID; this.INdevId = INdevID; this.OUTdevID = OUTdevID; //start radio com //radioCon = new RadioConnection(radioIP, 8002, user4radioCon); radioCon = rc; //radioCon.OnAuthDone2 += new RadioConnection.AuthDoneDEl2(radioCon_OnAuthDone); radioCon.OnChannelEvent += new RadioConnection.ChannelEvent(radioCon_OnChannelEvent); radioCon.OnCallEvent += new RadioConnection.CallEvent(radioCon_OnCallEvent); radioCon.OnSpeakersEvent += new RadioConnection.SpeakersEvent(radioCon_OnSpeakersEvent); radioCon.OnRemoteControlEvent += new RadioConnection.RemoteControlEvent(radioCon_OnRemoteControlEvent); radioCon.OnTransmitEvent += new RadioConnection.TransmitEvent(radioCon_OnTransmitEvent); radioCon.OnRestartEvent += new RadioConnection.RestartEvent(radioCon_OnRestartEvent); //start voice bus for send voiceMulticastIP4Send = "224.10." + Main.GWID.ToString() + "." + radioID.ToString(); udp4VoiceSend = new UdpMulticast(voiceMulticastIP4Send, voice_port); RadioStatusProp = RADIO_STATUS.FREE; listenThread = new Thread(new ThreadStart(Listen2MessageBus)); listenThread.IsBackground = true; listenThread.Start(); #region LINX #if LINXB _timerPrivatePTTToLinx = new System.Timers.Timer(MS_TO_WAIT_FOR_LINX_CALL_SETUP); _timerPrivatePTTToLinx.Elapsed += _timerLinx_Elapsed; _timerPrivatePTTToLinx.AutoReset = false; #endif #endregion } private void Listen2MessageBus() { //SafeMobileLib.Utils.WriteLine("UDP multicast listen thread started!!!"+mcastGroup); while (true) { try { if (MessageBusHandler.MBMessageQueues_Hash.ContainsKey(radioID)) { MessageBusMessage msg = ((InterthreadMessageQueue) MessageBusHandler.MBMessageQueues_Hash[radioID]).GetItem(100); if (msg!=null) BusMessageParser(msg); } Thread.Sleep(100); } catch (ThreadAbortException) { break; } catch (Exception ex) { SafeMobileLib.Utils.WriteLine("RadioComHandler.cs->Listen2MessageBus" + ex.ToString()); Thread.Sleep(10); } } } private void BusMessageParser(MessageBusMessage msg) { SafeMobileLib.Utils.WriteLine("Got " + msg.OPcode, ConsoleColor.Red); if ((RadioStatus != RADIO_STATUS.FREE) && (msg.OPcode != 111)//all call stop && (msg.OPcode != 112)//private call stop && (msg.OPcode != 113)//group call stop && (msg.OPcode != 160)//Dkey && (RadioStatus != RADIO_STATUS.HangTime)) { SendRadioBussy(msg.SeqID, RadioStatus); return; } switch (msg.OPcode) { case 104: SafeMobileLib.Utils.WriteLine("RadioComHandler ID:"+radioID+" processing zone and channel change request"); channelQuerry_seqID = msg.SeqID; RadioStatusProp = RADIO_STATUS.ChannelQuery; StartCheckStatusTimer(); //send channel change request to radio radioCon.SendChannelChange(msg.ZoneNr, msg.ChannelNr); break; case 94: SafeMobileLib.Utils.WriteLine("RadioComHandler ID:" + radioID + "processing SOFTWARE zone and channel change request "); channelQuerry_seqID = msg.SeqID; SafeMobileLib.Utils.WriteLine("SOFTWARE channel broadcast sending it to message bus(opcode-124)"); //build string string test = "#124#" + Main.GWID + "." + radioID + "#" + sw_zone_nr.ToString() + "." + sw_channel_nr.ToString() + "#"; if (channelQuerry_seqID.Length > 2) MessageBusHandler.SendOnMsgBuss(channelQuerry_seqID, test); else MessageBusHandler.SendOnMsgBuss("0.0", test);//Broadcast break; case 101: SafeMobileLib.Utils.WriteLine($"RadioComHandler - processing all call request radioID = {radioID} "); //setting status to all call ini and starting timer RadioStatusProp = RADIO_STATUS.ALLcall_INIT; StartCheckStatusTimer(); if (PTTbussy) { SafeMobileLib.Utils.WriteLine("PTTbussy=" + PTTbussy + " speakersOn=" + speakersOn); test = "#121#" + Main.GWID + "." + radioID + "#2#"; MessageBusHandler.SendOnMsgBuss(msg.SeqID, test, $"It is busy. cannot process all call request radioID = {radioID}"); break; } radioCon.SendCallType(2, 0); bool ret = radioCon.SendPTTon(); if (ret) { voiceMulticastIP4Recv = msg.VoiceMulticastIP4Send; callType = 101; callSeqID = msg.SeqID; #if LINXB // Reset Linx call info to default value just in case ResetLinxCallInfoValue(); #endif } else { test = "#121#" + Main.GWID + "." + radioID + "#2#"; MessageBusHandler.SendOnMsgBuss(msg.SeqID, test, $"cannot process all call request radioID = {radioID}"); } break; case 111: SafeMobileLib.Utils.WriteLine($"RadioComHandler - processing all call stop for radioID = {radioID}"); if (RadioStatus != RADIO_STATUS.ALLcall_INPROGRES) { SafeMobileLib.Utils.WriteLine("Radio status!= ALLcall_INPROGRES could not process all call stop.Current Status:" + RadioStatus); //send 115 failed to SD string message = "#115#" + Main.GWID + "." + radioID + "#2#"; MessageBusHandler.SendOnMsgBuss(msg.SeqID, message, $"Not in progres, cannot process all call stop for radioID = {radioID}"); break; } if (PTTbussy == true) { callType = 101; callSeqID = msg.SeqID; radioCon.SendPTToff(); SafeMobileLib.Utils.WriteLine("UDP4VoiceRecv - Stop Listen " + " PTT BUSY 111"); udp4VoiceRecv.StopListen(); StopCheckPTTTimer(); } else { SafeMobileLib.Utils.WriteLine("PTTbussy != true could not process all call stop"); //send 115 failed to SD string message = "#115#" + Main.GWID + "." + radioID + "#2#"; MessageBusHandler.SendOnMsgBuss(msg.SeqID, message); } break; case 102: SafeMobileLib.Utils.WriteLine($"RadioComHandler - processing private call request for radioID = {radioID}"); //temporary removed speakersOn from IF if (PTTbussy) { SafeMobileLib.Utils.WriteLine("PTTbussy=" + PTTbussy + " speakersOn=" + speakersOn); test = "#122#" + Main.GWID + "." + radioID + "#2#"; MessageBusHandler.SendOnMsgBuss(msg.SeqID, test, $"It is busy, cannot process all call request for radioID = {radioID}"); break; } if (callType != 102 || RadioStatus != RADIO_STATUS.HangTime) { SafeMobileLib.Utils.WriteLine("Changing call type to private call 102"); radioCon.SendCallType(1, msg.SUID); } //seting status to private call and starting timer RadioStatusProp = RADIO_STATUS.PrivateCall_INIT; StartCheckStatusTimer(); bool ret2 = radioCon.SendPTTon(); if (ret2) { voiceMulticastIP4Recv = msg.VoiceMulticastIP4Send; callType = 102; callSeqID = msg.SeqID; #if LINXB // Reset Linx call info to default value just in case ResetLinxCallInfoValue(); #endif } else { SafeMobileLib.Utils.WriteLine("SendPTTon command has failed"); test = "#122#" + Main.GWID + "." + radioID + "#2#"; MessageBusHandler.SendOnMsgBuss(msg.SeqID, test, $"cannot process all call request for radioID = {radioID}"); } break; case 112: SafeMobileLib.Utils.WriteLine($"RadioComHandler - processing private call stop for radioID = {radioID}"); if (RadioStatus != RADIO_STATUS.PrivateCall_INPROGRES) { SafeMobileLib.Utils.WriteLine("Radio status!= PrivateCall_INPROGRES could not process private call stop.Current Status:" + RadioStatus); //send 115 failed to SD string message = "#116#" + Main.GWID + "." + radioID + "#2#"; MessageBusHandler.SendOnMsgBuss(msg.SeqID, message, $"Not in progress, cannot process private call stop for radioID = {radioID}"); break; } if (PTTbussy == true) { callType = 102; callSeqID = msg.SeqID; radioCon.SendPTToff(); SafeMobileLib.Utils.WriteLine("UDP4VoiceRecv - Stop Listen " + " PTT Busy 112"); udp4VoiceRecv.StopListen(); StopCheckPTTTimer(); } else { SafeMobileLib.Utils.WriteLine("PTTbussy != true could not process private call stop"); //send 115 failed to SD string message = "#116#" + Main.GWID + "." + radioID + "#2#"; MessageBusHandler.SendOnMsgBuss(msg.SeqID, message, $"Not in call, cannot process private call stop for radioID = {radioID}"); } break; case 103: SafeMobileLib.Utils.WriteLine($"RadioComHandler - processing group call request radioID = {radioID}"); if (PTTbussy) { SafeMobileLib.Utils.WriteLine("PTTbussy=" + PTTbussy + " speakersOn=" + speakersOn); test = "#123#" + Main.GWID + "." + radioID + "#2#"; MessageBusHandler.SendOnMsgBuss(msg.SeqID, test, $"It is busy, cannot process group call request for radioID = {radioID}"); break; } if (callType!=103 || RadioStatus != RADIO_STATUS.HangTime) { SafeMobileLib.Utils.WriteLine("Changing call type to group call 103"); radioCon.SendCallType(0, msg.GroupID); } RadioStatusProp = RADIO_STATUS.GroupCall_INIT; StartCheckStatusTimer(); ret = radioCon.SendPTTon(); if (ret) { voiceMulticastIP4Recv = msg.VoiceMulticastIP4Send; callType = 103; callSeqID = msg.SeqID; #if LINXB // Reset Linx call info to default value just in case ResetLinxCallInfoValue(); #endif } else { test = "#123#" + Main.GWID + "." + radioID + "#2#"; MessageBusHandler.SendOnMsgBuss(msg.SeqID, test, $"cannot process group call request for radioID = {radioID}"); } break; case 113: SafeMobileLib.Utils.WriteLine($"RadioComHandler - processing group call stop for radioID = {radioID}"); if (RadioStatus != RADIO_STATUS.GroupCall_INPROGRES) { SafeMobileLib.Utils.WriteLine("Radio status!= GroupCall_INPROGRES could not process group call stop.Current Status:" + RadioStatus); //send 115 failed to SD string message = "#117#" + Main.GWID + "." + radioID + "#2#"; MessageBusHandler.SendOnMsgBuss(msg.SeqID, message, $"Not in progress, cannot process group call stop for radioID = {radioID}"); break; } if (PTTbussy == true) { callType = 103; callSeqID = msg.SeqID; radioCon.SendPTToff(); SafeMobileLib.Utils.WriteLine("UDP4VoiceRecv - Stop Listen " + " PTT Busy 113"); udp4VoiceRecv.StopListen(); StopCheckPTTTimer(); } else { SafeMobileLib.Utils.WriteLine("PTTbussy != true could not process group call stop"); //send 115 failed to SD string message = "#117#" + Main.GWID + "." + radioID + "#2#"; MessageBusHandler.SendOnMsgBuss(msg.SeqID, message, $"Not in call, cannot process group call stop for radioID = {radioID}"); break; } break; case 158: { // Got new update available SafeMobileLib.Utils.WriteLine("GOT Update available " + msg.versionNumber, ConsoleColor.Green); OnNewVersionAvailable?.Invoke(msg.versionNumber); break; } case 160: SafeMobileLib.Utils.WriteLine("RadioComHandler ID:" + radioID + "processing DeKey"); RadioStatusProp = RADIO_STATUS.DKEY; StartCheckStatusTimer(); radioCon.SendDeKey(); break; case 161: SafeMobileLib.Utils.WriteLine("RadioComHandler ID:" + radioID + "processing remote monitor request." + "Id for remote monitor:" + msg.SUID); if (RadioStatus != RADIO_STATUS.FREE) { SendRadioBussy(msg.SeqID, RadioStatus); break; } RadioStatusProp = RADIO_STATUS.RemoteMonitor; StartCheckStatusTimer(); radioCon.SendRemoteMonitor(msg.SUID); RemoteseqID = msg.SeqID; break; case 238: { SafeMobileLib.Utils.WriteLine("GOT EMERGENCY ACK " + "[ type " + msg.AlarmType + "] for " + msg.SUID); if(msg.AlarmType != AlarmTypes.unknown) radioCon.SendEmergencyAck(msg.SUID); break; } default: //SafeMobileLib.Utils.WriteLine("Unknown opcode for BusMessageParser file: RADIOCOMHANDLER.cs " + opCode); break; } } private string GetCallType() { switch (RadioStatus) { case RADIO_STATUS.ALLcall_INIT: case RADIO_STATUS.ALLcall_INPROGRES: return "all call"; case RADIO_STATUS.PrivateCall_INIT: case RADIO_STATUS.PrivateCall_INPROGRES: return "private call"; case RADIO_STATUS.GroupCall_INIT: case RADIO_STATUS.GroupCall_INPROGRES: return "group call"; default: return "unknown call"; } } void udp4VoiceRecv_OnNewDataRecv(byte[] data, int dataLen) { SafeMobileLib.Utils.WriteLine($"Voice ({GetCallType()}) form SD on ip {voiceMulticastIP4Recv} -> Radio (IMEI: {this.radioIMEI}; IP: {this.radioIP}; soundIn: {INdevId}; soundOut: {OUTdevID}) "); try { buffWaveProvider?.AddSamples(data, 0, dataLen); } catch (InvalidOperationException) { // Buffer is full SafeMobileLib.Utils.WriteLine($"Radio (IMEI: {this.radioIMEI}; IP: {this.radioIP}; soundIn: {INdevId}; soundOut: {OUTdevID}) has voice from SD buffer full", ConsoleColor.Red); } catch (Exception ex) { SafeMobileLib.Utils.WriteLine(ex.ToString(), ConsoleColor.Red); } } private void waveInClass_DataAvailable(object sender, WaveInEventArgs e) { VoiceReceivedFromRadio(e.Buffer, e.BytesRecorded); } public void VoiceReceivedFromRadio(byte[] voiceBuffer, int nbOfAudioBytes) { if (speakersOn && nbOfAudioBytes != 0 && PTTbussy && !monitorRequest) { #if LINXB if (LinxCallInfo != LINX.LinxCall.DefaultValue) { // voice to linx RadioVoiceToLinx?.Invoke(this, new LINX.RadioVoiceToLinxEventArgs(voiceBuffer, nbOfAudioBytes)); if (CALL_TYPE == 103 && GroupCallInitiatedByRadio) { // Group call initiated by radio // Send voice to dispatcher also udp4VoiceSend.Send(voiceBuffer, nbOfAudioBytes); } } else { //SafeMobileLib.Utils.WriteLine("catre bigu Length: "+dataLen+"#" + voiceMulticastIP4Send); udp4VoiceSend.Send(voiceBuffer, nbOfAudioBytes); } #else SafeMobileLib.Utils.WriteLine($"Radio (IMEI: {this.radioIMEI}, IP: {this.radioIP}, soundIN: {INdevId}, soundOUT: {OUTdevID}) puts voice on multicast; " + nbOfAudioBytes + "#" + voiceMulticastIP4Send); udp4VoiceSend.Send(voiceBuffer, nbOfAudioBytes); #endif } if (speakersOn && nbOfAudioBytes != 0 && monitorRequest) { //SafeMobileLib.Utils.WriteLine("Sending audio data for ROMOTE MONITORING"); udp4VoiceSend.Send(voiceBuffer, nbOfAudioBytes); } } #region radio connection events static int count = 0; void radioCon_OnRestartEvent() { throw new NotImplementedException(); } void radioCon_OnRemoteControlEvent(bool status, REMOTE_RADIO_FUNCS func, int remote_radioId) { if (func == REMOTE_RADIO_FUNCS.Monitor) { if (status) { string test = "#162#" + Main.GWID + "." + radioID + "." + remote_radioId + "#1#"; MessageBusHandler.SendOnMsgBuss(RemoteseqID, test); monitorID = remote_radioId; monitorRequest = true; //start voice buss for send RadioStatusProp = RADIO_STATUS.RemoteMonitor_INPROGRES; } else { string test = "#162#" + Main.GWID + "." + radioID + "." + remote_radioId + "#2#"; MessageBusHandler.SendOnMsgBuss(RemoteseqID, test); RadioStatusProp = RADIO_STATUS.FREE; } } } void radioCon_OnChannelEvent(int zoneNr, int channelNr) { sw_zone_nr = zoneNr; sw_channel_nr = channelNr; //seqID = "0.0"; SafeMobileLib.Utils.WriteLine(id_local+"Got a channel broadcast sending it to message bus(opcode-124)"); //build string string test = "#124#" + Main.GWID + "." + radioID + "#" + zoneNr.ToString() + "." + channelNr.ToString() + "#"; if (channelQuerry_seqID.Length > 2) MessageBusHandler.SendOnMsgBuss(channelQuerry_seqID, test); else MessageBusHandler.SendOnMsgBuss("0.0", test);//Broadcast RadioStatusProp = RADIO_STATUS.FREE; //send event to GW if (this.OnChannelEvent != null) { this.OnChannelEvent(zoneNr, channelNr); } } void radioCon_OnCallEvent(CALL_STATUS status, int callerID, int callType, int groupID) { if (radioIMEI != callerID && callerID != 0) { //SafeMobileLib.Utils.WriteLine("#### " + status); SafeMobileLib.Utils.WriteLine("Portable On Call Event #### " + status, ConsoleColor.Magenta); //register route if (!Main.capacityPlus) { SafeMobileLib.Utils.WriteLine("RadioGW received a call from field unit with ID " + callerID + " on gateway radio with IP " + radioIP + " \n\r Adding route to route table if route not present.", ConsoleColor.Red); if (RouteManager.NetworkIDs_Hash.ContainsKey(radioIP)) { //RoutingUtils.addRoute(callerID.ToString(), radioIP, RouteManager.NetworkIDs_Hash[radioIP].ToString()); RoutingUtils.addRoute_withBuffer(callerID.ToString(), radioIP, RouteManager.NetworkIDs_Hash[radioIP].ToString()); } else { SafeMobileLib.Utils.WriteLine("RouteManager.NetworkIDs_Hash does not contain a key for " + radioIP); } //RoutingUtils.displayRoutes(); } CALLER_ID = callerID; int callStatus = 0; int _callType = 101; if (callType == 0x04) { _callType = 102; } else { _callType = (groupID == 0xffffff) ? 101 : 103; } CALL_TYPE = _callType; string message = ""; switch (status) { case CALL_STATUS.INIT: if ((_callType == 101)) // all call { SafeMobileLib.Utils.WriteLine("------------------------------------------------"); SafeMobileLib.Utils.WriteLine("Got INITIAL call from:" + callerID + " callType:" + callType + " groupID:" + groupID); callStatus = 1; //send broadcast message = "#125#" + Main.GWID + "." + radioID + "." + callerID.ToString() + "#" + callStatus + "#" + _callType + "#" + groupID + "#"; MessageBusHandler.SendOnMsgBuss("0.0", message, "all call - init"); //setting voice on/off PTTbussy = true; RadioStatusProp = RADIO_STATUS.ALLcall_INPROGRES; } break; case CALL_STATUS.INCALL: if (_callType == 102 || _callType == 103) // private or group call { SafeMobileLib.Utils.WriteLine("Got INCALL call from:" + callerID + " callType:" + callType + " groupID:" + groupID); callStatus = 1; #if LINXB bool isGroupCall = (_callType == 103); int? linxIDForPrivateCall = null; if (LinxCallInfo != LINX.LinxCall.DefaultValue) // Check if I'm in hangtime with a Linx { if (_callType == 102) { // Start timer just for private calls _timerPrivatePTTToLinx.Start(); } } else if (_callType == 102 && (linxIDForPrivateCall = LinxIDForPrivateCall(callerID)) != null) // Check if i make a private call to a linx { this.LinxCallInfo = new LINX.LinxCall(linxIDForPrivateCall.Value.ToString(), GetRadioSipID(callerID, false)); } else { // normal info for mBus //build string message = "#125#" + Main.GWID + "." + radioID + "." + callerID.ToString() + "#" + callStatus + "#" + _callType + "#" + groupID + "#"; MessageBusHandler.SendOnMsgBuss("0.0", message); } CallLinx?.Invoke(this, new LINX.CallLinxEventArgs(callerID, isGroupCall, groupID)); #else //send broadcast message = "#125#" + Main.GWID + "." + radioID + "." + callerID.ToString() + "#" + callStatus + "#" + _callType + "#" + groupID + "#"; MessageBusHandler.SendOnMsgBuss("0.0", message, "broadcast inCall"); #endif //setting voice on/off PTTbussy = true; RadioStatusProp = ( _callType == 102 ) ? RADIO_STATUS.PrivateCall_INPROGRES : RADIO_STATUS.GroupCall_INPROGRES; } break; case CALL_STATUS.HANGTIMER: if (_callType == 102 || _callType == 103) { SafeMobileLib.Utils.WriteLine("Got HANGtimmer call from:" + callerID + " callType:" + callType + " groupID:" + groupID); callStatus = 2; #if LINXB // Check if I'm in hangtime with a Linx if (LinxCallInfo != LINX.LinxCall.DefaultValue) { RadioEndsPTTtoLinx?.Invoke(this, new EventArgs()); if (this.CALL_TYPE == 103) { // group call, send hang time command to SD // normal info for mBus //build string message = "#125#" + Main.GWID + "." + radioID + "." + callerID.ToString() + "#" + callStatus + "#" + _callType + "#" + groupID + "#"; MessageBusHandler.SendOnMsgBuss("0.0", message); } } else { // normal info for mBus //build string message = "#125#" + Main.GWID + "." + radioID + "." + callerID.ToString() + "#" + callStatus + "#" + _callType + "#" + groupID + "#"; MessageBusHandler.SendOnMsgBuss("0.0", message); } #else //send broadcast message = "#125#" + Main.GWID + "." + radioID + "." + callerID.ToString() + "#" + callStatus + "#" + _callType + "#" + groupID + "#"; MessageBusHandler.SendOnMsgBuss("0.0", message, "broadcast hangtime"); #endif //setting voice on/off PTTbussy = false; RadioStatusProp = RADIO_STATUS.HangTime; } break; case CALL_STATUS.CALLEND: SafeMobileLib.Utils.WriteLine("Got callend from:" + callerID + " callType:" + callType + " groupID:" + groupID); callStatus = 3; #if LINXB if (RadioStatusProp != RADIO_STATUS.GroupCall_INIT && LinxCallInfo != LINX.LinxCall.DefaultValue) { // Clear the details of the previous linx call ResetLinxCallInfoValue(); GroupCallInitiatedByRadio = false; if (this.CALL_TYPE == 103) { // If is group call, send call end on mbus for dispatcher message = "#125#" + Main.GWID + "." + radioID + "." + callerID.ToString() + "#" + callStatus + "#" + _callType + "#" + groupID + "#"; MessageBusHandler.SendOnMsgBuss("0.0", message); } } else { //build string message = "#125#" + Main.GWID + "." + radioID + "." + callerID.ToString() + "#" + callStatus + "#" + _callType + "#" + groupID + "#"; MessageBusHandler.SendOnMsgBuss("0.0", message); } #else //send broadcast message = "#125#" + Main.GWID + "." + radioID + "." + callerID.ToString() + "#" + callStatus + "#" + _callType + "#" + groupID + "#"; MessageBusHandler.SendOnMsgBuss("0.0", message, "broadcast callend"); #endif //setting voice on/off PTTbussy = false; RadioStatusProp = RADIO_STATUS.FREE; SafeMobileLib.Utils.WriteLine("------------------------------------------------"); break; } } else { //this is for calls made from SD to GW. //callerID is the same as the ID of base station SafeMobileLib.Utils.WriteLine("Base Station On Call Event #### " + status, ConsoleColor.Magenta); if (status == CALL_STATUS.HANGTIMER) { //send broadcast string test = "#125#" + Main.GWID + "." + radioID + "." + callerID.ToString() + "#2#0#0#"; MessageBusHandler.SendOnMsgBuss("0.0", test, "broadcast hangtime"); RadioStatusProp = RADIO_STATUS.FREE; SafeMobileLib.Utils.WriteLine("------------------------------------------------"); } if (status == CALL_STATUS.CALLEND) { #if LINXB if (RadioStatusProp != RADIO_STATUS.GroupCall_INIT && LinxCallInfo != LINX.LinxCall.DefaultValue) { // Clear the details of the previous linx call ResetLinxCallInfoValue(); GroupCallInitiatedByRadio = false; } else { string test = "#125#" + Main.GWID + "." + radioID + "." + callerID.ToString() + "#3#0#0#"; MessageBusHandler.SendOnMsgBuss("0.0", test); } #else //send broadcast string test = "#125#" + Main.GWID + "." + radioID + "." + callerID.ToString() + "#3#0#0#"; MessageBusHandler.SendOnMsgBuss("0.0", test, "broadcast callend"); #endif RadioStatusProp = RADIO_STATUS.FREE; SafeMobileLib.Utils.WriteLine("------------------------------------------------"); } } } void radioCon_OnSpeakersEvent(bool onOFF) { if (onOFF) { SafeMobileLib.Utils.WriteLine("=============================="); } SafeMobileLib.Utils.WriteLine("speakers "+onOFF); if (!onOFF) { SafeMobileLib.Utils.WriteLine("=============================="); } speakersOn = onOFF; if (monitorRequest && !speakersOn) { SafeMobileLib.Utils.WriteLine("Speakers off.Turning remote monitor OFF"); RadioStatusProp = RADIO_STATUS.FREE; monitorRequest = false; string test = "#172#" + Main.GWID + "." + radioID + "." + monitorID + "#1#"; MessageBusHandler.SendOnMsgBuss("0.0", test); } //hangtimmer for private call is handled in speakers event //if (RadioStatus == RADIO_STATUS.CallEvent && !speakersOn && CALL_TYPE==102) if (RadioStatus == RADIO_STATUS.PrivateCall_INPROGRES && !speakersOn && CALL_TYPE == 102) { SafeMobileLib.Utils.WriteLine("Got HANGtimmer call from:" + CALLER_ID + " callType:" + callType + " groupID:0"); bool onOff = false; int callStatus = 2; //send broadcast string message = "#125#" + Main.GWID + "." + radioID + "." + CALLER_ID.ToString() + "#" + callStatus + "#102#0#"; MessageBusHandler.SendOnMsgBuss("0.0", message); //setting voice on/off PTTbussy = onOff; RadioStatusProp = RADIO_STATUS.FREE; } PTTbussy = onOFF; } void radioCon_OnTransmitEvent(bool onOFF) { try { //if call originate from BASE stattion it means that the base sation made a call // 101-all call 102 - private call 103- group call // send back to SD the appropiete response(call on/off) SafeMobileLib.Utils.WriteLine($"radioCon_OnTransmitEvent CALL TYPE : {callType}" ); if (callType != 0) { switch (callType) { case 101: if (onOFF) { SendALLcallACK2SD(callSeqID); } else { if (RadioStatusProp == RADIO_STATUS.ALLcall_INIT) { //ALL call could not be performend!!!(channel bussy or something) radioCon.SendPTToff(); } //send ack to SD string test = "#115#" + Main.GWID + "." +radioID + "#1#"; MessageBusHandler.SendOnMsgBuss(callSeqID, test, "all call off - send ack to SD"); PTTbussy = false; RadioStatusProp = RADIO_STATUS.FREE; tCheckPTT = null; } callType = 0; break; case 102: if (onOFF) SendPrivateCallACK2SD(callSeqID); else { if (RadioStatusProp == RADIO_STATUS.PrivateCall_INIT) { //Private call could not be performend!!!(channel bussy or something) radioCon.SendPTToff(); } //send ack to SD string test = "#116#" +Main.GWID + "." + radioID +"#1#"; MessageBusHandler.SendOnMsgBuss(callSeqID, test, "private call off - send ack to SD"); PTTbussy = false; RadioStatusProp = RADIO_STATUS.FREE; tCheckPTT = null; } callType = 0; break; #if LINXB case 1002: // Private linx call if (onOFF) { PTTbussy = true; RadioStatusProp = RADIO_STATUS.PrivateCall_INPROGRES; // Send 122 to notify dispatchers on mbus that radio gw is busy MessageBusHandler.SendOnMsgBuss("123456789", "#122#" + Main.GWID + "." + radioID + "#1#"); if (LinxCallInfo != LINX.LinxCall.DefaultValue) { // Fire event - linx call established LinxCallEstablished?.Invoke(this, new EventArgs()); } } else { if (RadioStatusProp == RADIO_STATUS.PrivateCall_INIT) { //Private call could not be performend!!!(channel bussy or something) radioCon.SendPTToff(); if (LinxCallInfo != LINX.LinxCall.DefaultValue) { // Notify the Linx LinxCallFailed?.Invoke(this, new EventArgs()); } } PTTbussy = false; RadioStatusProp = RADIO_STATUS.FREE; // send 116 on mbus to notify dispatchers that the radio gw is free string test = "#116#" + Main.GWID + "." + radioID + "#1#"; MessageBusHandler.SendOnMsgBuss("123456789", test); // tCheckPTT = null is not necessary } break; #endif case 103: if (onOFF) SendGroupCallACK2SD(callSeqID); else { if (RadioStatusProp == RADIO_STATUS.GroupCall_INIT) { //Group call could not be performend!!!(channel bussy or something) radioCon.SendPTToff(); } //send ack to SD string test = "#117#" + Main.GWID + "." + radioID + "#1#"; MessageBusHandler.SendOnMsgBuss(callSeqID, test, "group call off - send ack to SD"); PTTbussy = false; RadioStatusProp = RADIO_STATUS.FREE; tCheckPTT = null; } callType = 0; break; #if LINXB case 1003: // Group Linx call if (onOFF) { PTTbussy = true; RadioStatusProp = RADIO_STATUS.GroupCall_INPROGRES; // Send 123 to notify dispatchers on mbus that radio gw is busy MessageBusHandler.SendOnMsgBuss("123456789", "#123#" + Main.GWID + "." + radioID + "#1#"); if (LinxCallInfo != LINX.LinxCall.DefaultValue) { // Fire event - linx call established LinxCallEstablished?.Invoke(this, new EventArgs()); } else SafeMobileLib.Utils.WriteLine("------------- Here i should start the call but i dont do it", ConsoleColor.Red); } else { if (RadioStatusProp == RADIO_STATUS.GroupCall_INIT) { //Group call could not be performend!!!(channel bussy or something) radioCon.SendPTToff(); if (LinxCallInfo != LINX.LinxCall.DefaultValue) { // Notify linx to close the call LinxCallFailed?.Invoke(this, new EventArgs()); } } PTTbussy = false; RadioStatusProp = RADIO_STATUS.FREE; // send 117 on mbus to notify dispatchers that the readio gw is free string test = "#117#" + Main.GWID + "." + radioID + "#1#"; MessageBusHandler.SendOnMsgBuss(callSeqID, test); // tCheckPTT = null; this in useless } break; #endif } } } catch (Exception ex) { SafeMobileLib.Utils.WriteLine(ex.ToString()); } } //if timer expired(1min) auto send PTT off private void CheckPTT(Object state) { SafeMobileLib.Utils.WriteLine("@@@@@@@@@ PTT timer expired ... sending PTT oFF !!!!!!!!for:"+radioIP+"@@@@@@@@@@"); if (PTTbussy == true) { radioCon.SendPTToff(); SafeMobileLib.Utils.WriteLine("UDP4VoiceRecv - Stop Listen " + " PTT Expired "); udp4VoiceRecv.StopListen(); } } private void CheckSTATUS(Object state) { if ((RadioStatus != RADIO_STATUS.ALLcall_INPROGRES) && (RadioStatus != RADIO_STATUS.PrivateCall_INPROGRES)&& (RadioStatus != RADIO_STATUS.GroupCall_INPROGRES)&& (RadioStatus != RADIO_STATUS.RemoteMonitor_INPROGRES)&& (RadioStatus != RADIO_STATUS.HangTime) && (RadioStatus != RADIO_STATUS.FREE)) { RadioStatusProp = RADIO_STATUS.FREE; SafeMobileLib.Utils.WriteLine("!!!!!!!!Timer expired and nothing happend... setting radio status to FREE!!!!!"); } } private String RemoteseqID = ""; private string channelQuerry_seqID = ""; #endregion //methods for GW #region methods for GW public bool Start() { SafeMobileLib.Utils.WriteLine("IndecId:" + INdevId + "outdev:" + OUTdevID); //test radio com if (radioCon.Connected) { //start messageBus listner //udp.StartListen(); //start voice if ((INdevId != -1) && (OUTdevID != -1)) { NAudio.Wave.WaveFormat waveFormat = new NAudio.Wave.WaveFormat(Main.sampleRate, Main.bitdepth, 1); buffWaveProvider = new BufferedWaveProvider(waveFormat); if (Main.AudioDriver == AudioDriverType.Windows) { _audioManager = new AudioManager(OUTdevID, INdevId, Main.recordedBufferMiliseconds, waveFormat); _audioManager.WaveInAudioAvailable += waveInClass_DataAvailable; _audioManager.Init(buffWaveProvider); _audioManager.WaveInStartRecording(); _audioManager.Play(); } connected = true; return true; } else { SafeMobileLib.Utils.WriteLine("Unable to start audio device"); connected = false; return false; } } else return false; } public void Stop() { connected = false; //send stop message to MSG bus string test = "#100#" + Main.GWID + "." + radioID + "#0#"; MessageBusHandler.SendOnMsgBuss("0.0", test); _audioManager?.Dispose(); if (udp4VoiceRecv != null) { SafeMobileLib.Utils.WriteLine("UDP4VoiceRecv - Stop Listen " + " STOP"); udp4VoiceRecv.StopListen(); } //detach events radioCon.OnChannelEvent -= new RadioConnection.ChannelEvent(radioCon_OnChannelEvent); radioCon.OnCallEvent -= new RadioConnection.CallEvent(radioCon_OnCallEvent); radioCon.OnSpeakersEvent -= new RadioConnection.SpeakersEvent(radioCon_OnSpeakersEvent); radioCon.OnRemoteControlEvent -= new RadioConnection.RemoteControlEvent(radioCon_OnRemoteControlEvent); radioCon.OnTransmitEvent -= new RadioConnection.TransmitEvent(radioCon_OnTransmitEvent); radioCon.OnRestartEvent -= new RadioConnection.RestartEvent(radioCon_OnRestartEvent); if (listenThread != null) { listenThread.Abort(); listenThread = null; } } public void SendZoneChannelChange(int zoneNr, int channelNr) { radioCon.SendChannelChange(zoneNr, channelNr); } public void StartCheckStatusTimer() { tCheckSTATUS = new System.Threading.Timer(CheckSTATUS, null, 5000, System.Threading.Timeout.Infinite); } #endregion private void StartCheckPTTTimer() { if (tCheckPTT != null) tCheckPTT.Dispose(); tCheckPTT = new System.Threading.Timer(CheckPTT, null, 55000, System.Threading.Timeout.Infinite); } private void StopCheckPTTTimer() { if (tCheckPTT != null) tCheckPTT.Dispose(); tCheckPTT = null; } #region events public delegate void ChannelEvent(int zoneNr, int channelNr); public event ChannelEvent OnChannelEvent; public delegate void AuthDoneDEl(); public event AuthDoneDEl OnAuthDone; #endregion private void SendALLcallACK2SD(string _seqID) { string test = "#121#" + Main.GWID + "." + radioID + "#1#"; MessageBusHandler.SendOnMsgBuss(_seqID, test, "all call on - send ack to SD"); SafeMobileLib.Utils.WriteLine("\n\n########### **************** ALL CALL VOICE ACK: " + voiceMulticastIP4Recv); SafeMobileLib.Utils.WriteLine("########### **************** GwID =" + Main.GWID + " | radioID = " + radioID + " | \n\n"); PTTbussy = true; //start voice buss for send StartVoiceBuss(); StartCheckPTTTimer(); RadioStatusProp = RADIO_STATUS.ALLcall_INPROGRES; } private void SendPrivateCallACK2SD(string _seqID) { string test = "#122#" + Main.GWID + "." + radioID + "#1#"; MessageBusHandler.SendOnMsgBuss(_seqID, test, "private call on - send ack to SD"); SafeMobileLib.Utils.WriteLine("\n\n########### **************** PRIVATE CALL VOICE ACK: " + voiceMulticastIP4Recv); SafeMobileLib.Utils.WriteLine("########### **************** GwID =" + Main.GWID + " | radioID = " + radioID + " | \n\n"); PTTbussy = true; //start voice buss for send StartVoiceBuss(); StartCheckPTTTimer(); RadioStatusProp = RADIO_STATUS.PrivateCall_INPROGRES; } private void StartVoiceBuss() { udp4VoiceRecv = new UdpMulticast(voiceMulticastIP4Recv, voice_port); udp4VoiceRecv.OnNewDataRecv += new UdpMulticast.newData4Send(udp4VoiceRecv_OnNewDataRecv); udp4VoiceRecv.StartListen(Main.LocalIP); } private void SendGroupCallACK2SD(string _seqID) { string test = "#123#" + Main.GWID + "." + radioID + "#1#"; MessageBusHandler.SendOnMsgBuss(_seqID, test, "group call on - send ack to SD"); SafeMobileLib.Utils.WriteLine("\n\n########### **************** GROUP CALL VOICE ACK: " + voiceMulticastIP4Recv); SafeMobileLib.Utils.WriteLine("########### **************** GwID = " + Main.GWID + " | radioID = " + radioID + " | \n\n"); PTTbussy = true; //start voice buss for send StartVoiceBuss(); StartCheckPTTTimer(); RadioStatusProp = RADIO_STATUS.GroupCall_INPROGRES; } private void SendRadioBussy(string _seqID,RADIO_STATUS status) { SafeMobileLib.Utils.WriteLine("Sending Radio bussy. reason "+status +" status byte:"+((byte)status)); //string test = "#10#" + Main.GWID + "." + radioID + "#"+((byte)status).ToString()+"#2#"; string test = "#122#" + Main.GWID + "." + radioID + "#2#"; MessageBusHandler.SendOnMsgBuss(_seqID, test, "Radio is busy!"); } internal void ClearVoiceResources() { //if (r.rcHandler.waveOutClass != null) //{ // r.rcHandler.waveOutClass.Dispose(); // r.rcHandler.waveOutClass = null; //} //if (r.rcHandler.waveInClass != null) //{ // r.rcHandler.waveInClass.DataAvailable -= r.rcHandler.waveInClass_DataAvailable; // r.rcHandler.waveInClass.Dispose(); // r.rcHandler.waveInClass = null; //} if (_audioManager != null) { _audioManager.WaveInAudioAvailable -= waveInClass_DataAvailable; _audioManager.Dispose(); } } public delegate void NewVersionAvailableDel(String version); public event NewVersionAvailableDel OnNewVersionAvailable; #region METHODS for Linx #if LINXB internal bool InviteFromLinx(string linxSipID, int radioSipID, int radioID, bool isGroupCall) { bool success = false; if (IsPTTbussy) { SafeMobileLib.Utils.WriteLine($@"Decline the inivite from sip id {linxSipID} to radio {radioID} because base station {this.radioID} is ptt busy", ConsoleColor.DarkRed); } else { if (isGroupCall) { radioCon.SendCallType(0, radioID); // group radio ptt RadioStatusProp = RADIO_STATUS.GroupCall_INIT; } else { radioCon.SendCallType(1, radioID); // private radio ptt RadioStatusProp = RADIO_STATUS.PrivateCall_INIT; } // start the tCheckStatus timer StartCheckStatusTimer(); success = radioCon.SendPTTon(); if (success) { this.LinxCallInfo = new LINX.LinxCall(linxSipID, radioSipID); callType = isGroupCall ? 1003 : 1002; } else SafeMobileLib.Utils.WriteLine($@"Decline the inivite from sip id {linxSipID} to radio {radioID} because base station {this.radioID} failed to send PTT", ConsoleColor.DarkRed); } return success; } internal void LinxReceivesPTTFromRadio(LINX.LinxCall linxCall) { if (this.CALL_TYPE == 102) { // Stop the timer for private calls _timerPrivatePTTToLinx.Stop(); } else if (this.CALL_TYPE == 103) { // Ensure that voice is being sent to LINX this.LinxCallInfo = linxCall; } } private void _timerLinx_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { // If timer has elapsed then the dialog with linx has not been established on portable ptt button press // I will send dekey to the portable in this case if (RadioStatusProp == RADIO_STATUS.CallEvent) { RadioStatusProp = RADIO_STATUS.DKEY; StartCheckStatusTimer(); radioCon.SendDeKey(); RadioEndsPTTtoLinx?.Invoke(this, new EventArgs()); } } private int GetRadioSipID(int radioID, bool isGroup) { int? radioSipID = RadioSipIDRequested?.Invoke(radioID, isGroup); return radioSipID ?? -1; } private int? LinxIDForPrivateCall(int radioID) { int? linxID = LinxIDForPrivateCallRequested?.Invoke(radioID); return linxID; } public void ResetLinxCallInfoValue() { this.LinxCallInfo = LINX.LinxCall.DefaultValue; } #endif #endregion #region EVENTS for Linx #if LINXB public event EventHandler LinxCallEstablished; public event EventHandler LinxCallFailed; public event EventHandler CallLinx; public event EventHandler RadioVoiceToLinx; public event EventHandler RadioEndsPTTtoLinx; public delegate int? LinxIDForPrivateCallDel(int radioID); public event LinxIDForPrivateCallDel LinxIDForPrivateCallRequested; public delegate int RadioSipIDRequestedDel(int radioID, bool isGroup); public event RadioSipIDRequestedDel RadioSipIDRequested; #endif #endregion } public enum RADIO_STATUS { OFF = 0x00, FREE = 0x01, ALLcall_INIT = 101, ALLcall_INPROGRES = 121, PrivateCall_INIT = 102, PrivateCall_INPROGRES = 122, GroupCall_INIT = 103, GroupCall_INPROGRES = 123, ChannelQuery = 104, CallEvent = 125, RemoteMonitor = 161, RemoteMonitor_INPROGRES = 162, DKEY = 160, EnableDisable = 150, HangTime = 108, } }