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 acknowledgedMessagesSeqNo = new List(); /* Send sms thread worker and queue */ private Thread SendSMSThreadobj; private static InterthreadMessageQueue sendSMSQueue = new InterthreadMessageQueue(); 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()); } } /// /// Decode a message received from the message bus /// /// 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("") == 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(); } } }