using System; using System.Collections.Generic; using System.Text; using System.Net; using SafeNetLib; using System.Net.Sockets; using System.Collections; using System.Threading; using System.Windows.Forms; using System.Net.NetworkInformation; namespace MotoTRBO_SOC { public class ARSThread { private readonly int READ_TIMEOUT = 8640; // (seconds) public UInt16 port; //public MotoTRBOGW parent; private UdpClient udpClient; private IPEndPoint RemoteIpEndPoint; public ARSThread(UInt16 p_port) { port = p_port; // update that the Thread is still running MotoTRBO_GW.lastARSThreadUpdate = DateTime.Now; //LOGS.LOG("Init ARS Thread on port " + port); } public void Start() { Utils.ConsWrite(DebugMSG_Type.ARS, "Initiating ARS Thread on port " + port); if (!MotoTRBO_GW.cfg.capPlus) { CreateUDPConnection(); } else { CreateUDPConnectionCapPlus(); } } /// /// Create the UDP Connection with every network interface /// private void CreateUDPConnection() { foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces()) { if (netInterface.Description.ToUpper().Contains("MOTOTRBO") || netInterface.Description.ToUpper().Contains("MOTOROLA")) { Console.WriteLine("Name: " + netInterface.Name); Console.WriteLine("Description: " + netInterface.Description); Console.WriteLine("Addresses: "); IPInterfaceProperties ipProps = netInterface.GetIPProperties(); foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses) { Console.WriteLine(" " + addr.Address.ToString()); if (addr.Address.ToString().Contains(":")) continue; try { IPAddress ip = null; if (IPAddress.TryParse(addr.Address.ToString(), out ip) == true) { String ipAddress = ip.ToString(); var udpClient2 = new UdpClient(AddressFamily.InterNetwork); udpClient2.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); udpClient2.Client.Bind(new IPEndPoint(IPAddress.Parse(ipAddress), port)); // fire event for UDP Connection successfully if (OnUDPConnectionChanged != null) OnUDPConnectionChanged(true, ipAddress); //udpClient2.JoinMulticastGroup(multicastAddress, localAddress); udpClient2.BeginReceive(OnReceiveSink, new object[] { udpClient2, new IPEndPoint(IPAddress.Parse(ipAddress), ((IPEndPoint) udpClient2.Client.LocalEndPoint).Port) }); } } catch (Exception) { } } Console.WriteLine(""); } } } /// /// Create the UDP Connection with the master radio in case of the Capacity Plus system /// private void CreateUDPConnectionCapPlus() { // fire event for UDP Connection is not up if (OnUDPConnectionChanged != null) OnUDPConnectionChanged(false, ""); try { udpClient = new UdpClient(port); udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); //udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, port)); udpClient.Client.ReceiveTimeout = 1000 * READ_TIMEOUT; Utils.WriteLine("ARS udp client initiated", ConsoleColor.Green); //UpdateLabelEvent("Registered for ARS messages"); // fire event for UDP Connection successfully if (OnUDPConnectionChanged != null) OnUDPConnectionChanged(true, ""); } catch (Exception ex) { if (ex.ToString().Contains(" Only one usage of each socket address")) { Console.ForegroundColor = ConsoleColor.Red; Utils.ConsWrite(DebugMSG_Type.always, "UDP Port for ARS is already in use. Please try again after fixing the error. \n" + ex.ToString()); Console.ForegroundColor = ConsoleColor.Gray; Thread.Sleep(500); //MessageBox.Show("UDP Port for SMS is already in use. Please try again after fixing the error.", "SMS Error"); //System.Windows.Forms.Application.Exit(); } else { Console.ForegroundColor = ConsoleColor.Red; Utils.ConsWrite(DebugMSG_Type.always, "ARS UDP Exception: " + ex.ToString()); Console.ForegroundColor = ConsoleColor.Gray; //MessageBox.Show("Could not create UDP Connection for SMS. Application will now close", "SMS Error"); //System.Windows.Forms.Application.Exit(); } } } private void OnReceiveSink(IAsyncResult result) { IPEndPoint ep = null; var args = (object[])result.AsyncState; var session = (UdpClient)args[0]; var local = (IPEndPoint)args[1]; byte[] buffer = session.EndReceive(result, ref ep); //Do what you want here with the data of the buffer Console.WriteLine("Message received from " + ep + " to " + local); ParseReceivedBytes(buffer, ep, local, session); // update that the Thread is still running MotoTRBO_GW.lastARSThreadUpdate = DateTime.Now; //We make the next call to the begin receive session.BeginReceive(OnReceiveSink, args); } public void HandleConnection() { Thread.Sleep(500); while (MotoTRBO_GW.running) { while (udpClient != null && MotoTRBO_GW.running) { try { // update that the Thread is still running MotoTRBO_GW.lastARSThreadUpdate = DateTime.Now; //IPEndPoint object will allow us to read datagrams sent from any source. RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0); // Blocks until a message returns on this socket from a remote host. Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint); ParseReceivedBytes(receiveBytes, RemoteIpEndPoint, RemoteIpEndPoint, udpClient); } catch (Exception ex) { if (ex.ToString().Contains("A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond")) { // read timeout has expired. This is a normal behaviour in my case when the udp socket is supposed to have a timout of 5 seconds } else { Utils.ConsWrite(DebugMSG_Type.DB, "##### ARSThread Exception #########\n" + ex.ToString()); udpClient = null; } } } // end while //Utils.ConsWrite(DebugMSG_Type.always, "### Error: UDPclient is NULL !!! - ARS thread closed"); if (udpClient == null) { int count = 0; while (MotoTRBO_GW.running && count < 6) { Thread.Sleep(500); count++; } // recreate the udp connection if (!MotoTRBO_GW.cfg.capPlus) { //CreateUDPConnection(); } else { CreateUDPConnectionCapPlus(); } } } } /// /// Parse an ARS message received and add route if needed using the interface on which it was Received /// /// ARS message bytes as received /// IP Address of the unit which reported the ARS. From it the subscriber id will be parsed /// by doing a transformation to decimal of the 2nd,3rd and 4th octets /// The network interface on which the ARS was received. In the system with multiple /// radio gateways, you will need to know on which network adapter to redirect all the traffic with that unit. This /// is done by creating a route using cmd. /// The udp client on which the ACK response will be sent private void ParseReceivedBytes(Byte[] receiveBytes, IPEndPoint ep, IPEndPoint interfaceReceived, UdpClient udpClient) { string returnData = Encoding.ASCII.GetString(receiveBytes); //Utils.ConsWrite(DebugMSG_Type.always, "»»» ARS RECEIVED " + returnData); //Utils.printBytesArray(receiveBytes); char[] separator = { '.' }; string[] su = ep.Address.ToString().Split(separator); uint radioID = (Convert.ToUInt32(su[1])) * 256 * 256 + (Convert.ToUInt32(su[2])) * 256 + Convert.ToUInt32(su[3]); string radioIP = su[0] + "." + su[1] + "." + su[2] + "." + su[3]; //Utils.ConsWrite(DebugMSG_Type.always, "»»» ARS RECEIVED from IP " + radioIP); string suid = radioID.ToString(); header_T reth = DecodePacket(receiveBytes); // detect type of ARS String arsType = (reth.pdu_type == 0x00 ? "ON" : (reth.pdu_type == 0x01 ? "OFF" : "??")); // trigger ARS received event if (reth.pdu_type == 0x00 || reth.pdu_type == 0x01) OnArsReceived(suid, (reth.pdu_type == 0x00 ? true : false)); Utils.ConsWrite(DebugMSG_Type.ARS, "»»» ARS " + arsType + " from " + suid); // do not do anything with this units because it is not assigned to this gateway if (DBconnThread.GetDbID4RadioID(suid) <= 0) { Utils.ConsWrite(DebugMSG_Type.ARS, "+++ Unit " + suid + " not assigned to this gateway"); return; } if (arsType.Contains("ON")) { LocationThread.SendTriggeredLocationSTOP(suid, LocationThread.REQ_ID); } if (reth.events == header_event.Initial_Event || reth.events == header_event.Refresh_Event) { // Sends ACK Byte[] sendBytes = { 0x00, 0x02, 0xBF, 0x00 }; udpClient.Send(sendBytes, sendBytes.Length, ep.Address.ToString(), ep.Port); Utils.ConsWrite(DebugMSG_Type.ARS, "««« ARS ACK for " + suid); //LOGS.LOG("ARS ACK sent !"); } if (reth.events == header_event.Initial_Event) { SMSmsg sms = new SMSmsg(); sms.suid = suid; sms.msg = "GPS started"; sms.req_conf = false; //MotoTRBOGW.sendSMSQueue.PostItem(sms); //resend all SMS waiting but check id the SMS is still in the list /* lock (SN_Queues.waitConfSMSList.SyncRoot) { ArrayList temp = new ArrayList(); foreach (SMSmsg msg in SN_Queues.waitConfSMSList) { if (msg.suid == suid) { //SN_Queues.sendSMSQueue.PostItem(msg); } else temp.Add(msg); } SN_Queues.waitConfSMSList = temp; } */ } switch (reth.pdu_type) { case 0x00: // registration PerformeARSon(suid); InsertARSinQueue(suid, "ON"); //Utils.ConsWrite(DebugMSG_Type.ARS, "SU " + suid + " registered"); //LocationThread.SendTriggeredLocationSTOP(suid, 0x01); Thread p = new Thread(delegate() { int coun = 0; while (MotoTRBO_GW.running && (coun++) < 6) { Thread.Sleep(500); } if (LocationThread.SendTriggeredLocationRequest(suid, ((SUinfo)SN_Queues.ht_SUInfo[suid]).repInterval)) if (OnTriggeredLocationRequest != null) OnTriggeredLocationRequest(suid, ((SUinfo)SN_Queues.ht_SUInfo[suid]).repInterval); }); p.Start(); //LocationThread.SendTriggeredLocationRequestCSBK(suid, ((SUinfo)SN_Queues.ht_SUInfo[suid]).repInterval); if(!MotoTRBO_GW.cfg.capPlus) RouteManager.RegisterRouteARS(suid, interfaceReceived); goto case 0x0F; case 0x0F: // query reply break; case 0x01: // deregistration InsertARSinQueue(suid, "OFF"); //Utils.ConsWrite(DebugMSG_Type.ARS, "SU " + suid + " unregistered"); LOGS.LOG("SU " + suid + " unregistered"); //mark ARS off in SUinfo //add ars info to HT ((SUinfo)SN_Queues.ht_SUInfo[suid]).arsCheckTime = DateTime.UtcNow; ((SUinfo)SN_Queues.ht_SUInfo[suid]).ARSon = false; break; default: break; } //Thread.Sleep(1000); } // ------------------------------------------------------------------- // Aux functions // ------------------------------------------------------------------- struct header_T { public bool ext, ack, priority, cntl; public byte pdu_type; public header_event events; public byte encoding; } header_T DecodePacket(Byte[] data) { int i, pdata; pdata = (int)data[0]; pdata <<= 8; pdata |= (int)data[1]; /* LOGS.WriteLine("Length =" + pdata + "(0x" + data[0].ToString("X") + "," + data[1].ToString("X") + ")"); Console.Write("Data: "); for (i = 2; i < pdata + 2; i++) Console.Write(" 0x" + data[i].ToString("X")); LOGS.WriteLine(); */ // ---------------------------------------- // parse header // ---------------------------------------- header_T header = new header_T(); header.ext = false; if ((data[2] & 0x80) != 0) header.ext = true; header.ack = false; if ((data[2] & 0x40) != 0) header.ack = true; header.priority = false; if ((data[2] & 0x20) != 0) header.priority = true; header.cntl = false; if ((data[2] & 0x10) != 0) header.cntl = true; header.pdu_type = (byte)(data[2] & 0x0F); switch (header.pdu_type) { case 0x00: Utils.ConsWrite(DebugMSG_Type.ARS, "ARS Registration"); break; case 0x0F: Utils.ConsWrite(DebugMSG_Type.ARS, "Query ACK"); break; case 0x01: Utils.ConsWrite(DebugMSG_Type.ARS, "Deregistration message"); break; default: Utils.ConsWrite(DebugMSG_Type.ARS, "Unknown ARS message"); break; } i = 3; if (i >= pdata + 2) { return header; } if (header.ext) { // read second header octet byte evt = data[i]; evt &= 0x70; evt >>= 5; switch (evt) { case 0: header.events = header_event.Unqualified_Event; break; case 1: header.events = header_event.Initial_Event; break; case 2: header.events = header_event.Refresh_Event; break; default: header.events = header_event.UNKNOWN; break; } header.events = (header_event)evt; header.encoding = (byte)(data[i] & 0x0f); i++; } if (i >= pdata + 2) { return header; } // ---------------------------------------- // parse address len // ---------------------------------------- int addr_len = data[i]; i++; if (i >= pdata + 2) { return header; } /* if (addr_len > 0) Console.Write("Address: "); for (int k = 0; k < addr_len; k++) Console.Write(" 0x" + data[i++].ToString("X")); LOGS.WriteLine(); */ // ---------------------------------------- // parse address len // ---------------------------------------- i -= 2; //LOGS.WriteLine("i=" + i); return header; } bool InsertARSinQueue(string p_radioID, string p_message) { try { ArsMSG ars = new ArsMSG(); ars.imei = p_radioID; ars.msg = p_message; SN_Queues.arsMsgQueue.PostItem(ars); return true; } catch (Exception e) { Utils.ConsWrite(DebugMSG_Type.always, "Error inserting ARS in Queue"); Utils.ConsWrite(DebugMSG_Type.always, e.Message); return false; } } private void PerformeARSon(string SUID) { try { if (SN_Queues.ht_SUInfo.Contains(SUID)) { //add ars info to HT ((SUinfo)SN_Queues.ht_SUInfo[SUID]).arsCheckTime = DateTime.UtcNow; ((SUinfo)SN_Queues.ht_SUInfo[SUID]).ARSon = true; //get reporting interval int repInter = ((SUinfo)SN_Queues.ht_SUInfo[SUID]).repInterval; //Utils.ConsWrite(DebugMSG_Type.GPS, "Interval for SUID:" + SUID + " =" + repInter); //create location message!! MotoTRBOcmdMsg msg = new MotoTRBOcmdMsg(); msg.m_cmd = (byte)MotoTRBOcmd.SET_REPORT_INTERVAL; msg.m_suid = SUID; msg.m_payload = repInter.ToString(); if (repInter != 0) { SN_Queues.locationQueue.PostItem(msg); } else { Utils.ConsWrite(DebugMSG_Type.GPS, "SUID:" + SUID + " interval=0 , no GPS reporting needed."); } } else { Utils.ConsWrite(DebugMSG_Type.GPS, "!!!!!! SUID:" + SUID + " Not found in DB!!!!!!!"); } } catch (Exception ex) { Utils.ConsWrite(DebugMSG_Type.ARS, "SUID:" + SUID + " ERROT in PerformeARSon"); Utils.ConsWrite(DebugMSG_Type.always, ex.ToString()); } } public void StopARSThread() { try { if (udpClient != null) { udpClient.Close(); udpClient = null; } } catch (Exception) { } } // this method is designed to send a public void onPowerUPGateway() { // run through all the units lock (SN_Queues.ht_SUInfo.SyncRoot) { if (SN_Queues.ht_SUInfo != null) { foreach (DictionaryEntry de in SN_Queues.ht_SUInfo) { string radioID = (string) de.Key; SUinfo unitInfo = (SUinfo) de.Value; Console.WriteLine(" {0,-17}{1}", de.Key, de.Value); // build all procedure to stop previous location trigger // and to send a new one LocationThread.SendTriggeredLocationSTOP(unitInfo.suid, 0xEE); int coun = 0; while (MotoTRBO_GW.running && (coun++) < 6) { Thread.Sleep(500); } if (LocationThread.SendTriggeredLocationRequest(unitInfo.suid, ((SUinfo)SN_Queues.ht_SUInfo[unitInfo.suid]).repInterval)) if (OnTriggeredLocationRequest != null) OnTriggeredLocationRequest(unitInfo.suid, ((SUinfo)SN_Queues.ht_SUInfo[unitInfo.suid]).repInterval); RouteManager.RegisterRouteARS(unitInfo.suid); } } } } public delegate void UDPConnectionChangedDelegate(bool isConnected, String gateway); public event UDPConnectionChangedDelegate OnUDPConnectionChanged; public delegate void ARSReceivedDEl(string suid, bool isOn); public event ARSReceivedDEl OnArsReceived; public delegate void TriggeredLocationRequestDEl(String radioID, int reportingInterval); public event TriggeredLocationRequestDEl OnTriggeredLocationRequest; } }