using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Net.Sockets; using System.Net; using System.Collections; using System.ComponentModel; using SafeMobileLib; using SafeMobileLib.MessageDecoders; namespace MotoRepeater { class WatcherServerThread { /* This field is used to define the type of the message. The size of this field is one byte * long. The data type for this field is unsigned integer. * Four messages are currently used by the DDMS: * * */ public enum MessageID { RELIABLE_NOTIFY = 4, SUBSCRIBE_V2 = 20, DELETE_MOBILITY = 21, MOBILITY_UPDATE = 22 }; public enum SubscriptionState { Rejected = 1, Accepted = 2, Terminated = 3, RefreshAccepted = 4 } public readonly int DialogID = 1313; private readonly int ReadTimeout = 1000 * 60 * 60 * 24; // 24 hours /* * The notification messages are sent over a negotiated TCP/IP connection when using the Confirmed Notify Interface */ private TcpClient client; private Thread listenThread; private Thread intervalThread; private NetworkStream clientStream; byte[] message; private string p_ctrIP; private int port; int totalUsers; public bool connDown = false; public bool prevConnDownState = true; public WatcherServerThread(string _ctrlIP, int port) { //Utils.ConsWrite(DebugMSG_Type.CTRL, "WatcherServerThread Constructor"); message = new byte[128]; p_ctrIP = _ctrlIP; this.port = port; totalUsers = 0; } public void Stop() { if (clientStream != null) { clientStream.Close(); } if (listenThread!= null && listenThread.IsAlive) { if (intervalThread.IsAlive) { intervalThread.Abort(); } else listenThread.Abort(); } } public void Start() { try { client = new TcpClient(); //serverEndPoint = new IPEndPoint("127.0.0.1", 3000); //client.Connect(serverEndPoint); client.Connect(p_ctrIP, port); Utils.WriteLine("••• Watcher Server Thread connected on " + p_ctrIP + ":" + port, ConsoleType.CTRL); clientStream = client.GetStream(); if (clientStream != null) { //set read timeout to 35 seconds, according to Motorola Connect Plus PN_Watcher specifications clientStream.ReadTimeout = ReadTimeout; connDown = false; // fire event only when the state had changed if (prevConnDownState != connDown) OnDdmsConnectionStatusChanged(true); // save new state prevConnDownState = connDown; } else { connDown = true; // fire event only when the state had changed if (prevConnDownState != connDown) OnDdmsConnectionStatusChanged(false); // save new state prevConnDownState = connDown; Utils.WriteLine("WatcherServerThread -> clientStream==null", ConsoleType.ALL); } } catch (Exception ex) { Utils.WriteLine("Error WatcherServerThread connecting to " + p_ctrIP + " \n", ConsoleType.ALL); connDown = true; // fire event only when the state had changed if (prevConnDownState != connDown) OnDdmsConnectionStatusChanged(false); // save new state prevConnDownState = connDown; } listenThread = new Thread(new ThreadStart(HandleClientComm)); listenThread.IsBackground = true; listenThread.Start(); intervalThread = new Thread(new ThreadStart(HandleInterval)); intervalThread.IsBackground = true; intervalThread.Start(); Utils.WriteLine("»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»««««««««««««««««««««««««««««««", ConsoleType.CTRL); Utils.WriteLine(String.Format("»»»»»»»»»»»»»»»»» Watcher Server Listening «««««««««««««««««"), ConsoleType.CTRL); Utils.WriteLine("»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»««««««««««««««««««««««««««««««", ConsoleType.CTRL); } public void RestartTCP() { try { client = new TcpClient(); //serverEndPoint = new IPEndPoint("127.0.0.1", 3000); //client.Connect(serverEndPoint); client.Connect(p_ctrIP, port); //Utils.WriteLine(ConsoleType.CTRL, "WatcherServerThread connected on " + p_ctrIP + ":" + port); Utils.WriteLine("♥♥♥ Watcher Server Thread connected on " + p_ctrIP + ":" + port); clientStream = client.GetStream(); if (clientStream != null) { //set read timeout to 35 seconds, according to Motorola Connect Plus PN_Watcher specifications clientStream.ReadTimeout = ReadTimeout; /* listenThread = new Thread(new ThreadStart(HandleClientComm)); listenThread.IsBackground = true; listenThread.Start(); */ //resend subscribe all Thread.Sleep(500); //SubscribeList(DialogID, new List() { "101" }); //SubscribeALL(1234); connDown = false; // fire event only when the state had changed if (prevConnDownState != connDown) OnDdmsConnectionStatusChanged(true); // save new state prevConnDownState = connDown; Utils.WriteLine("Restart finnished ip" + p_ctrIP + " port:" + port, ConsoleType.CTRL); } else { connDown = true; // fire event only when the state had changed if (prevConnDownState != connDown) OnDdmsConnectionStatusChanged(false); // save new state prevConnDownState = connDown; Utils.WriteLine("WatcherServerThread -> clientStream==null", ConsoleType.ALL); } } catch (Exception ex) { Utils.WriteLine("••• Connection with DDMS was not established!", ConsoleType.ALL); } } //IF THE UNIT IS NOT IN ht_SUInfo dont process the message private void HandleClientComm() { int count = 0; while (MotoRepeater_GW.isRunning) { try { if (clientStream != null) { clientStream.ReadTimeout = ReadTimeout; //Utils.WriteLine("Watcher Server waiting..."); //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); //Print(message, result, true); NotifyRespons resp = Decode(message, result); if (resp.Error) { Utils.WriteLine("»»» Error response from " + resp.SUID, ConsoleType.ALL); Utils.WriteLine(Utils.PrintBytesArray(message), ConsoleColor.Yellow); //continue; } else { if (resp.DialogID != 1234) { Utils.WriteLine("»»» One time resp from " + resp.SUID, ConsoleType.ALL); resp.PollResponse = true; } try { OnARSReceived(Int64.Parse(resp.SUID), resp.Active); } catch (Exception) { } } } } catch (System.IO.IOException ex) { //Utils.ConsWrite(DebugMSG_Type.always, "I/O exception!!"); if (MotoRepeater_GW.isRunning) Utils.WriteLine("φφφ Subscribe Exception: " + ex.ToString(), ConsoleType.ALL); Thread.Sleep(500); connDown = true; // fire event only when the state had changed if (prevConnDownState != connDown) OnDdmsConnectionStatusChanged(false); // save new state prevConnDownState = connDown; } catch (Exception ex) { if(MotoRepeater_GW.isRunning) Utils.WriteLine("φφφ Subscribe Exception: " + ex.ToString(), ConsoleType.ALL); Thread.Sleep(500); connDown = true; // fire event only when the state had changed if (prevConnDownState != connDown) OnDdmsConnectionStatusChanged(false); // save new state prevConnDownState = connDown; } while (MotoRepeater_GW.isRunning && connDown == true) { //Utils.WriteLine(ConsoleType.ALL, "HandleClientComm -> connDown==true Restarting TCP"); if (++count % 20 == 0) { RestartTCP(); count = 1; } Thread.Sleep(500); } //Thread.Sleep(100); } } private void HandleInterval() { int count = 0; while (MotoRepeater_GW.isRunning) { string suid = ""; int dialogID = -1; try { //test if we have a change in reporting interval if (++count % 3 == 0) { SUinfo info = SN_Queues.NewIntervalQueue.GetItem(100); if (info != null && info.ARSon) { suid = info.suid; Utils.WriteLine("Reporting interval changed for " + info.suid + " [" + info.repInterval + " sec]", ConsoleColor.Cyan); //dialogID = 0x1111; //SubscribeOneTime(dialogID, info.suid); if(LocationThread.SendTriggeredLocationRequestWithLOC(info.suid, info.repInterval)) if (OnTriggeredLocationRequest != null) OnTriggeredLocationRequest(info.suid, info.repInterval); } count = 0; } Thread.Sleep(500); } catch (Exception ex) { Utils.WriteLine("Error HandleInterval for suid:" + suid + " dialogID:" + dialogID, ConsoleType.ALL); } } } 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) { Utils.WriteLine("Incorect message.Message does not have the minimum size of 6bytes. actual length=" + recv, ConsoleType.ALL); Utils.WriteLine("!!!!!!!!!!!!subscription state TLV missing!!!!!!!!!!!", ConsoleType.ALL); client.Close(); connDown = true; // fire event only when the state had changed if (prevConnDownState != connDown) OnDdmsConnectionStatusChanged(false); // save new state prevConnDownState = connDown; return nResp; } //decode message ID int msgID = data[0]; if (msgID != (int)MessageID.RELIABLE_NOTIFY) { Utils.WriteLine(String.Format("Incorect msgID:{0}. MsgID must be 4(reliable notify message)", msgID), ConsoleType.ALL); 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: //Utils.WriteLine("Subscription State: " + SubscriptionState.Accepted); break; case (byte)SubscriptionState.RefreshAccepted: //Utils.WriteLine("Subscription State: " + SubscriptionState.RefreshAccepted); break; case (byte)SubscriptionState.Rejected: Utils.WriteLine("!!! MNIS Subscription State: " + SubscriptionState.Rejected); break; case (byte)SubscriptionState.Terminated: //Utils.WriteLine("Subscription State: " + SubscriptionState.Terminated); break; } } else { Utils.WriteLine("••• Unknown subscription state:" + value, ConsoleType.CTRL); } //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); //Utils.WriteLine("TLV Expire " + 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; //Utils.WriteLine("TLV Entity " + 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"); //Utils.WriteLine("TLV Event " + 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) { //Utils.WriteLine(ConsoleType.ALL, "DialogID:" + dialogID); 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[23]; //length msg[0] = 0x15; msg[1] = 0x00; // message ID msg[2] = 20; // Dialog ID byte[] tempDialogID = BitConverter.GetBytes(dialogID); msg[3] = tempDialogID[1]; msg[4] = tempDialogID[0]; //Message BODY //The Expires TLV Field (2^32 secs = ~ 136 years) msg[5] = 0x01; msg[6] = 0x04; msg[7] = 0xff; msg[8] = 0xff; msg[9] = 0xff; msg[10] = 0xff; //The Entities TLV Field msg[11] = 0x0a; msg[12] = 0x02; // ID:all msg[13] = 0x2a; msg[14] = 0x00; //The Events TLV Field msg[15] = 0x02; msg[16] = 0x06; msg[17] = (byte)'1'; msg[18] = 0x00; msg[19] = (byte)','; msg[20] = 0x00; msg[21] = (byte)'2'; msg[22] = 0x00; Send(msg, msg.Length); Utils.WriteLine("««« Subscribe ALL Message to the controller"); //Print(msg, msg.Length, false); } public void SubscribeOneTime(int dialogID, string suid)// TLV expire =0 { //Utils.ConsWrite(DebugMSG_Type.DEV, "DialogID:" + dialogID); char[] suidARR = suid.ToCharArray(); byte[] msg = new byte[18 + suidARR.Length*2]; //length msg[0] = 0x00; msg[1] = (byte)(16 + suidARR.Length * 2); // 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] = 0x00;//one time //The Entities TLV Field msg[8] = 0x0a; msg[9] = (byte)(suidARR.Length*2); for (int i = 0; i < suidARR.Length; i++) { byte b = (byte)suidARR[i]; //Console.WriteLine(b); msg[10 + i * 2] = b; msg[10 + i * 2 + 1] = 0x00; } //The Events TLV Field msg[10 + suidARR.Length * 2] = 0x02; msg[10 + suidARR.Length * 2 + 1] = 0x06; msg[10 + suidARR.Length * 2 + 2] = (byte)'1'; msg[10 + suidARR.Length * 2 + 3] = 0x00; msg[10 + suidARR.Length * 2 + 4] = (byte)','; msg[10 + suidARR.Length * 2 + 5] = 0x00; msg[10 + suidARR.Length * 2 + 6] = (byte)'2'; msg[10 + suidARR.Length * 2 + 7] = 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[17]; //length msg[0] = 0x0f; msg[1] = 0x00; // message ID msg[2] = 20; // Dialog ID msg[3] = 0x33; msg[4] = 0x44; //Message BODY //The Expires TLV Field msg[5] = 0x01; msg[6] = 0x02; msg[7] = 0xff; msg[8] = 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[9] = 0x0a; msg[10] = 0x02; // ID:all msg[11] = 0x2a; msg[12] = 0x00; //The Events TLV Field msg[13] = 0x02; msg[14] = 0x02; msg[15] = 0x2a; msg[16] = 0x00; Send(msg, msg.Length); Utils.WriteLine("««« Subscribe ALL Message to the controller"); //Utils.ConsWrite(DebugMSG_Type.CTRL, "-----------Sent Subscribe to the controller-----"); Print(msg, msg.Length, false); } public void SubscribeList(int dialogID, List suids)// TLV expire =0 { //Utils.ConsWrite(DebugMSG_Type.DEV, "DialogID:" + dialogID); //create arr string: suid1,suid2,....,suidn string strSUIDS=""; for (int i = 0; i < suids.Count; i++) { if (i != (suids.Count - 1)) { strSUIDS += suids[i] + ","; } else { strSUIDS += suids[i]; } } //Utils.WriteLine(ConsoleType.ARS, "suids string:" + strSUIDS); char[] suidARR = strSUIDS.ToCharArray(); byte[] msg = new byte[15 + suidARR.Length * 2]; //length msg[0] = 0x00; msg[1] = (byte)(13 + suidARR.Length * 2); // 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] = 0x00;//one time //The Entities TLV Field msg[5] = 0x0a; msg[6] = (byte)(suidARR.Length * 2); for (int i = 0; i < suidARR.Length; i++) { byte b = (byte)suidARR[i]; //Console.WriteLine(b); msg[7 + i * 2] = b; msg[7 + i * 2 + 1] = 0x00; } //The Events TLV Field msg[7 + suidARR.Length * 2] = 0x02; msg[7 + suidARR.Length * 2 + 1] = 0x06; msg[7 + suidARR.Length * 2 + 2] = (byte)'1'; msg[7 + suidARR.Length * 2 + 3] = 0x00; msg[7 + suidARR.Length * 2 + 4] = (byte)','; msg[7 + suidARR.Length * 2 + 5] = 0x00; msg[7 + suidARR.Length * 2 + 6] = (byte)'2'; msg[7 + suidARR.Length * 2 + 7] = 0x00; Send(msg, msg.Length); //Console.WriteLine("-----------Sent Subscribe ALL 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); Utils.WriteLine("««« PONG "); //Utils.WriteLine(ConsoleType.ALL, "-----------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 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 delegate void ARSReceived(Int64 radioID, bool isON); public event ARSReceived OnARSReceived; public delegate void ConnectionStatusDEl(bool isUP); public event ConnectionStatusDEl OnDdmsConnectionStatusChanged; public delegate void TriggeredLocationRequestDEl(String radioID, int reportingInterval); public event TriggeredLocationRequestDEl OnTriggeredLocationRequest; } 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; } } private bool pollResponse; public bool PollResponse { get { return pollResponse; } set { pollResponse = value; } } } }