using System; using System.Net; using System.Net.Sockets; using System.Threading; using SafeNetLib; using System.Collections; namespace MotoTRBO_SOC { public class LocationThread { private static int port; private byte[] data = new byte[1024]; private static UdpClient udpClient; private static byte reqID_lastByte=0; /* Poll Handler Section */ private Thread pollRequestHandlerThread; private Int32 reqID_send = 0x21; public static byte REQ_ID = 0xEE; private readonly int READ_TIMEOUT = 8640; // (seconds) public LocationThread(int locationPort) { port = locationPort; pollRequestHandlerThread = new Thread(new ThreadStart(pollRequestHandler)); pollRequestHandlerThread.Start(); } private void pollRequestHandler() { while (MotoTRBO_GW.running) { try { MotoTRBOcmdMsg msg = SN_Queues.locationQueue.GetItem(100); if (msg != null) { //Console.WriteLine("SN_Queues.locationQueue.Count:" + SN_Queues.locationQueue.Count); if (msg.m_cmd == (byte)MotoTRBOcmd.SEND_POLL) { // send poll request and add seq_ID to the poll request ProcessCommand(msg); // wait for 5 seconds before processing another poll request Thread.Sleep(5000); } else { // do nothing for now } } } catch (Exception) { } } } public void handleConnection() { Utils.ConsWrite(DebugMSG_Type.DB, "Location Thread - Initialized on port " + port); try { if (udpClient == null) { 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;//200000; if (OnUDPConnectionChanged != null) OnUDPConnectionChanged(true); Utils.WriteLine("Location udp client initiated", ConsoleColor.Green); } //UpdateLabelEvent("Registered for ARS messages"); } 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 Location 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, "Location 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(); } } IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0); while (MotoTRBO_GW.running) { while (udpClient != null) { try { // update that the Thread is still running MotoTRBO_GW.lastPositionThreadUpdate = DateTime.Now; // Blocks until a message returns on this socket from a remote host. Byte[] receivedBytes = udpClient.Receive(ref remoteIpEndPoint); char[] separator = { '.' }; string[] su = remoteIpEndPoint.Address.ToString().Split(separator); uint radioID = (Convert.ToUInt32(su[1])) * 256 * 256 + (Convert.ToUInt32(su[2])) * 256 + Convert.ToUInt32(su[3]); string suid = radioID.ToString(); Utils.ConsWrite(DebugMSG_Type.DB, "Location Thread received data from " + radioID); //Utils.printBytesArray(receivedBytes); //decode location try { LocationDecoder locDecoder = new LocationDecoder(suid, receivedBytes, false); if (locDecoder.result == (byte)Result_Codes_ENUM.SYNTAX_ERROR) { //SendTriggeredLocationSTOP(suid, REQ_ID); } else if (locDecoder.result == (byte)Result_Codes_ENUM.PROTOCOL_ELEMENT_NOT_SUPPORTED) { Utils.ConsWrite(DebugMSG_Type.DB, "PROTOCOL_ELEMENT_NOT_SUPPORTED " + (locDecoder.cell.isCSBK ? " [CSBK]" : "[LEV_OF_CONFIDENCE]")); try { // portable station doesn't support CSBK messages if (locDecoder.cell.isCSBK) { // stop current Triggered Location Request SendTriggeredLocationSTOPCSBK(suid, REQ_ID); // send Triggered Location Request which contains the level of confidence if (SendTriggeredLocationRequest(suid, ((SUinfo)SN_Queues.ht_SUInfo[suid]).repInterval)) // raise the event if (OnTriggeredLocationRequest != null) OnTriggeredLocationRequest(suid, ((SUinfo)SN_Queues.ht_SUInfo[suid]).repInterval); } // portable stations doesn't support level of confidence else { // stop current Triggered Location Request SendTriggeredLocationSTOP(suid, REQ_ID); Thread p = new Thread(delegate() { int coun = 0; while (MotoTRBO_GW.running && (coun++) < 6) { Thread.Sleep(500); } // send Triggered Location Request which contains the level of confidence if (SendPlainTriggeredLocationRequest(suid, ((SUinfo)SN_Queues.ht_SUInfo[suid]).repInterval)) if (OnTriggeredLocationRequest != null) OnTriggeredLocationRequest(suid, ((SUinfo)SN_Queues.ht_SUInfo[suid]).repInterval); }); p.Start(); } } catch (Exception) { } } else if (locDecoder.result == (byte)Result_Codes_ENUM.SUCCESS || locDecoder.result == (byte)Result_Codes_ENUM.QUERY_INFO_NOT_ATTAINABLE || locDecoder.result == (byte)Result_Codes_ENUM.QUERY_INFO_NOT_CURRENTLY_ATTAINABLE) { if (locDecoder.cell.lat != null) { // get the time difference between the GPS Position and the Global Time (which can be an internet time) TimeSpan locationDifference = (DateTime.Now).Subtract(locDecoder.cell.location_time); // if position in the future with more than 2 days if (Math.Abs(locationDifference.TotalSeconds) > 172800) { Utils.WriteLine("POSITION DIFFERENCE IS " + locationDifference.Seconds, ConsoleColor.Red); locDecoder.cell.location_time = DateTime.Now; // skip this position //continue; } String[] toSendString = new String[4]; toSendString[0] = DateTo70Format(locDecoder.cell.location_time).ToString(); // if speed was requested or not toSendString[1] = locDecoder.cell.spd != null ? locDecoder.cell.spd.ToString() : "0"; toSendString[2] = locDecoder.cell.lat.ToString(); toSendString[3] = locDecoder.cell.lng.ToString(); locDecoder.cell.suid = suid; try { // conver lat and long to double because the DB insert uses double values locDecoder.cell.d_lat = Double.Parse(locDecoder.cell.lat); locDecoder.cell.d_lng = Double.Parse(locDecoder.cell.lng); if (locDecoder.cell.spd == null) locDecoder.cell.spd = "0"; } catch (Exception) { // could not parse lat and long as a double value locDecoder.cell.d_lat = 0; locDecoder.cell.d_lng = 0; } // position was reported as usual if (locDecoder.cell.triggered) { //Utils.ConsWrite(DebugMSG_Type.DB, "»»» Position [" + Math.Round(Double.Parse(locDecoder.cell.lat), 4) + "," + Math.Round(Double.Parse(locDecoder.cell.lng), 4) + "] from " + locDecoder.cell.suid); Utils.ConsWrite(DebugMSG_Type.DB, String.Format("»»» Position [{0:0.0000},{1:0.0000}] from {2}, speed: {3}, speed_vrt {4}", Math.Round(Double.Parse(locDecoder.cell.lat), 4), Math.Round(Double.Parse(locDecoder.cell.lng), 4), locDecoder.cell.suid, locDecoder.cell.spd != null ? locDecoder.cell.spd : "null", locDecoder.cell.spd_v != null ? locDecoder.cell.spd_v : "null")); SN_Queues.DBQueueLocation.PostItem(locDecoder.cell); //ht_location_hash.Add(locDecoder.cell.suid, locDecoder.cell.location_time); //LOGS.LOG(MotoTRBO_GW.cfg.gw_id + locDecoder.cell.suid + " Trig GPS"); } // position was generated by a poll request else { Utils.ConsWrite(DebugMSG_Type.DB, String.Format("»»» Poll Res [{0:0.0000},{1:0.0000}] from {2} [{3}], speed :{4}, speed_vrt: {5}", Math.Round(Double.Parse(locDecoder.cell.lat), 4), Math.Round(Double.Parse(locDecoder.cell.lng), 4), locDecoder.cell.suid, locDecoder.cell.seq_ID, locDecoder.cell.spd != null ? locDecoder.cell.spd : "null", locDecoder.cell.spd_v != null ? locDecoder.cell.spd_v : "null")); //Utils.ConsWrite(DebugMSG_Type.DB, "Poll seq_ID : " + locDecoder.cell.seq_ID); try { lock (SN_Queues.ht_POLL_List.SyncRoot) { foreach (DictionaryEntry item in SN_Queues.ht_POLL_List) { //if (((POLLmsg)item.Value).suid == locDecoder.cell.suid) if (((POLLmsg)item.Value).requestID == locDecoder.cell.seqID) { ((POLLmsg)item.Value).response = locDecoder.cell.location_time; ((POLLmsg)item.Value).lat = locDecoder.cell.lat; ((POLLmsg)item.Value).lng = locDecoder.cell.lng; ((POLLmsg)item.Value).speed = locDecoder.cell.spd; SN_Queues.DBQueueLocation.PostItem(locDecoder.cell); SN_Queues.recvPOLLQueue.PostItem((POLLmsg)item.Value); //SN_Queues.ht_POLL_List.Remove(item); //Utils.ConsWrite(DebugMSG_Type.DB, "ADDED POLL TO HASH"); break; } } } } catch (Exception ex) { Utils.ConsWrite(DebugMSG_Type.always, "ERROR processing poll msg suid:" + locDecoder.cell.suid + " | " + ex.ToString()); } //SN_Queues.DBQueueLocation.PostItem(locDecoder.cell); } // received poll as a response to ARS if (!locDecoder.cell.triggered && locDecoder.cell.seq_ID.Length == 0) ; else // fire event for position received if (OnLocationReceived != null) OnLocationReceived(locDecoder.cell); } } } catch (Exception ex) { Console.WriteLine(ex.ToString()); } Thread.Sleep(3); } catch (Exception e) { if (e.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, "##### LocationThread Exception #########\n" + e.ToString()); udpClient = null; } // set udp client to null if I want to recreate the udp client //udpClient = null; } } if (OnUDPConnectionChanged != null) OnUDPConnectionChanged(false); int count = 0; while (MotoTRBO_GW.running && count < 6) { Thread.Sleep(500); count++; } try { // here the udpClient should be null so I have to recreate it 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; if (OnUDPConnectionChanged != null) OnUDPConnectionChanged(true); } catch (Exception ex) { } } Utils.ConsWrite(DebugMSG_Type.DB, "##### LocationThread ENDED #########\n"); } /// /// Stop the Location UDP client /// public void Stop() { try { if (udpClient != null) { udpClient.Close(); udpClient = null; } } catch (Exception) { } } static public string Byte2String(byte[] data) { string sdata = ""; int i; for (i = 0; i < data.Length; i++) { int ii; ii = (int)data[i]; sdata += "0x" + ii.ToString("X") + " "; } return sdata; } static public string Byte2String(byte[] data, int startIndex, int len) { string sdata = ""; int i; if (startIndex > data.Length) return ""; for (i = startIndex; i < startIndex + len && i < data.Length; i++) { int ii; ii = (int)data[i]; sdata += "0x" + ii.ToString("X") + " "; } return sdata; } private uint DateTo70Format(DateTime param) { long nOfSeconds; //Console.WriteLine("DateTo70Format param=" + param); System.DateTime dt70 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); TimeSpan span = param - dt70; nOfSeconds = (long)span.TotalSeconds; return ((uint)nOfSeconds); } /// /// Request a field radio to send only GPS and Horizontal speed info at a specific interval /// /// Radio ID of the unit /// Time interval in seconds at which the radio has to report it's location public static bool SendPlainTriggeredLocationRequest(string SUID, int report_time) { reqID_lastByte++; if (reqID_lastByte > 0xfe) reqID_lastByte = 0x00; Byte[] sendBytes = { (byte)Document_Identifiers_ENUM.Triggered_Location_Request_NoCDT, 0x0B, // length in Bytes (byte)Common_Element_Tokens_ENUM.request_id, //0x04, 0x24, 0x68, 0xAC, 0xE0, //0x04, 0x24, 0x68, 0xAC, 0xEE,//reqID_lastByte, 0x04, 0x00, 0x00, 0x00, REQ_ID, (byte)Query_Request_Messages_Tokens_ENUM.ret_info1, // location = circle-2d (byte)Query_Request_Messages_Tokens_ENUM.request_speed_hor, (byte)Query_Request_Messages_Tokens_ENUM.periodic_trigger, (byte)Query_Request_Messages_Tokens_ENUM.interval, 0x00, 0x00 //(byte)report_time// in seconds }; try { if (report_time < 0x80) { //sendBytes[10] = (byte)report_time; sendBytes[12] = (byte)report_time; } else { sendBytes[1] += 1; // adjust length int process = report_time; // MSB process >>= 7; process &= 0x007F; process |= 0x0080; sendBytes[12] = (byte)process; sendBytes[13] = (byte)(report_time & 0x007F); //LSB } udpClient.Client.SendTo(sendBytes, new IPEndPoint(IPAddress.Parse(Utils.ID2IP("12", SUID)), port)); //Utils.WriteLine("Triggered location request sent to radio ID " + SUID); Utils.ConsWrite(DebugMSG_Type.DB, "««« Triggered location request [" + report_time + " sec] for " + SUID); //locationUdpClient.Close(); return true; } catch (Exception ex) { Utils.ConsWrite(DebugMSG_Type.DB, "Location Thread SendTriggeredLocationRequest exception: " + ex.ToString()); return false; } } /// /// Request a field radio to send GPS, H and V speed, Level of Confidence info at a specific interval /// /// Radio ID of the unit /// Time interval in seconds at which the radio has to report it's location public static bool SendTriggeredLocationRequest(string SUID, int report_time) { reqID_lastByte++; if (reqID_lastByte > 0xfe) reqID_lastByte = 0x00; Byte[] sendBytes = { (byte)Document_Identifiers_ENUM.Triggered_Location_Request_NoCDT, 0x0E, // length in Bytes (byte)Common_Element_Tokens_ENUM.request_id, //0x04, 0x24, 0x68, 0xAC, 0xEE, 0x04, 0x00, 0x00, 0x00, REQ_ID, (byte)Query_Request_Messages_Tokens_ENUM.ret_info1, // location = circle-2d (byte)Query_Request_Messages_Tokens_ENUM.request_speed_hor, // horizontal speed (byte)Query_Request_Messages_Tokens_ENUM.request_speed_vrt, // vertical speed //(byte)Query_Request_Messages_Tokens_ENUM.request_altitude, //(byte)Query_Request_Messages_Tokens_ENUM.require_altitude_acc, //(byte)0x0A, (byte)Query_Request_Messages_Tokens_ENUM.request_lev_conf, // level of confidence (byte)0x00, (byte)Query_Request_Messages_Tokens_ENUM.periodic_trigger, (byte)Query_Request_Messages_Tokens_ENUM.interval, 0x00, 0x00 //(byte)report_time// in seconds }; try { if (report_time < 0x80) { //sendBytes[10] = (byte)report_time; sendBytes[15] = (byte)report_time; } else { sendBytes[1] += 1; // adjust length int process = report_time; // MSB process >>= 7; process &= 0x007F; process |= 0x0080; sendBytes[15] = (byte)process; sendBytes[16] = (byte)(report_time & 0x007F); //LSB } udpClient.Client.SendTo(sendBytes, new IPEndPoint(IPAddress.Parse(Utils.ID2IP("12", SUID)), port)); Utils.ConsWrite(DebugMSG_Type.DB, "««« Triggered location request [" + report_time + " sec] for " + SUID); return true; } catch (Exception ex) { Utils.ConsWrite(DebugMSG_Type.DB, "Location Thread SendTriggeredLocationRequest exception: " + ex.ToString()); return false; } } /// /// Request a field radio to send GPS, Horizontal speed info at a specific interval, in a CSBK representation /// /// Radio ID of the unit /// Time interval in seconds at which the radio has to report it's location public static bool SendTriggeredLocationRequestCSBK(string SUID, int report_time) { Byte[] sendBytes = { //0x05, 0x08, 0x22, 0x03, 0x00, 0x00, 0x01, 0x40, 0x01, 0x41 0x09, 0x0B, 0x22, 0x03, 0x00, 0x00, REQ_ID, 0x40, 0x01, 0x41, 0x34, 0x31, 0x1E /*(byte)Document_Identifiers_ENUM.Triggered_Location_Request_NoCDT, 0x0D, (byte)Common_Element_Tokens_ENUM.request_id, 0x03, 0x00, 0x00, 0x03, //reqID_lastByte, 0x52, (byte)Query_Request_Messages_Tokens_ENUM.require_speed_hor, 0x40, 0x01, 0x41, // start csbk (byte)Query_Request_Messages_Tokens_ENUM.periodic_trigger, (byte)Query_Request_Messages_Tokens_ENUM.interval, 0x0F*/ }; /*Byte[] sendBytes = { (byte)Document_Identifiers_ENUM.Triggered_Location_Request_NoCDT, 0x0F, // length in Bytes (byte)Common_Element_Tokens_ENUM.request_id, //0x04, 0x24, 0x68, 0xAC, 0xE0, 0x03, 0x00, 0x00, 0xEE,//reqID_lastByte, 0x51, (byte)Query_Request_Messages_Tokens_ENUM.request_speed_hor, // START OF impl_spec_data (byte)Query_Request_Messages_Tokens_ENUM.impl_spec_data, 0x01, 0x41, // 0x01 - length, 0x41 - request CSBK and LRRP (byte)Query_Request_Messages_Tokens_ENUM.periodic_trigger, (byte)Query_Request_Messages_Tokens_ENUM.interval };*/ try { /* if (report_time < 0x80) { //sendBytes[10] = (byte)report_time; sendBytes[13] = (byte)report_time; } else { sendBytes[1] += 1; // adjust length int process = report_time; // MSB process >>= 7; process &= 0x007F; process |= 0x0080; sendBytes[13] = (byte)process; sendBytes[14] = (byte)(report_time & 0x007F); //LSB } */ udpClient.Client.SendTo(sendBytes, new IPEndPoint(IPAddress.Parse(Utils.ID2IP("12", SUID)), port)); //Utils.WriteLine("Triggered location request sent to radio ID " + SUID); Utils.ConsWrite(DebugMSG_Type.DB, "««« Triggered location request [" + report_time + " sec] for " + SUID); //Utils.printBytesArray(sendBytes); //locationUdpClient.Close(); return true; } catch (Exception ex) { Utils.ConsWrite(DebugMSG_Type.DB, "Location Thread SendTriggeredLocationRequest exception: " + ex.ToString()); return false; } } /// /// Request the stop of a Triggered Location from a field radio /// /// The field radio ID which needs to stop sending location at a schedule time /// The triggered location ID that needs to be stopped [each field radio can have 4 Triggered Locations] public static void SendTriggeredLocationSTOP(string SUID, byte reqID) { // do not send location stop if not configured in the config file if (!MotoTRBO_GW.cfg.locationStop) return; byte[] arr_radioID = Utils.ID2ByteARR("12", SUID); Byte[] sendBytes = { (byte)Document_Identifiers_ENUM.Triggered_Location_Stop_Request_NoCDT, 0x06, // length in Bytes (byte)Common_Element_Tokens_ENUM.request_id, 0x04, 0x00, 0x00, 0x00, reqID, }; try{ udpClient.Client.SendTo(sendBytes, new IPEndPoint(IPAddress.Parse(Utils.ID2IP("12", SUID)), port)); Utils.ConsWrite(DebugMSG_Type.DB, "««« Triggered location STOP request for " + SUID); } catch (Exception ex) { Utils.ConsWrite(DebugMSG_Type.DB, "Location Thread SendTriggeredLocationSTOP exception: " + ex.ToString()); } } /// /// Request the stop of a Triggered CSBK Location from a field radio /// /// The field radio ID which needs to stop sending location at a schedule time /// The triggered location ID that needs to be stopped [each field radio can have 4 Triggered Locations] public static void SendTriggeredLocationSTOPCSBK(string SUID, byte reqID) { // do not send location stop if not configured in the config file if (!MotoTRBO_GW.cfg.locationStop) return; byte[] arr_radioID = Utils.ID2ByteARR("12", SUID); Byte[] sendBytes = { (byte)Document_Identifiers_ENUM.Triggered_Location_Stop_Request_NoCDT, 0x05, // length in Bytes (byte)Common_Element_Tokens_ENUM.request_id, 0x03, 0x00, 0x00, reqID, }; try { udpClient.Client.SendTo(sendBytes, new IPEndPoint(IPAddress.Parse(Utils.ID2IP("12", SUID)), port)); Utils.ConsWrite(DebugMSG_Type.DB, "««« Triggered location STOP request for " + SUID); } catch (Exception ex) { Utils.ConsWrite(DebugMSG_Type.DB, "Location Thread SendTriggeredLocationSTOP exception: " + ex.ToString()); } } /// /// Request a field radio to report immediate its location [also know as poll] /// /// The field radio ID which needs to send imeediate its location /// The triggered location ID for this poll request public static void SendPollRequest(string SUID, Int32 reqID_send) { //get reqid bytes byte[] intBytes = BitConverter.GetBytes(reqID_send); if (BitConverter.IsLittleEndian) Array.Reverse(intBytes); Byte[] sendBytes = { (byte)Document_Identifiers_ENUM.Immediate_Location_Request_NoCDT, 0x0A, // length in Bytes (byte)Common_Element_Tokens_ENUM.request_id, //0x04, 0x24, 0x68, 0xAC, 0xE0, 0x04, intBytes[0], intBytes[1], intBytes[2], intBytes[3], (byte)Query_Request_Messages_Tokens_ENUM.ret_info1, //(byte)Query_Request_Messages_Tokens_ENUM.require_altitude}; (byte)Query_Request_Messages_Tokens_ENUM.request_speed_hor, (byte)Query_Request_Messages_Tokens_ENUM.request_lev_conf, // level of confidence (byte)0x00,}; udpClient.Client.SendTo(sendBytes, new IPEndPoint(IPAddress.Parse(Utils.ID2IP("12", SUID)), port)); Utils.ConsWrite(DebugMSG_Type.DB, "««« POLL location request for " + SUID + " [" + reqID_send + "]"); } void ProcessCommand(MotoTRBOcmdMsg p_msg) { try { reqID_send++; if (reqID_send == Int32.MaxValue) reqID_send = 1; p_msg.m_seqID = reqID_send; p_msg.m_time = DateTime.Now; if (p_msg.m_cmd == (byte)MotoTRBOcmd.SEND_POLL) { SendPollRequest(p_msg.m_suid, reqID_send); if(OnImmediateLocationRequest != null) OnImmediateLocationRequest(p_msg.m_suid); //Utils.ConsWrite(DebugMSG_Type.GPS, "Poll request sent for unit:" + p_msg.m_suid); lock (SN_Queues.ht_POLL_List.SyncRoot) { foreach (DictionaryEntry item in SN_Queues.ht_POLL_List) { try { if (((POLLmsg)item.Value).suid == p_msg.m_suid && ((POLLmsg)item.Value).DBid.ToString() == p_msg.m_payload) { ((POLLmsg)item.Value).sent = DateTime.Now.ToUniversalTime(); ((POLLmsg)item.Value).requestID = reqID_send; //Utils.ConsWrite(DebugMSG_Type.DB, "Poll request found in MotoTRBOGW.ht_POLL_List for unit:" + p_msg.m_suid); //Utils.ConsWrite(DebugMSG_Type.DB, "reqID_send:" + reqID_send); SN_Queues.sentPOLLQueue.PostItem((POLLmsg)item.Value); break; } } catch (Exception ex) { Utils.ConsWrite(DebugMSG_Type.always, "ERROR in foreach (DictionaryEntry item in SN_Queues.ht_POLL_List):"); Utils.ConsWrite(DebugMSG_Type.always, ex.Message); } } } } } catch (Exception e) { Utils.ConsWrite(DebugMSG_Type.always, "Could not send Location Request to unit"); Utils.ConsWrite(DebugMSG_Type.always, e.Message); } } public Boolean isOnline() { return udpClient == null ? false : true; } public delegate void UDPConnectionChangedDelegate(bool isConnected); public event UDPConnectionChangedDelegate OnUDPConnectionChanged; public delegate void LocationReceivedDEl(htCell_t position); public event LocationReceivedDEl OnLocationReceived; public delegate void TriggeredLocationRequestDEl(String radioID, int reportingInterval); public event TriggeredLocationRequestDEl OnTriggeredLocationRequest; public delegate void ImmediateLocationRequestDEl(String radioID); public event ImmediateLocationRequestDEl OnImmediateLocationRequest; } }