using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Net.Sockets; using System.Net; using SafeMobileLib; using System.Linq; namespace CPlus_GW { class WatcherServerThread { private TcpClient client; private IPEndPoint serverEndPoint; private Thread listenThread; private NetworkStream clientStream; private UdpMulticast udpMulticast; byte[] message; private string ctrIP; private int port; string gwID; string report; int totalUsers; private bool isRunning = false; public WatcherServerThread(string ctrlIP, int port, string p_gwID, string p_report) { try { SafeMobileLib.Utils.WriteLine("WatcherServerThread Constructor"); message = new byte[128]; ctrIP = ctrlIP; this.port = port; totalUsers = 0; gwID = p_gwID; report = p_report; client = new TcpClient(); serverEndPoint = new IPEndPoint(IPAddress.Parse(ctrIP), port); client.Connect(serverEndPoint); SafeMobileLib.Utils.WriteLine("TCP WatcherServerThread connected on " + port, ConsoleColor.Cyan); CPlusGW.TextQueue.PostItem("WatcherServerThread connected on " + port); clientStream = client.GetStream(); //set read timeout to 35 seconds, according to Motorola Connect Plus PN_Watcher specifications clientStream.ReadTimeout = 35000; //CPlusGW.status = true; } catch (Exception ex) { SM.Debug(ex.ToString()); } } public void Stop() { isRunning = false; if (clientStream != null) { clientStream.Close(); } if (listenThread.IsAlive) { listenThread.Abort(); } } public void Start() { isRunning = true; try { udpMulticast = new UdpMulticast(Program.cfg.multi_IP, Program.cfg.multi_port); //udpMulticast.OnNewDataRecv += new UdpMulticast.newData4Send(udpMulticast_OnNewDataRecv); SafeMobileLib.Utils.WriteLine("WatcherServerThread successfully registered to multicast group"); //udpMulticast.StartListen(); } catch (Exception ex) { SafeMobileLib.Utils.WriteLine("WatcherServerThread exception while joining the multicast group: " + ex.ToString(), ConsoleColor.Red); } listenThread = new Thread(new ThreadStart(HandleClientComm)); listenThread.IsBackground = true; listenThread.Start(); SafeMobileLib.Utils.WriteLine("============WatcherServerThread Listenning====================================="); } public void RestartTCP() { try { SafeMobileLib.Utils.WriteLine($"Reconnecting to controller ip:{ctrIP} port:{port}"); if (client != null) { if (client.Connected) client.Close(); } client = new TcpClient(); serverEndPoint = new IPEndPoint(IPAddress.Parse(ctrIP), port); client.Connect(serverEndPoint); SafeMobileLib.Utils.WriteLine("WatcherServerThread connected on " + port); clientStream = client.GetStream(); //set read timeout to 35 seconds, according to Motorola Connect Plus PN_Watcher specifications clientStream.ReadTimeout = 35000; //resend subscribe all Thread.Sleep(100); SubscribeALL(1234); } catch (Exception ex) { SafeMobileLib.Utils.WriteLine("Restart FAILED!!!! " + ex.Message); } } bool connDown = false; private void HandleClientComm() { while (isRunning) { try { //Console.WriteLine("Waiting for stuff..."); //read LEN first byte[] msgLen = new byte[2]; int result = clientStream.Read(msgLen, 0, msgLen.Length); int msgLength = msgLen[0] * 265 + msgLen[1]; message = new byte[msgLength]; result = clientStream.Read(message, 0, msgLength); //Console.Write(DateTime.Now + "-> "); //if we got PING send Pong if (message[0] == 0xff) { //Print(message, result, true, "PING"); SafeMobileLib.Utils.WriteLine("PING received"); CPlusGW.TextQueue.PostItem("PING received."); PONG(); } else { //Print(message, result, true); NotifyRespons resp = Decode(message, result); int reportingInterval = 60; Int32.TryParse(report, out reportingInterval); Vehicles veh = MainForm.vehicles.FirstOrDefault(d => d.Imei.Equals(resp.SUID)); if (veh == null) { SafeMobileLib.Utils.WriteLine($"Units {resp.SUID} not defined in the admin", ConsoleColor.White); Thread.Sleep(100); continue; } else if (!veh.Active) { SafeMobileLib.Utils.WriteLine($"Units {resp.SUID} not defined in the admin", ConsoleColor.White); Thread.Sleep(100); continue; } else reportingInterval = veh.GpsInterval; if (resp.Active) { totalUsers++; //SM.Debug("SU " + resp.SUID + " ON ### Total users: " + totalUsers); //CPlusGW.TextQueue.PostItem("Unit " + resp.SUID + " ON"); MotoTRBOcmdMsg msg = new MotoTRBOcmdMsg(); msg.m_cmd = (byte)MotoTRBOcmd.SET_REPORT_INTERVAL; msg.m_suid = resp.SUID; msg.m_payload = reportingInterval+""; CPlusGW.locationQueue.PostItem(msg); SendARSOnMultiCast(resp.SUID, true); //send sms reg msq SendSMSThread.Send_Reg_msg(resp.SUID + ".1"); } else { totalUsers++; SendARSOnMultiCast(resp.SUID, false); //SM.Debug("SU " + resp.SUID + " OFF ### Total users: " + totalUsers); //CPlusGW.TextQueue.PostItem("Unit " + resp.SUID + " OFF"); } String consoleMessage = "ARS " + (resp.Active ? "ON" : "OFF") + " for " + resp.SUID; SafeMobileLib.Utils.WriteLine("»»» " + consoleMessage, ConsoleColor.Green); CPlusGW.TextQueue.PostItem(consoleMessage); } } catch (ThreadAbortException tex) { SafeMobileLib.Utils.WriteLine("Watch server thread closed!!!", ConsoleColor.Red); } catch (Exception ex) { SafeMobileLib.Utils.WriteLine(ex.ToString(), ConsoleColor.Red); int count = 0; while(count < 300) { if (!isRunning) break; count++; Thread.Sleep(100); } connDown = true; } try { if (connDown) { RestartTCP(); connDown = false; Thread.Sleep(1000); } } catch (Exception ex) { Console.WriteLine(ex.ToString()); connDown = true; } Thread.Sleep(100); } } private void SendARSOnMultiCast(string SUID, bool onOff) { //send ARS ON string seqID = "0.0"; string test = "#130#" + SUID + "#" +((onOff)?"ON#":"OFF#"); String cmdok = "#" + seqID + 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 to messagebus udpMulticast.Send(buf, buf.Length); //SM.Debug("TX:" + cmdok); if (onOff)//send system location just on "ON" { //send Subscriber system location seqID = "0.0"; //build string string fullSource = Program.cfg.gatewayID.ToString() + "#1#" + SUID;//Main.GWID.ToString() + "#" + trboRemoteIP + "#" + suid; test = "#139#" + fullSource + "#"; cmdok = "#" + seqID + test; tmp = cmdok.Length + 1; tmp += tmp.ToString().Length; cmdok = "#" + tmp.ToString() + cmdok; enc = System.Text.Encoding.ASCII; byte[] buf2 = enc.GetBytes(cmdok); //send to messagebus udpMulticast.Send(buf2, buf2.Length); //SM.Debug("TX:" + cmdok); } } public delegate void MessageRecv(NotifyRespons resp); public event MessageRecv OnMessageRecv; private NotifyRespons Decode(byte[] data, int recv) { //Console.WriteLine("Got notify message"); NotifyRespons nResp = new NotifyRespons(); nResp.Active = false; nResp.DialogID = 0; nResp.Error = true; nResp.SUID = "0"; //int msgLength = data[0] * 265 + data[1]; //length doesent match... return error //if (msgLength != recv-2) { Console.WriteLine("Received length({0}) doesent match with message length({1})..returning errror!!!",recv-2,msgLength); return nResp; } //test if we got at least msgLen, msgID,dialogID,subscription state if (recv < 6) { SafeMobileLib.Utils.WriteLine("Incorect message.Message does not have the minimum size of 6bytes. actual length=" + recv); SafeMobileLib.Utils.WriteLine("!!!!!!!!!!!!subscription state TLV missing!!!!!!!!!!!"); return nResp; } //decode message ID int msgID = data[0]; if (msgID != 4) { SafeMobileLib.Utils.WriteLine($"Incorect msgID:{msgID}. MsgID must be 4(reliable notify message)"); return nResp; } //decode dialog ID int dialogID = data[1] * 256 + data[2]; nResp.DialogID = dialogID; //Console.WriteLine("DialogID:" + dialogID); //next TLV are optional for (int i = 3; i < data.Length; i++) { switch (data[i]) { case 3: //decode subscription state TLV byte tag = data[i]; byte len = data[i + 1]; byte value = data[i + 2]; if (tag == 0x03 && len == 0x01) { switch (value) { case (byte)SubscriptionState.Accepted: //Console.WriteLine("Subscription State:" + SubscriptionState.Accepted + " value:" + value); break; case (byte)SubscriptionState.RefreshAccepted: //Console.WriteLine("Subscription State:" + SubscriptionState.RefreshAccepted + " value:" + value); break; case (byte)SubscriptionState.Rejected: //Console.WriteLine("Subscription State:" + SubscriptionState.Rejected + " value:" + value); break; case (byte)SubscriptionState.Terminated: //Console.WriteLine("Subscription State:" + SubscriptionState.Terminated + " value:" + value); break; } } else { SafeMobileLib.Utils.WriteLine("Unknown subscription state:" + value); } //jump to next tlv i += 1 + len; break; //decode Expires TLV case 1: tag = data[i]; len = data[i + 1]; byte[] value_arr = new byte[len]; Array.Copy(data, i + 2, value_arr, 0, len); int expireTime = ConvertByteArr2Int(value_arr); //Console.WriteLine("Expire time:" + expireTime); //jump to next tlv i += 1 + len; break; //Decode Entity TLV case 10: tag = data[i]; len = data[i + 1]; value_arr = new byte[len]; Array.Copy(data, i + 2, value_arr, 0, len); string suid = Encoding.Unicode.GetString(value_arr); nResp.SUID = suid; //Console.WriteLine("Entity(SUID):" + suid); //jump to next tlv i += 1 + len; nResp.Error = false; break; //Decode Event TLV ... "1" - Present, "2" - Absent case 2: tag = data[i]; len = data[i + 1]; value_arr = new byte[len]; Array.Copy(data, i + 2, value_arr, 0, len); string event_string = Encoding.Unicode.GetString(value_arr); //nResp.Active = (event_string == "1"); //Console.WriteLine("Event TLV:" + event_string); //jump to next tlv i += 1 + len; break; case 14: tag = data[i]; len = data[i + 1]; value_arr = new byte[len]; Array.Copy(data, i + 2, value_arr, 0, len); string presence = Encoding.Unicode.GetString(value_arr); switch (presence) { case "0": //Console.WriteLine("Presence state TLV: unknown value:" + presence); break; case "1": nResp.Active = true; //Console.WriteLine("Presence state TLV: present value:" + presence); break; case "2": nResp.Active = false; //Console.WriteLine("Presence state TLV: absent value:" + presence); break; } //jump to next tlv i += 1 + len; break; } } return nResp; } public void Send(byte[] data, int len) { if (clientStream != null) { clientStream.Write(data, 0, len); clientStream.Flush(); } } #region Subscribe messages and PONG message /// /// Send the subcribe message to the controler /// /// unique dialog ID /// ID of radio subscriber that is being watched /// time to live of the subscription public void Subscribe(int dialogID, int ID, byte expiresTime) { byte[] tempID = convertInt2ByteLE(ID); byte[] msg = new byte[18 + tempID.Length]; //length msg[0] = 0x00; msg[1] = Convert.ToByte(0x10 + tempID.Length); // message ID msg[2] = 0x03; // Dialog ID byte[] tempDialogID = BitConverter.GetBytes(dialogID); msg[3] = tempDialogID[1]; msg[4] = tempDialogID[0]; //Message BODY //The Expires TLV Field msg[5] = 0x01; msg[6] = 0x01; msg[7] = expiresTime; //The TCPPort TLV Field msg[8] = 0x06; msg[9] = 0x02; byte[] tempPort = BitConverter.GetBytes(0x3344); msg[10] = tempPort[1]; msg[11] = tempPort[0]; //The Entities TLV Field msg[12] = 0x0a; msg[13] = Convert.ToByte(tempID.Length); // ID for (int i = 0; i < tempID.Length; i++) { msg[14 + i] = tempID[i]; } //The Events TLV Field msg[14 + tempID.Length] = 0x02; msg[14 + tempID.Length + 1] = 0x02; msg[14 + tempID.Length + 2] = 0x31; msg[14 + tempID.Length + 3] = 0x00; Send(msg, msg.Length); //Console.WriteLine("-----------Sent Subscribe to the controller-----"); //Print(msg, msg.Length, false); } public void SubscribeALL(int dialogID) { byte[] msg = new byte[17]; //length msg[0] = 0x00; msg[1] = 0x0f; // message ID msg[2] = 0x03; // Dialog ID byte[] tempDialogID = BitConverter.GetBytes(dialogID); msg[3] = tempDialogID[1]; msg[4] = tempDialogID[0]; //Message BODY //The Entities TLV Field msg[5] = 0x0a; msg[6] = 0x02; // ID:all msg[7] = 0x2a; msg[8] = 0x00; //The Events TLV Field msg[9] = 0x02; msg[10] = 0x06; msg[11] = (byte)'1'; msg[12] = 0x00; msg[13] = (byte)','; msg[14] = 0x00; msg[15] = (byte)'2'; msg[16] = 0x00; Send(msg, msg.Length); //Console.WriteLine("-----------Sent Subscribe ALL to the controller-----"); //Print(msg, msg.Length, false); } public void SubscribeALL() { byte[] msg = new byte[20]; //length msg[0] = 0x00; msg[1] = 0x12; // message ID msg[2] = 0x03; // Dialog ID msg[3] = 0x33; msg[4] = 0x44; //Message BODY //The Expires TLV Field msg[5] = 0x01; msg[6] = 0x01; msg[7] = 0xff; //The TCPPort TLV Field msg[8] = 0x06; msg[9] = 0x02; byte[] temp = BitConverter.GetBytes(0x3344); msg[10] = temp[1]; msg[11] = temp[0]; //The Entities TLV Field msg[12] = 0x0a; msg[13] = 0x02; // ID:all msg[14] = 0x2a; msg[15] = 0x00; //The Events TLV Field msg[16] = 0x02; msg[17] = 0x02; msg[18] = 0x2a; msg[19] = 0x00; Send(msg, msg.Length); SafeMobileLib.Utils.WriteLine("-----------Sent Subscribe to the controller-----"); //Print(msg, msg.Length, false); } //send PONG back to controller public void PONG() { byte[] msg = new byte[12]; //length msg[0] = 0x00; msg[1] = 0x0a; // message ID msg[2] = 0xff; // body length msg[3] = 0x08; //PONG -utf16 LE string //P msg[4] = 0x50; msg[5] = 0x00; //O msg[6] = 0x4f; msg[7] = 0x00; //N msg[8] = 0x4e; msg[9] = 0x00; //G msg[10] = 0x47; msg[11] = 0x00; Send(msg, msg.Length); //Console.WriteLine("-----------Sent PONG to the controller-----"); //Print(msg, msg.Length, false, "PONG:"); } #endregion #region AUX methods private void Print(byte[] data, int length, bool inOut) { //Console.Clear(); Console.WriteLine("--------------------------------------------------------------------------- " + length); Console.Write("Data (" + ((inOut) ? "RECEIVED" : "SENT") + "): "); for (int i = 0; i < length; i++) Console.Write(" 0x" + data[i].ToString("X2")); Console.WriteLine(""); Console.WriteLine("--------------------------------------------------------------------------- "); } private void Print(byte[] data, int length, bool inOut, string txt) { if (Program.cfg.ping) { //Console.Clear(); Console.WriteLine("--------------------------------------------------------------------------- " + length); Console.Write(txt + " (" + ((inOut) ? "RECEIVED" : "SENT") + "): "); for (int i = 0; i < length; i++) Console.Write(" 0x" + data[i].ToString("X2")); Console.WriteLine(""); Console.WriteLine("--------------------------------------------------------------------------- "); } } private static byte[] convertInt2ByteLE(int id) { Byte[] retBytes = Encoding.Unicode.GetBytes(id.ToString()); return retBytes; } private int ConvertByteArr2Int(byte[] arr) { byte[] worker = new byte[4]; arr.CopyTo(worker, 0); return BitConverter.ToInt32(worker, 0); } #endregion } public class NotifyRespons { private string suid; public string SUID { get { return suid; } set { suid = value; } } private bool active; public bool Active { get { return active; } set { active = value; } } private int dialogID; public int DialogID { get { return dialogID; } set { dialogID = value; } } private bool error; public bool Error { get { return error; } set { error = value; } } } public enum SubscriptionState { Rejected = 1, Accepted = 2, Terminated = 3, RefreshAccepted = 4 } }