756 lines
36 KiB
Plaintext
756 lines
36 KiB
Plaintext
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}", Math.Round(Double.Parse(locDecoder.cell.lat), 4),
|
|
Math.Round(Double.Parse(locDecoder.cell.lng), 4), locDecoder.cell.suid));
|
|
|
|
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<lat:" + locDecoder.cell.lat + "><lng:" + locDecoder.cell.lng + ">");
|
|
}
|
|
// 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}]", Math.Round(Double.Parse(locDecoder.cell.lat), 4),
|
|
Math.Round(Double.Parse(locDecoder.cell.lng), 4), locDecoder.cell.suid, locDecoder.cell.seq_ID));
|
|
|
|
//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");
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Stop the Location UDP client
|
|
/// </summary>
|
|
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);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Request a field radio to send only GPS and Horizontal speed info at a specific interval
|
|
/// </summary>
|
|
/// <param name="SUID">Radio ID of the unit</param>
|
|
/// <param name="report_time">Time interval in seconds at which the radio has to report it's location</param>
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Request a field radio to send GPS, H and V speed, Level of Confidence info at a specific interval
|
|
/// </summary>
|
|
/// <param name="SUID">Radio ID of the unit</param>
|
|
/// <param name="report_time">Time interval in seconds at which the radio has to report it's location</param>
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Request a field radio to send GPS, Horizontal speed info at a specific interval, in a CSBK representation
|
|
/// </summary>
|
|
/// <param name="SUID">Radio ID of the unit</param>
|
|
/// <param name="report_time">Time interval in seconds at which the radio has to report it's location</param>
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
/// <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)
|
|
{
|
|
// 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());
|
|
}
|
|
}
|
|
|
|
|
|
/// <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)
|
|
{
|
|
// 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());
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Request a field radio to report immediate its location [also know as poll]
|
|
/// </summary>
|
|
/// <param name="SUID">The field radio ID which needs to send imeediate its location</param>
|
|
/// <param name="reqID">The triggered location ID for this poll request</param>
|
|
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;
|
|
|
|
}
|
|
}
|