SafeDispatch/MotoRepeaterCore/LocationThread.cs
2024-02-22 18:43:59 +02:00

610 lines
29 KiB
C#

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 + "]");
}
/// <summary>
/// Request the stop of a Triggered Location from a field radio
/// </summary>
/// <param name="SUID">The field radio ID which needs to stop sending location at a schedule time</param>
/// <param name="reqID">The triggered location ID that needs to be stopped [each field radio can have 4 Triggered Locations]</param>
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());
}
}
/// <summary>
/// Request the stop of a Triggered CSBK Location from a field radio
/// </summary>
/// <param name="SUID">The field radio ID which needs to stop sending location at a schedule time</param>
/// <param name="reqID">The triggered location ID that needs to be stopped [each field radio can have 4 Triggered Locations]</param>
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;
}
}