using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Text; using System.Net; using System.Net.Sockets; using System.Threading; using System.Collections; using SafeMobileLib; using SafeMobileLib.MessageDecoders; namespace MotoRepeater { class LocationThread { private static UInt16 port; private byte[] data = new byte[1024]; private static UdpClient udpClient; private static byte reqID_lastByte = 0; public static byte REQ_ID = 0xEE; private MotoRepeater_GW repeaterGw; public LocationThread(UInt16 locationPort, MotoRepeater_GW repeaterGw) { port = locationPort; this.repeaterGw = repeaterGw; } public void handleConnection() { Utils.WriteLine("♥♥♥ Location Thread Started on port " + port); try { udpClient = new UdpClient(port); } catch (Exception ex) { Utils.WriteLine("Location Thread Error Creating UDP Client: " + ex.ToString(), ConsoleColor.Red); } IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, port); while (MotoRepeater_GW.isRunning) { try { //Utils.WriteLine("WAITING FOR INFO ON LOCATION"); // 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.WriteLine("Location Thread received data from radio " + radioID); //put information on message bus try { LocationDecoder locDecoder = new LocationDecoder(suid, receivedBytes, true, false); if (locDecoder == null) { Utils.WriteLine("NULL", ConsoleColor.Yellow); continue; } if (locDecoder.result == (byte)Result_Codes_ENUM.SYNTAX_ERROR) { Utils.WriteLine("SYNTAX_ERROR", ConsoleColor.Yellow); } else if (locDecoder.result == (byte)Result_Codes_ENUM.PROTOCOL_ELEMENT_NOT_SUPPORTED) { int requestingInterval = repeaterGw.GetReportingInterval(suid); if(requestingInterval < 5) { Utils.WriteLine("Requesting interval for " + suid + " set to default value : 180 [found: " + requestingInterval + "]", ConsoleColor.DarkMagenta); requestingInterval = 180; } Utils.WriteLine("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, requestingInterval)) // raise the event if (OnTriggeredLocationRequest != null) OnTriggeredLocationRequest(suid, requestingInterval); } // portable stations doesn't support level of confidence else { // stop current Triggered Location Request SendTriggeredLocationSTOP(suid, REQ_ID, false); Thread p = new Thread(delegate() { int coun = 0; while (MotoRepeater_GW.isRunning && (coun++) < 6) { Thread.Sleep(500); } //https://stackoverflow.com/questions/13120904/mysql-tinyint1-vs-tinyint2-vs-tinyint3-vs-tinyint4 // send Triggered Location Request which contains the level of confidence if (SendTriggeredLocationRequest(suid, requestingInterval)) if (OnTriggeredLocationRequest != null) OnTriggeredLocationRequest(suid, requestingInterval); }); 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.result == (byte)Result_Codes_ENUM.QUERY_INFO_NOT_ATTAINABLE || locDecoder.result == (byte)Result_Codes_ENUM.QUERY_INFO_NOT_CURRENTLY_ATTAINABLE) ; //Utils.WriteLine(locDecoder.result + ""); 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.AddSeconds(MotoRepeater_GW.timeDifference.TotalSeconds)).Subtract(locDecoder.cell.location_time); Utils.WriteLine("CRT Time is " + DateTime.UtcNow.AddSeconds(MotoRepeater_GW.timeDifference.TotalSeconds).ToString(), ConsoleColor.Cyan); Utils.WriteLine("Location Time is " + locDecoder.cell.location_time.ToString(), ConsoleColor.Cyan); Utils.WriteLine("Level of Conficence " + locDecoder.cell.level_confidence.ToString(), ConsoleColor.Cyan); /* Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Time GPS " + locDecoder.cell.location_time.ToString()); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("AppTime " + DateTime.UtcNow.ToString()); ; Console.WriteLine("POSITION DIFFERENCE IS " + locationDifference.Seconds); Console.ForegroundColor = ConsoleColor.Gray; */ // if position in the future with more than 2 days if (Math.Abs(locationDifference.TotalSeconds) > 172800) { // skip this position continue; } String[] toSendString = new String[4]; toSendString[0] = DateTo70Format(locDecoder.cell.location_time).ToString(); toSendString[1] = locDecoder.cell.spd.ToString(); toSendString[2] = locDecoder.cell.lat.ToString(); toSendString[3] = locDecoder.cell.lng.ToString(); int opcode = 0; if (locDecoder.cell.triggered) { //SM.Debug("Location thread->location triggere response"); opcode = 131; Utils.WriteLine(String.Format("»»» Position [{0:0.0000},{1:0.0000}] from {2}", Math.Round(Double.Parse(locDecoder.cell.lat), 4), Math.Round(Double.Parse(locDecoder.cell.lng), 4), suid)); int speed = 0; int.TryParse(locDecoder.cell.spd, out speed); //Utils.WriteLine("POSITION TIME IS : " + locDecoder.cell.location_time.ToString()); LocationEventArgs e = new LocationEventArgs() { RadioID = Int64.Parse(suid), GPSTime = (Int64)locDecoder.cell.location_time.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds, Speed = speed, Latitude = Math.Round(Double.Parse(locDecoder.cell.lat), 4), Longitude = Math.Round(Double.Parse(locDecoder.cell.lng), 4), Altitude = locDecoder.cell.altitude, LevelOfConfidence = locDecoder.cell.level_confidence, seqID = locDecoder.cell.seqID }; OnLocationReceived?.Invoke(e); } else { Utils.WriteLine(String.Format("»»» Poll Res [{0:0.0000},{1:0.0000}] from {2} [{3}]", Math.Round(Double.Parse(locDecoder.cell.lat), 4), Math.Round(Double.Parse(locDecoder.cell.lng), 4), suid, locDecoder.cell.seq_ID)); //Utils.WriteLine("POLL SEQ ID " + locDecoder.cell.seq_ID + " | " + locDecoder.cell.seqID, ConsoleColor.Yellow); //SM.Debug("Location thread->location poll response"); opcode = 231; int speed = 0; int.TryParse(locDecoder.cell.spd, out speed); LocationEventArgs e = new LocationEventArgs() { RadioID = Int64.Parse(suid), GPSTime = (Int64)locDecoder.cell.location_time.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds, Speed = speed, Latitude = Math.Round(Double.Parse(locDecoder.cell.lat), 4), Longitude = Math.Round(Double.Parse(locDecoder.cell.lng), 4), Altitude = locDecoder.cell.altitude, LevelOfConfidence = locDecoder.cell.level_confidence, seqID = locDecoder.cell.seqID }; OnPollReceived?.Invoke(e); } /* Byte[] toSendMulticast = Utils.createLocationMessage(131, suid, toSendString); udpMulticast.Send(toSendMulticast, toSendMulticast.Length); string str = System.Text.Encoding.ASCII.GetString(toSendMulticast, 0, toSendMulticast.Length); //SM.Debug("Location thread successfully sent data to message bus: \n" + str); Utils.WriteLine("TX MultiBus Location [" + str.Trim() + "]"); */ } } } catch (Exception ex) { Utils.WriteLine("Error at parsing location " + ex.ToString(), ConsoleColor.Red); Utils.WriteLine(Utils.PrintBytesArray(receivedBytes), ConsoleColor.Red); } Thread.Sleep(100); } catch (Exception e) { Utils.WriteLine("••• LocationThread Exception " + e.ToString()); if (MotoRepeater_GW.isRunning) { Utils.WriteLine("••• Location Thread is restarting..."); try { /* udpClient = new UdpClient(); udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, port)); */ udpClient = new UdpClient(port); } catch (Exception ex) { Utils.WriteLine("••• Location Thread Error Creating UDP Client: " + ex.ToString()); } } } } Utils.WriteLine("Location Thread is getting killed", ConsoleColor.Yellow); // fire event OnLocationThreadKilled?.Invoke(); } 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); } 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, 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)Report_Messages_Tokens_ENUM.circle_2d, //0x51, (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.WriteLine("««« Triggered location request [" + report_time + " sec] for " + SUID); return true; //locationUdpClient.Close(); } catch (Exception ex) { Utils.WriteLine("Location Thread SendTriggeredLocationRequest exception: " + ex.ToString()); } return false; } 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, 0x01, 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, 0x01, //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, 0x1E */ }; /*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.WriteLine("««« Triggered location request [" + report_time + " sec] for " + SUID); Utils.PrintBytesArray(sendBytes); return true; //locationUdpClient.Close(); } catch (Exception ex) { Utils.WriteLine("Location Thread SendTriggeredLocationRequest exception: " + ex.ToString()); } return false; } #region location with LOC public static bool SendTriggeredLocationRequestWithLOC(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_info2, // 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.WriteLine("««« Triggered location request with altitude [" + report_time + " sec] for " + SUID); return true; } catch (Exception ex) { Utils.WriteLine("Location Thread SendTriggeredLocationRequestWithLOC exception: " + ex.ToString(), ConsoleColor.Red); return false; } } #endregion /* * */ public static void SendPollRequest(string SUID) { SendPollRequest(SUID, REQ_ID); } public static void SendPollRequest(string SUID, byte reqID) { reqID_lastByte++; if (reqID_lastByte > 0xff) reqID_lastByte = 0x00; Byte[] sendBytes = { (byte)Document_Identifiers_ENUM.Immediate_Location_Request_NoCDT, 0x08, // length in Bytes (byte)Common_Element_Tokens_ENUM.request_id, //0x04, 0x24, 0x68, 0xAC, 0xE0, 0x04, 0x24, 0x68, 0xAC, reqID, 0x51, //(byte)Query_Request_Messages_Tokens_ENUM.require_altitude}; (byte)Query_Request_Messages_Tokens_ENUM.request_speed_hor}; /* // improved poll request that I'm using in Safenet Conventional Gateway //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.WriteLine("»»» POLL location request for " + SUID + " [" + reqID + "]"); } /// /// 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, bool isCSBK) { if (!MotoRepeater_GW.cfg.SEND_LOCATION_STOP) 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, }; if(isCSBK) { sendBytes = new Byte[] { (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.WriteLine("»»» Triggered location STOP request for " + SUID); } catch (Exception ex) { Utils.WriteLine("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) { if (!MotoRepeater_GW.cfg.SEND_LOCATION_STOP) 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.WriteLine("««« Triggered location STOP request for " + SUID); } catch (Exception ex) { Utils.WriteLine("Location Thread SendTriggeredLocationSTOP exception: " + ex.ToString()); } } public void StopLocationThread() { if (udpClient != null) { udpClient.Close(); } } public delegate void LocationThreadKilled(); public event LocationThreadKilled OnLocationThreadKilled; public delegate void LocationReceived(LocationEventArgs e); public event LocationReceived OnLocationReceived; public delegate void PollReceived(LocationEventArgs e); public event PollReceived OnPollReceived; public delegate void TriggeredLocationRequestDEl(String radioID, int reportingInterval); public event TriggeredLocationRequestDEl OnTriggeredLocationRequest; } }