SafeDispatch/MotoTrbo_GW/SMSSendThread.cs
2024-02-22 18:43:59 +02:00

585 lines
26 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MotoTRBO_GW;
using System.Net.Sockets;
using SafeMobileLib;
using System.Net;
using System.Threading;
using System.Diagnostics;
namespace MotoTrbo_GW
{
class SMSSendThread
{
private byte WAIT_SMS_SEC = 5; // max no of seconds to wait for SMS ack
private int MAX_SMS_SIZE = 140; // max SMS allowed size
private Int32 smsPort = 0;
private String mIP;
private Int32 mPort;
int sms_seq_id;
public UdpClient udpClientSMS = null;
private UdpMulticast udpMulticastBusConnection = null;
public static List<String> acknowledgedMessagesSeqNo = new List<string>();
/* Send sms thread worker and queue */
private Thread SendSMSThreadobj;
private static InterthreadMessageQueue<String> sendSMSQueue = new InterthreadMessageQueue<String>();
public DBsmsManager DBSms = null;
public SMSSendThread(Int32 port, String multicastID, String multicastPort)
{
// register for the event when a message needs to be removed from the ACK queue
// because we need a new seqID for sending a new message
SMSConfirm.OnRemoveFromQueue += delegate (Int32 radioID, string seqNo)
{
SafeMobileLib.Utils.WriteLine("OnRemoveFromQueue", ConsoleColor.Red, ConsoleType.SMS);
// encode a fake ack message
System.Text.Encoding enc = System.Text.Encoding.ASCII;
byte[] buf = enc.GetBytes(SMSReceiveThread.GetAck4MessagebusForSequence(seqNo + ""));
//send SMS ack
udpMulticastBusConnection.Send(buf, buf.Length);
// add message to the acknowledged ones
SMSSendThread.acknowledgedMessagesSeqNo.Add(seqNo);
};
smsPort = port;
mIP = multicastID;
mPort = Int32.Parse(multicastPort);
SendSMSThreadobj = new Thread(new ThreadStart(SendSMSWorker));
SendSMSThreadobj.IsBackground = true;
SendSMSThreadobj.Start();
}
public void handleConnection()
{
SafeMobileLib.Utils.WriteLine("SMSSend Thread started", ConsoleType.SMS);
try
{
udpClientSMS = new UdpClient();
udpClientSMS.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
udpClientSMS.Client.Bind(new IPEndPoint(IPAddress.Any, smsPort));
}
catch (Exception ex)
{
SafeMobileLib.Utils.WriteLine("SMSSendThread handleConnection exception while creating udpClient: " + ex.ToString(), ConsoleColor.Red, ConsoleType.SMS);
}
try
{
udpMulticastBusConnection = new UdpMulticast(mIP, mPort);
udpMulticastBusConnection.OnNewDataRecv += new UdpMulticast.newData4Send(udp_OnNewDataReceived);
udpMulticastBusConnection.StartListen(Main.LocalIP);
//SafeMobileLib.Utils.WriteLine("SMSSendThread successfully registered to multicast group");
}
catch (Exception ex)
{
SafeMobileLib.Utils.WriteLine("SMSSendThread exception while joining the multicast group: " + ex.ToString(), ConsoleColor.Red, ConsoleType.SMS);
}
while (true)
{
Thread.Sleep(100);
}
}
private void SendSMSWorker()
{
while (Program.isRunning)
{
try
{
//MotoTRBOcmdMsg msg = MotoTRBOGW.locationQueue.GetItem(100);
String msg = sendSMSQueue.GetItem(-1);//block until message is in queue
string opCode = "";
bool wasSent = DecodeMessageBusMsg(msg, out opCode);
// sleep only if sms and if it was sent
if (opCode.Equals((int)MessageBusCmds.SendSMSRequest + "") || opCode.Equals((int)MessageBusCmds.SendSMSRequest2 + "")
|| opCode.Equals((int)MessageBusCmds.SendGroupSMSRequest + "") || opCode.Equals((int)MessageBusCmds.SendGroupSMSRequest2 + ""))
{
if (wasSent)
{
Thread.Sleep(Main.sms_sending_interval); //minimum interval between SMS as per MotoTRBO specs is 1200
//SafeMobileLib.Utils.WriteLine("WAIT ", ConsoleColor.Red);
}
else
Thread.Sleep(1);
}
// don't need to sleep the thread more than 1 ms because the queue is blocking already
else
Thread.Sleep(1);
}
catch (Exception ex)
{
SafeMobileLib.Utils.WriteLine("SendSMSWorkerException: " + ex.ToString(), ConsoleType.SMS);
}
} // end while (true)
}
void udp_OnNewDataReceived(byte[] data, int dataLen)
{
bool shouldAddAgain = true;
try
{
string str = Encoding.ASCII.GetString(data, 0, dataLen);
//SafeMobileLib.Utils.WriteLine("RECEIVED " + str, ConsoleColor.Yellow);
String[] tempArray = str.Trim().Split('#');
if (tempArray.Length > 3)
{
DecodeMessageBusMsg(str, out tempArray[3]);
/*
if (tempArray[3].Equals((int)MessageBusCmds.SendSMSRequest + "") || tempArray[3].Equals((int)MessageBusCmds.SendSMSRequest2 + ""))
{
// skip message if already acknowledged
if (acknowledgedMessagesSeqNo.Contains(tempArray[2]))
return;
// get the radio imei and then parse the radioID
String IMEI = (tempArray[4].Split('.'))[2];
Int32 radioID = 0;
Int32.TryParse(IMEI, out radioID);
// add an entity into the smsConfirmation queue
if (!Main.smsConfirmForUnits.ContainsKey(radioID))
Main.smsConfirmForUnits.Add(radioID, new SMSConfirm(radioID));
// skip this message because it already exists in the sending queue
int nr_of_retries = Main.smsConfirmForUnits[radioID].GetNumberOfRetries(tempArray[2]);
bool exists = Main.smsConfirmForUnits[radioID].ExistsSequenceInQueue(tempArray[2]);
if (exists && nr_of_retries <= Main.number_of_retries)
{
shouldAddAgain = false;
//SafeMobileLib.Utils.WriteLine("MESSAGE [" + str + "] is waiting for ACK [" + nr_of_retries + "]", ConsoleColor.Yellow);
}
// send fake ack and remove it from the confirmation queue if send for more than number of retries
else if (exists && nr_of_retries > Main.number_of_retries)
{
shouldAddAgain = false;
// encode a fake ack message
System.Text.Encoding enc = System.Text.Encoding.ASCII;
byte[] buf = enc.GetBytes(SMSReceiveThread.GetAck4MessagebusForSequence(sms_seq_id + ""));
SafeMobileLib.Utils.WriteLine("MESSAGE [" + str + "] is expired [" + nr_of_retries + "]", ConsoleColor.DarkCyan, ConsoleType.SMS);
//send SMS ack
udpMulticastBusConnection.Send(buf, buf.Length);
}
// add message to queue
if (shouldAddAgain)
{
//SafeMobileLib.Utils.WriteLine("ADDED TO sendSMSQueue " + str, ConsoleColor.Blue);
sendSMSQueue.PostItem(str);
}
}*/
}
}
catch (Exception ex)
{
SafeMobileLib.Utils.WriteLine(ex.ToString());
}
}
/// <summary>
/// Decode a message received from the message bus
/// </summary>
/// <param name="msg"></param>
private bool DecodeMessageBusMsg(String msg, out String opCode)
{
bool wasSent = false;
String[] tempArray = msg.Trim().Split('#');
if (tempArray.Length > 3)
{
opCode = tempArray[3];
if (opCode.Equals((int)MessageBusCmds.SendSMSRequest + "") || opCode.Equals((int)MessageBusCmds.SendSMSRequest2 + ""))
{
// skip message if already acknowledged
if (acknowledgedMessagesSeqNo.Contains(tempArray[2]))
return false;
int sched_timeGMT = 0;
if (tempArray.Length > 6)
sched_timeGMT = Convert.ToInt32(tempArray[6]);
// SafeMobileLib.Utils.WriteLine("SMS sched time={0} and current time ={1}",sched_timeGMT, DBmanager.DateTo70Format(DateTime.Now.ToUniversalTime()));
int gwid_recv = Convert.ToInt32((tempArray[4].Split('.'))[0]);
int radio_gwid_recv = Convert.ToInt32((tempArray[4].Split('.'))[1]);
// detect if the message is for this gateway (the radio gateway will be selected by the route management)
if (gwid_recv == Main.GWID)
{
if (sched_timeGMT <= (DateTime.Now.ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds))
{
// SafeMobileLib.Utils.WriteLine("SCHED " + sched_timeGMT + " [" + msg.Trim() + "]", ConsoleColor.White);
// SafeMobileLib.Utils.WriteLine("TIME " + (DBmanager.DateTo70Format(DateTime.Now.ToUniversalTime())), ConsoleColor.White);
// get the radio imei and then parse the radioID
String IMEI = (tempArray[4].Split('.'))[2];
Int32 radioID = 0;
Int32.TryParse(IMEI, out radioID);
// add an entity into the smsConfirmation queue
if (!Main.smsConfirmForUnits.ContainsKey(radioID))
Main.smsConfirmForUnits.Add(radioID, new SMSConfirm(radioID));
// get the sequence number that was used previously
if (Main.smsConfirmForUnits[radioID].ExistsSequenceInQueue(tempArray[2]))
sms_seq_id = Main.smsConfirmForUnits[radioID].getFromConfirmationQueueBySeq(tempArray[2]);
// add message to the queue only if requires ack
else if (Main.wait4ack)
sms_seq_id = Main.smsConfirmForUnits[radioID].addToConfirmationQueue(tempArray[2]);
// sometimes I receive a message with 0.0, used only when waiting for ack
if ((sms_seq_id == 0 || tempArray[2].StartsWith("0.0")) && Main.wait4ack)
{
SafeMobileLib.Utils.WriteLine("COULD NOT FOUND SMS SEQUENCE ID " + tempArray[2], ConsoleColor.Red, ConsoleType.SMS);
return false;
}
// do not send again the message if already ack
if (SMSSendThread.acknowledgedMessagesSeqNo.Contains(tempArray[2]))
{
SafeMobileLib.Utils.WriteLine("SMS already ACK" + tempArray[2], ConsoleColor.Red, ConsoleType.SMS);
return false;
}
if (Main.wait4ack)
{
// increment number of sendings and last sending
Main.smsConfirmForUnits[radioID].UpdateNumberOfRetries(tempArray[2]);
}
int lastSent = Main.smsConfirmForUnits[radioID].GetLastSchedTime(tempArray[2]); ;
int nr_of_retries = Main.smsConfirmForUnits[radioID].GetNumberOfRetries(tempArray[2]);
if (DateTime.Now.ToUniversalTime().DateTo70Format() - lastSent < 60)
{
SafeMobileLib.Utils.WriteLine("GOT AN 143 THAT SHOULDN'T EXIST HERE" + msg, ConsoleColor.Red, ConsoleType.SMS);
return false;
}
if (Main.wait4ack)
{
Main.smsConfirmForUnits[radioID].UpdateLastSchedTime(tempArray[2],
(int)DateTime.Now.ToUniversalTime().DateTo70Format());
}
// SafeMobileLib.Utils.WriteLine("SMSSendThread received from multicast bus: " + str.Trim());
if ((sms_seq_id != -1) && Main.wait4ack)
{
//SafeMobileLib.Utils.WriteLine("Sending SMS with ACK");
if (nr_of_retries <= Main.number_of_retries)
{
SendSMS(IMEI, tempArray[5], 1, sms_seq_id);
wasSent = true;
}
}
else
{
//SafeMobileLib.Utils.WriteLine("Sending SMS without ACK");
SendSMS(IMEI, tempArray[5], -1, sms_seq_id);
wasSent = true;
if (!Main.wait4ack)
{
// encode a fake ack message
System.Text.Encoding enc = System.Text.Encoding.ASCII;
byte[] buf = enc.GetBytes(SMSReceiveThread.GetAck4MessagebusForSequence(tempArray[2] + ""));
//send SMS ack
udpMulticastBusConnection.Send(buf, buf.Length);
}
}
// add message back to queue in case the ack is not received to be sent again
if (Main.wait4ack)
{
//SafeMobileLib.Utils.WriteLine(tempArray[2] + " - Number of retries is " + nr_of_retries, ConsoleColor.Magenta);
if (nr_of_retries > Main.number_of_retries)
{
// remove the message from the confirmation queue
Main.smsConfirmForUnits[radioID].getFromConfirmationQueue(sms_seq_id);
// add message to the acknowledged ones
acknowledgedMessagesSeqNo.Add(tempArray[2]);
SafeMobileLib.Utils.WriteLine("Message failed to be delivered " + nr_of_retries + " times. Sending fake ack." + msg, ConsoleColor.Yellow, ConsoleType.SMS);
// encode a fake ack message
System.Text.Encoding enc = System.Text.Encoding.ASCII;
byte[] buf = enc.GetBytes(SMSReceiveThread.GetAck4MessagebusForSequence(tempArray[2] + ""));
//send SMS ack
udpMulticastBusConnection.Send(buf, buf.Length);
// flag that no mmesage was sent
wasSent = false;
}
else
{
// change the scheduleACK time to force the message to be send after 'nr_of_retries * 60 sec'
//tempArray[6] = (DBmanager.DateTo70Format(DateTime.Now.ToUniversalTime()) + (nr_of_retries * 60)) + "";
tempArray[6] = (DateTime.Now.ToUniversalTime().DateTo70Format() + (Main.retry_interval_sec)) + "";
msg = String.Join("#", tempArray);
sendSMSQueue.PostItem(msg);
SafeMobileLib.Utils.WriteLine("[" + nr_of_retries + "] Added message back to queue " + msg, ConsoleColor.Green, ConsoleType.SMS);
}
}
}
else
{
// add again message to the queue to be sent later
sendSMSQueue.PostItem(msg);
// flag that no mmesage was sent
wasSent = false;
}
}
}
else if (tempArray[3].Equals("144") || tempArray[3].Equals("145"))
{
//#60#3.1562590094636981976945036122#144#2.4.1#asd#1562590094#
// SafeMobileLib.Utils.WriteLine("SMSSendThread received from multicast bus: " + str.Trim());
int sched_timeGMT = 0;
if (tempArray.Length > 6)
sched_timeGMT = Convert.ToInt32(tempArray[6]);
// SafeMobileLib.Utils.WriteLine("SMS sched time={0} and current time ={1}",sched_timeGMT, DBmanager.DateTo70Format(DateTime.Now.ToUniversalTime()));
int gwid_recv = Convert.ToInt32((tempArray[4].Split('.'))[0]);
if (gwid_recv == Main.GWID)
{
if (sched_timeGMT <= (DateTime.Now.ToUniversalTime().DateTo70Format()))
{
DBSms = new DBsmsManager(Main.DBServer, Main.DBSchema, Main.DBUser, Main.DBPass, Main.DBPort);
bool alreadySent = DBSms.Get_Group_SMS_Status(tempArray[5], tempArray[2]);
if (alreadySent)
{
Thread.Sleep(500);
return wasSent = true;
}
SafeMobileLib.Utils.WriteLine("Send group SMS without ACK " + tempArray[5] + " for " + tempArray[4] + " received... sending it to radio", ConsoleType.SMS);
String cpsID = (tempArray[4].Split('.'))[2];
//Thread.Sleep(2000);
//Boolean RetSMS = gw.SendSMS(radioID, tempArray[5]);
//SMShandle.Add2QueueCPS(tempArray[2], cpsID, tempArray[5]);
SendSMSGroup(cpsID, tempArray[5], -1, sms_seq_id);
string test = "#244#1#";
String cmdok = "#" + tempArray[2] + test;
Int32 tmp = cmdok.Length + 1; tmp += tmp.ToString().Length; cmdok = "#" + tmp.ToString() + cmdok;
System.Text.Encoding enc = System.Text.Encoding.ASCII;
byte[] buf = enc.GetBytes(cmdok);
//send SMS ack
udpMulticastBusConnection.Send(buf, buf.Length);
SafeMobileLib.Utils.WriteLine("TX:" + cmdok);
wasSent = true;
}
else
{
// add again message to the queue to be sent later
sendSMSQueue.PostItem(msg);
// flag that no mmesage was sent
wasSent = false;
}
}
}
return wasSent;
}
else
{
opCode = "0";
return wasSent;
}
}
Byte[] PackSMS(string msg_body, bool confirm, int p_sms_seq_id)
{
int msg_len = msg_body.Length;
if (msg_len > MAX_SMS_SIZE)
{
msg_len = MAX_SMS_SIZE;
SafeMobileLib.Utils.WriteLine("Warning: SMS truncated to " + msg_len + " characters", ConsoleType.SMS);
}
if (msg_len > msg_body.Length)
{
msg_body = msg_body.Remove(MAX_SMS_SIZE);
}
// compute the len
int len = msg_len;
len *= 2; // each character is encoded on 2B
len += 4; // this is for the headers
Byte[] data = new Byte[len + 2]; //+2 for the msg size
int msb = (len & 0xff00) >> 8;
data[0] = (byte)(msb & 0x00ff);
data[1] = (byte)(len & 0x00ff);
// put the headers
if (confirm)
{
data[2] = 0xe0; // first header
}
else
{
data[2] = 0xa0; // first header
}
data[3] = 0x00; // addr size
byte lsb_id = (byte)(p_sms_seq_id & 0x1f);
data[4] = (byte)(0x80 | lsb_id);
byte msb_id = (byte)(p_sms_seq_id & 0x60); // keep bits 6:5
data[5] = (byte)(msb_id | 0x04);
for (int i = 0; i < msg_len; i++)
{
data[6 + i * 2] = (byte)msg_body[i];
data[6 + i * 2 + 1] = 0;
}
return data;
}
public void SendSMS(string SUID, string msg, int msg_idx, int p_sms_seq_id)
{
try
{
// detect if ticketing message
if (msg.Contains("<JTS>") == true && msg.Contains("\r\n") == true)
{
string[] TicketString = msg.Split('^');
msg = TicketString[0];
//comment or message body
if (TicketString.Length > 1)
{
if (TicketString[1] != "") msg += string.Format("({0})", TicketString[1]);
}
if (TicketString.Length > 3)
{
if (TicketString[4] != "")
{
msg += " until ";
msg += (Convert.ToInt32(TicketString[4])).GetDTLocalFromSeconds().ToString("dd/MM/yyyy HH:mm:ss, ddd");
}
}
}
int wait_confirm_sec = 0;
if (msg_idx > 0)
wait_confirm_sec = WAIT_SMS_SEC;
if (msg_idx == -1)
wait_confirm_sec = 0;
if (wait_confirm_sec != 0)
{
Byte[] sendBytes = PackSMS(msg, true, p_sms_seq_id);
SafeMobileLib.Utils.WriteLine("««« SMS with confirmation [" + msg + "] to " + SUID, ConsoleType.SMS);
udpClientSMS.Send(sendBytes, sendBytes.Length, (new RadioID2IP("12", SUID)).GetIP(), smsPort);//12
}
else
{
Byte[] sendBytes = PackSMS(msg, false, p_sms_seq_id);
SafeMobileLib.Utils.WriteLine("««« SMS without confirmation [" + msg + "] to " + SUID, ConsoleType.SMS);
udpClientSMS.Send(sendBytes, sendBytes.Length, (new RadioID2IP("12", SUID)).GetIP(), smsPort);//12
}
}
catch (Exception exc)
{
SafeMobileLib.Utils.WriteLine(exc.ToString(), ConsoleColor.Red);
}
}
public void SendSMSGroup(string SUID, string msg, int msg_idx, int p_sms_seq_id)
{
try
{
if (!MotoTrbo_GW.Main.capacityPlus)
{
foreach (GatewayID_IP gatewayRadio in MotoTrbo_GW.Main.gatewayRadios)
{
String commandGRP = "route delete 225.0.0.0";
ExecuteCommand(commandGRP);
Thread.Sleep(200);
commandGRP = "route add 225.0.0.0 mask 255.0.0.0 " + gatewayRadio.remoteIP + " metric 80 IF " + gatewayRadio.ID;
SafeMobileLib.Utils.WriteLine("**** Add groups IP\n" + commandGRP, ConsoleType.SMS);
ExecuteCommand(commandGRP);
Thread.Sleep(1000);
Byte[] sendBytes = PackSMS(msg, true, p_sms_seq_id);
udpClientSMS.Send(sendBytes, sendBytes.Length, (new RadioID2IP("225", SUID)).GetIP(), smsPort);
}
}
else
{
Byte[] sendBytes = PackSMS(msg, true, p_sms_seq_id);
udpClientSMS.Send(sendBytes, sendBytes.Length, (new RadioID2IP("225", SUID)).GetIP(), smsPort);
}
}
catch (Exception exc)
{
SafeMobileLib.Utils.WriteLine(exc.ToString());
}
}
private static void ExecuteCommand(String command)
{
Process p = new Process();
p = new Process();
p.StartInfo.FileName = "cmd.exe";
String arguments = "/c " + command;
p.StartInfo.Arguments = arguments;
p.StartInfo.UseShellExecute = false;
p.Start();
}
}
}