SafeNet/MotoTRBO_SOC/ARSThread.cs
2021-02-24 13:50:23 +02:00

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;
}
}