From 4af5eede4085f01935a113ddbc4646933d2e8d48 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 24 Aug 2021 10:28:46 -0600 Subject: [PATCH] feat(PepperDashCore): #114 Adds QscCoreDoubleTcpIpClient --- .../Comm/QscCoreDoubleTcpIpClient.cs | 312 ++++++++++++++++++ .../Pepperdash Core/PepperDash_Core.csproj | 1 + 2 files changed, 313 insertions(+) create mode 100644 Pepperdash Core/Pepperdash Core/Comm/QscCoreDoubleTcpIpClient.cs diff --git a/Pepperdash Core/Pepperdash Core/Comm/QscCoreDoubleTcpIpClient.cs b/Pepperdash Core/Pepperdash Core/Comm/QscCoreDoubleTcpIpClient.cs new file mode 100644 index 0000000..2f61dc8 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/Comm/QscCoreDoubleTcpIpClient.cs @@ -0,0 +1,312 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Core +{ + public class QscCoreDoubleTcpIpClient : IKeyed + { + public string Key { get; private set; } + + public event EventHandler BoolChange; + public event EventHandler UshortChange; + public event EventHandler StringChange; + + public GenericTcpIpClient MasterClient { get; private set; } + public GenericTcpIpClient SlaveClient { get; private set; } + + string Username; + string Password; + string LineEnding; + + CommunicationGather MasterGather; + CommunicationGather SlaveGather; + + bool IsPolling; + int PollingIntervalSeconds; + CTimer PollTimer; + + bool SlaveIsActive; + + /// + /// Default constuctor for S+ + /// + public QscCoreDoubleTcpIpClient() + { + MasterClient = new GenericTcpIpClient("temp-master"); + MasterClient.AutoReconnect = true; + MasterClient.AutoReconnectIntervalMs = 2000; + SlaveClient = new GenericTcpIpClient("temp-slave"); + SlaveClient.AutoReconnect = true; + SlaveClient.AutoReconnectIntervalMs = 2000; + + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public void Connect(string key, string masterAddress, int masterPort, + string slaveAddress, int slavePort, string username, string password, + int pollingIntervalSeconds, string lineEnding) + { + Key = key; + + PollingIntervalSeconds = pollingIntervalSeconds; + Username = username; + Password = password; + LineEnding = lineEnding; + + MasterClient.Initialize(key + "-master"); + SlaveClient.Initialize(key + "-slave"); + + MasterClient.Hostname = masterAddress; + MasterClient.Port = masterPort; + + if (MasterClient != null) + { + MasterClient.Disconnect(); + } + + if (SlaveClient != null) + { + SlaveClient.Disconnect(); + } + + if (MasterGather == null) + { + MasterGather = new CommunicationGather(MasterClient, lineEnding); + MasterGather.IncludeDelimiter = true; + } + + MasterGather.LineReceived -= MasterGather_LineReceived; + MasterGather.LineReceived += new EventHandler(MasterGather_LineReceived); + + MasterClient.ConnectionChange -= MasterClient_SocketStatusChange; + MasterClient.ConnectionChange += MasterClient_SocketStatusChange; + + SlaveClient.Hostname = slaveAddress; + SlaveClient.Port = slavePort; + + if (SlaveGather == null) + { + SlaveGather = new CommunicationGather(SlaveClient, lineEnding); + SlaveGather.IncludeDelimiter = true; + } + + SlaveGather.LineReceived -= MasterGather_LineReceived; + SlaveGather.LineReceived += new EventHandler(SlaveGather_LineReceived); + + SlaveClient.ConnectionChange -= SlaveClient_SocketStatusChange; + SlaveClient.ConnectionChange += SlaveClient_SocketStatusChange; + + MasterClient.Connect(); + SlaveClient.Connect(); + } + + /// + /// + /// + public void Disconnect() + { + if (MasterClient != null) + { + MasterGather.LineReceived -= MasterGather_LineReceived; + MasterClient.Disconnect(); + } + if (SlaveClient != null) + { + SlaveGather.LineReceived -= SlaveGather_LineReceived; + SlaveClient.Disconnect(); + } + if (PollTimer != null) + { + IsPolling = false; + + PollTimer.Stop(); + PollTimer = null; + } + } + + /// + /// Does not include line feed + /// + public void SendText(string s) + { + if (SlaveIsActive) + { + if (SlaveClient != null) + { + Debug.Console(2, this, "Sending to Slave: {0}", s); + SlaveClient.SendText(s); + } + } + else + { + if (MasterClient != null) + { + Debug.Console(2, this, "Sending to Master: {0}", s); + MasterClient.SendText(s); + } + } + } + + void MasterClient_SocketStatusChange(object sender, GenericSocketStatusChageEventArgs args) + { + OnUshortChange((ushort)args.Client.ClientStatus, MasterClientStatusId); + + if (args.Client.IsConnected) + { + MasterGather.LineReceived += MasterGather_LineReceived; + + StartPolling(); + } + else + MasterGather.LineReceived -= MasterGather_LineReceived; + } + + void SlaveClient_SocketStatusChange(object sender, GenericSocketStatusChageEventArgs args) + { + OnUshortChange((ushort)args.Client.ClientStatus, SlaveClientStatusId); + + if (args.Client.IsConnected) + { + SlaveGather.LineReceived += SlaveGather_LineReceived; + + StartPolling(); + } + else + SlaveGather.LineReceived -= SlaveGather_LineReceived; + + } + + + void MasterGather_LineReceived(object sender, GenericCommMethodReceiveTextArgs e) + { + if (e.Text.Contains("login_required")) + { + MasterClient.SendText(string.Format("login {0} {1} \x0d\x0a", Username, Password)); + } + else if (e.Text.Contains("login_success")) + { + // START THE POLLING, YO! + } + else if (e.Text.StartsWith("sr")) + { + // example response "sr "MyDesign" "NIEC2bxnVZ6a" 1 1" + + var split = e.Text.Trim().Split(' '); + if (split[split.Length - 1] == "1") + { + SlaveIsActive = false; + OnBoolChange(false, SlaveIsActiveId); + OnBoolChange(true, MasterIsActiveId); + } + } + if (!SlaveIsActive) + OnStringChange(e.Text, LineReceivedId); + } + + void SlaveGather_LineReceived(object sender, GenericCommMethodReceiveTextArgs e) + { + if (e.Text.Contains("login_required")) + { + SlaveClient.SendText(string.Format("login {0} {1} \x0d\x0a", Username, Password)); + } + else if (e.Text.Contains("login_success")) + { + // START THE POLLING, YO! + } + else if (e.Text.StartsWith("sr")) + { + var split = e.Text.Trim().Split(' '); + if (split[split.Length - 1] == "1") + { + SlaveIsActive = true; + OnBoolChange(true, SlaveIsActiveId); + OnBoolChange(false, MasterIsActiveId); + } + } + if (SlaveIsActive) + OnStringChange(e.Text, LineReceivedId); + } + + void StartPolling() + { + if (!IsPolling) + { + IsPolling = true; + + Poll(); + if (PollTimer != null) + PollTimer.Stop(); + + PollTimer = new CTimer(o => Poll(), null, PollingIntervalSeconds * 1000, PollingIntervalSeconds * 1000); + } + } + + void Poll() + { + if (MasterClient != null && MasterClient.IsConnected) + { + Debug.Console(2, this, "Polling Master."); + MasterClient.SendText("sg\x0d\x0a"); + + } + if (SlaveClient != null && SlaveClient.IsConnected) + { + Debug.Console(2, this, "Polling Slave."); + SlaveClient.SendText("sg\x0d\x0a"); + } + } + + + + // login NAME PIN ---> login_success, login_failed + + // status get + // sg --> sr DESIGN_NAME DESIGN_ID IS_PRIMARY IS_ACTIVE + + // CRLF + + void OnBoolChange(bool state, ushort type) + { + var handler = BoolChange; + if (handler != null) + handler(this, new BoolChangeEventArgs(state, type)); + } + + void OnUshortChange(ushort state, ushort type) + { + var handler = UshortChange; + if (handler != null) + handler(this, new UshrtChangeEventArgs(state, type)); + } + + void OnStringChange(string value, ushort type) + { + var handler = StringChange; + if (handler != null) + handler(this, new StringChangeEventArgs(value, type)); + } + + + public const ushort MasterIsActiveId = 3; + public const ushort SlaveIsActiveId = 4; + public const ushort MainModuleInitiailzeId = 5; + + public const ushort MasterClientStatusId = 101; + public const ushort SlaveClientStatusId = 102; + + public const ushort LineReceivedId = 201; + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj b/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj index f746202..aa8d2c2 100644 --- a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj +++ b/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj @@ -83,6 +83,7 @@ +