From 7d38122ee0850abc8e29c73a3b02a26bc28991d3 Mon Sep 17 00:00:00 2001 From: Trevor Payne Date: Thu, 30 Mar 2023 16:31:50 -0500 Subject: [PATCH] feat: add check for cisco feedback registrations and logging if they are in error feat: log errors only, do not try to self-heal --- .../Endpoints/DGEs/DgeBaseController.cs | 259 ++++++++++++++++++ .../VideoCodec/CiscoCodec/CiscoSparkCodec.cs | 55 ++++ 2 files changed, 314 insertions(+) create mode 100644 essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/DgeBaseController.cs diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/DgeBaseController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/DgeBaseController.cs new file mode 100644 index 00000000..9510c4ec --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/DgeBaseController.cs @@ -0,0 +1,259 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.UI; + +using Crestron.SimplSharpPro.DM; + + +using Newtonsoft.Json; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.Config; +using Crestron.SimplSharpPro.DeviceSupport; +using PepperDash.Essentials.Core.DeviceInfo; + +namespace PepperDash.Essentials.DM.Endpoints.DGEs +{ + public class DgeBaseController : CrestronGenericBridgeableBaseDevice, IComPorts, IIROutputPorts, IHasBasicTriListWithSmartObject, ICec, IDeviceInfoProvider, IDgeRoutingWithFeedback + { + private const int CtpPort = 41795; + private readonly Dge100 _dge; + + public RoutingInputPort HdmiIn { get; protected set; } + public RoutingInputPort ProjectViewIn { get; protected set; } + public RoutingOutputPort HdmiOut { get; protected set; } + + protected readonly ushort DmInputJoin; + protected readonly ushort HdmiInputJoin; + protected readonly ushort ProjectViewJoin; + + private int SwitchValue; + + public RoutingPortCollection InputPorts + { + get; + private set; + } + + public RoutingPortCollection OutputPorts + { + get; + private set; + } + + private readonly TsxCcsUcCodec100EthernetReservedSigs _dgeEthernetInfo; + + public BasicTriListWithSmartObject Panel { get { return _dge; } } + + private DeviceConfig _dc; + + CrestronDgePropertiesConfig PropertiesConfig; + + public DgeBaseController(string key, string name, Dge100 device, DeviceConfig dc, CrestronDgePropertiesConfig props) + :base(key, name, device) + { + _dge = device; + _dc = dc; + + PropertiesConfig = props; + + DmInputJoin = (ushort)PropertiesConfig.DmInputJoin; + HdmiInputJoin = (ushort)PropertiesConfig.HdmiInputJoin; + ProjectViewJoin = (ushort)PropertiesConfig.ProjectViewVirtualInputJoin; + + // Set Ports for CEC + HdmiOut = new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + HdmiOut.Port = _dge.HdmiOut; + + + OutputPorts = new RoutingPortCollection { HdmiOut }; + + + _dgeEthernetInfo = _dge.ExtenderEthernetReservedSigs; + //_dgeEthernetInfo.DeviceExtenderSigChange += (extender, args) => UpdateDeviceInfo(); + _dgeEthernetInfo.Use(); + + DeviceInfo = new DeviceInfo(); + + AudioVideoSourceNumericFeedback = new IntFeedback(() => SwitchValue); + + + _dge.OnlineStatusChange += (currentDevice, args) => { if (args.DeviceOnLine) UpdateDeviceInfo(); }; + + _dge.SigChange += TrilistChange; + + } + + public virtual void TrilistChange(BasicTriList currentDevice, SigEventArgs args) + { + + } + + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + + } + + + #region IComPorts Members + + public CrestronCollection ComPorts + { + get { return _dge.ComPorts; } + } + + public int NumberOfComPorts + { + get { return _dge.NumberOfComPorts; } + } + + #endregion + + #region IIROutputPorts Members + + public CrestronCollection IROutputPorts + { + get { return _dge.IROutputPorts; } + } + + public int NumberOfIROutputPorts + { + get { return _dge.NumberOfIROutputPorts; } + } + + #endregion + + #region ICec Members + public Cec StreamCec { get { return _dge.HdmiOut.StreamCec; } } + #endregion + + #region Implementation of IDeviceInfoProvider + + public DeviceInfo DeviceInfo { get; private set; } + + public event DeviceInfoChangeHandler DeviceInfoChanged; + + public void UpdateDeviceInfo() + { + DeviceInfo.IpAddress = _dgeEthernetInfo.IpAddressFeedback.StringValue; + DeviceInfo.MacAddress = _dgeEthernetInfo.MacAddressFeedback.StringValue; + + GetFirmwareAndSerialInfo(); + + OnDeviceInfoChange(); + } + + private void GetFirmwareAndSerialInfo() + { + if (String.IsNullOrEmpty(_dgeEthernetInfo.IpAddressFeedback.StringValue)) + { + Debug.Console(1, this, "IP Address information not yet received. No device is online"); + return; + } + + var tcpClient = new GenericTcpIpClient("", _dgeEthernetInfo.IpAddressFeedback.StringValue, CtpPort, 1024) { AutoReconnect = false }; + + var gather = new CommunicationGather(tcpClient, "\r\n\r\n"); + + tcpClient.ConnectionChange += (sender, args) => + { + if (!args.Client.IsConnected) + { + return; + } + + args.Client.SendText("ver\r\n"); + }; + + gather.LineReceived += (sender, args) => + { + try + { + Debug.Console(1, this, "{0}", args.Text); + + if (args.Text.ToLower().Contains("host")) + { + DeviceInfo.HostName = args.Text.Split(':')[1].Trim(); + + Debug.Console(1, this, "hostname: {0}", DeviceInfo.HostName); + tcpClient.Disconnect(); + return; + } + + if (!args.Text.Contains('[')) + { + return; + } + var splitResponse = args.Text.Split('['); + + foreach (string t in splitResponse) + { + Debug.Console(1, this, "{0}", t); + } + + DeviceInfo.SerialNumber = splitResponse[1].Split(' ')[4].Replace("#", ""); + DeviceInfo.FirmwareVersion = splitResponse[1].Split(' ')[0]; + + Debug.Console(1, this, "Firmware: {0} SerialNumber: {1}", DeviceInfo.FirmwareVersion, + DeviceInfo.SerialNumber); + + tcpClient.SendText("host\r\n"); + } + catch (Exception ex) + { + Debug.Console(0, this, "Exception getting data: {0}", ex.Message); + Debug.Console(0, this, "response: {0}", args.Text); + } + }; + + tcpClient.Connect(); + } + + private void OnDeviceInfoChange() + { + var handler = DeviceInfoChanged; + + if (handler == null) return; + + handler(this, new DeviceInfoEventArgs(DeviceInfo)); + } + + #endregion + + + + + #region IDgeRouting Members + + public IntFeedback AudioVideoSourceNumericFeedback { get; set; } + + #endregion + + #region IRoutingNumeric Members + + public void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type) + { + throw new NotImplementedException(); + } + + #endregion + + #region IRouting Members + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + throw new NotImplementedException(); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs index 7dcf40bb..42e7bbd7 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs @@ -40,6 +40,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco private CTimer _brandingTimer; + private CTimer _registrationCheckTimer; + public CommunicationGather PortGather { get; private set; } public StatusMonitorBase CommunicationMonitor { get; private set; } @@ -262,6 +264,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco private StringBuilder _jsonMessage; + private StringBuilder _feedbackListMessage; + + private bool _feedbackListMessageIncoming; + private bool _jsonFeedbackMessageIsIncoming; public bool CommDebuggingIsOn; @@ -702,6 +708,8 @@ ConnectorID: {2}" // Fire the ready event SetIsReady(); + + _registrationCheckTimer = new CTimer(SendText, "xFeedback list", 360000, 360000); } public void SetCommDebug(string s) @@ -778,6 +786,30 @@ ConnectorID: {2}" return; } + if (!args.Text.StartsWith("/") && _feedbackListMessage != null) + { + _feedbackListMessageIncoming = false; + + var feedbackListString = _feedbackListMessage.ToString(); + _feedbackListMessage = null; + + ProcessFeedbackList(feedbackListString); + + } + + if (args.Text.StartsWith("/")) + { + Debug.Console(0, this, "Feedback List Data Received : {0}", args.Text); + _feedbackListMessageIncoming = true; + if(_feedbackListMessage == null) _feedbackListMessage = new StringBuilder(); + } + + if (_feedbackListMessageIncoming && _feedbackListMessage != null) + { + _feedbackListMessage.Append(args.Text); + return; + } + if (args.Text == "{" + Delimiter) // Check for the beginning of a new JSON message { _jsonFeedbackMessageIsIncoming = true; @@ -846,6 +878,19 @@ ConnectorID: {2}" } } + private void ProcessFeedbackList(string data) + { + Debug.Console(1, this, "Feedback List : "); + Debug.Console(1, this, data); + + if (data.Split('\n').Count() == _cliFeedbackRegistrationExpression.Split('\n').Count()) return; + Debug.Console(0, this, "Codec Feedback Registrations Lost - Registering Feedbacks"); + ErrorLog.Error(String.Format("[{0}] :: Codec Feedback Registrations Lost - Registering Feedbacks", Key)); + //var updateRegistrationString = "xFeedback deregisterall" + Delimiter + _cliFeedbackRegistrationExpression; + + //SendText(updateRegistrationString); + } + /// /// Enqueues a command to be sent to the codec. /// @@ -869,6 +914,16 @@ ConnectorID: {2}" Communication.SendText(command + Delimiter); } + public void SendText(object cmd) + { + var command = cmd as string; + if (String.IsNullOrEmpty(command)) return; + if (CommDebuggingIsOn) + Debug.Console(1, this, "Sending: '{0}'", ComTextHelper.GetDebugText(command + Delimiter)); + + Communication.SendText(command + Delimiter); + } + void DeserializeResponse(string response) { try