602 lines
23 KiB
C#
602 lines
23 KiB
C#
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();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create the UDP Connection with every network interface
|
|
/// </summary>
|
|
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("");
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Create the UDP Connection with the master radio in case of the Capacity Plus system
|
|
/// </summary>
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Parse an ARS message received and add route if needed using the interface on which it was Received
|
|
/// </summary>
|
|
/// <param name="receiveBytes">ARS message bytes as received</param>
|
|
/// <param name="ep">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</param>
|
|
/// <param name="interfaceReceived">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.</param>
|
|
/// <param name="udpClient">The udp client on which the ACK response will be sent</param>
|
|
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;
|
|
}
|
|
}
|