using System; using System.Collections.Generic; using System.Linq; using System.Text; using Crestron.SimplSharp; using Crestron.SimplSharpPro; using Crestron.SimplSharpPro.DeviceSupport; using System.ComponentModel; using PepperDash.Core; using Serilog.Events; namespace PepperDash.Essentials.Core { /// /// Used for monitoring comms that are IBasicCommunication. Will send a poll string and provide an event when /// statuses change. /// Default monitoring uses TextReceived event on Client. /// public class GenericCommunicationMonitor : StatusMonitorBase { public IBasicCommunication Client { get; private set; } /// /// Will monitor Client.BytesReceived if set to true. Otherwise the default is to monitor Client.TextReceived /// public bool MonitorBytesReceived { get; private set; } /// /// Return true if the Client is ISocketStatus /// public bool IsSocket { get { return Client is ISocketStatus; } } long PollTime; CTimer PollTimer; string PollString; Action PollAction; /// /// /// /// /// in MS, >= 5000 /// in MS, >= 5000 /// in MS, >= 5000 /// String to send to comm public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, long pollTime, long warningTime, long errorTime, string pollString) : base(parent, warningTime, errorTime) { if (pollTime > warningTime || pollTime > errorTime) throw new ArgumentException("pollTime must be less than warning or errorTime"); //if (pollTime < 5000) // throw new ArgumentException("pollTime cannot be less than 5000 ms"); Client = client; PollTime = pollTime; PollString = pollString; if (IsSocket) { (Client as ISocketStatus).ConnectionChange += new EventHandler(socket_ConnectionChange); } } public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, long pollTime, long warningTime, long errorTime, string pollString, bool monitorBytesReceived) : this(parent, client, pollTime, warningTime, errorTime, pollString) { SetMonitorBytesReceived(monitorBytesReceived); } /// /// Poll is a provided action instead of string /// /// /// /// /// /// /// public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, long pollTime, long warningTime, long errorTime, Action pollAction) : base(parent, warningTime, errorTime) { if (pollTime > warningTime || pollTime > errorTime) throw new ArgumentException("pollTime must be less than warning or errorTime"); //if (pollTime < 5000) // throw new ArgumentException("pollTime cannot be less than 5000 ms"); Client = client; PollTime = pollTime; PollAction = pollAction; if (IsSocket) { (Client as ISocketStatus).ConnectionChange += new EventHandler(socket_ConnectionChange); } } public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, long pollTime, long warningTime, long errorTime, Action pollAction, bool monitorBytesReceived) : this(parent, client, pollTime, warningTime, errorTime, pollAction) { SetMonitorBytesReceived(monitorBytesReceived); } /// /// Build the monitor from a config object /// public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, CommunicationMonitorConfig props) : this(parent, client, props.PollInterval, props.TimeToWarning, props.TimeToError, props.PollString) { if (IsSocket) { (Client as ISocketStatus).ConnectionChange += new EventHandler(socket_ConnectionChange); } } /// /// Builds the monitor from a config object and takes a bool to specify whether to monitor BytesReceived /// Default is to monitor TextReceived /// /// /// /// /// public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, CommunicationMonitorConfig props, bool monitorBytesReceived) : this(parent, client, props.PollInterval, props.TimeToWarning, props.TimeToError, props.PollString) { SetMonitorBytesReceived(monitorBytesReceived); } void SetMonitorBytesReceived(bool monitorBytesReceived) { MonitorBytesReceived = monitorBytesReceived; } public override void Start() { if (MonitorBytesReceived) { Client.BytesReceived += Client_BytesReceived; } else { Client.TextReceived += Client_TextReceived; } if (!IsSocket) { BeginPolling(); } } void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) { if (!e.Client.IsConnected) { // Immediately stop polling and notify that device is offline Stop(); Status = MonitorStatus.InError; ResetErrorTimers(); } else { // Start polling and set status to unknow and let poll result update the status to IsOk when a response is received Status = MonitorStatus.StatusUnknown; Start(); BeginPolling(); } } void BeginPolling() { Poll(); PollTimer = new CTimer(o => Poll(), null, PollTime, PollTime); } public override void Stop() { if(MonitorBytesReceived) { Client.BytesReceived -= this.Client_BytesReceived; } else { Client.TextReceived -= Client_TextReceived; } if (PollTimer != null) { PollTimer.Stop(); PollTimer = null; StopErrorTimers(); } } void Client_TextReceived(object sender, GenericCommMethodReceiveTextArgs e) { DataReceived(); } /// /// Upon any receipt of data, set everything to ok! /// /// /// void Client_BytesReceived(object sender, GenericCommMethodReceiveBytesArgs e) { DataReceived(); } void DataReceived() { Status = MonitorStatus.IsOk; ResetErrorTimers(); } void Poll() { StartErrorTimers(); if (Client.IsConnected) { //Debug.LogMessage(LogEventLevel.Verbose, this, "Polling"); if(PollAction != null) PollAction.Invoke(); else Client.SendText(PollString); } else { Debug.LogMessage(LogEventLevel.Verbose, this, "Comm not connected"); } } } public class CommunicationMonitorConfig { public int PollInterval { get; set; } public int TimeToWarning { get; set; } public int TimeToError { get; set; } public string PollString { get; set; } public CommunicationMonitorConfig() { PollInterval = 30000; TimeToWarning = 120000; TimeToError = 300000; PollString = ""; } } }