From d72792500472174810d88cd034f73504cd0f82ea Mon Sep 17 00:00:00 2001 From: jkdevito Date: Fri, 16 Jan 2026 11:33:35 -0600 Subject: [PATCH 01/50] feat: add HdMdNxM4kZEController class and update project file metadata --- src/Chassis/HdMdNxM4KzEController.cs | 510 +++++++++++++++++++++++++++ src/PepperDash.Essentials.DM.csproj | 12 +- 2 files changed, 516 insertions(+), 6 deletions(-) create mode 100644 src/Chassis/HdMdNxM4KzEController.cs diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs new file mode 100644 index 0000000..3f8ceab --- /dev/null +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -0,0 +1,510 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.DM; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.DM.Config; +using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.Config; + +namespace PepperDash.Essentials.DM.Chassis +{ + [Description("Wrapper class for all HdMdNxM4ZE switchers")] + public class HdMdNxM4kZEController : CrestronGenericBridgeableBaseDevice, IRoutingNumericWithFeedback, IHasFeedback + { + private HdMdNxM4kzE _Chassis; + + //private HdMd4x14kzE _Chassis4x1; + + //IroutingNumericEvent + public event EventHandler NumericSwitchChange; + + public Dictionary InputNames { get; set; } + public Dictionary OutputNames { get; set; } + + public RoutingPortCollection InputPorts { get; private set; } + public RoutingPortCollection OutputPorts { get; private set; } + + public FeedbackCollection VideoInputSyncFeedbacks { get; private set; } + public FeedbackCollection VideoOutputRouteFeedbacks { get; private set; } + public FeedbackCollection InputNameFeedbacks { get; private set; } + public FeedbackCollection OutputNameFeedbacks { get; private set; } + public FeedbackCollection OutputRouteNameFeedbacks { get; private set; } + public FeedbackCollection InputHdcpEnableFeedback { get; private set; } + public StringFeedback DeviceNameFeedback { get; private set; } + public BoolFeedback AutoRouteFeedback { get; private set; } + + #region Constructor + + public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, + HdMdNxM4kEBridgeablePropertiesConfig props) + : base(key, name, chassis) + { + _Chassis = chassis; + Name = name; + + if (props == null) + { + Debug.LogDebug(this, "HdMdNxM4kZEController properties are null, failed to build the device"); + return; + } + + + if (props.Inputs != null) + { + foreach (var kvp in props.Inputs) + { + Debug.LogDebug(this, "props.Inputs: {0}-{1}", kvp.Key, kvp.Value); + } + InputNames = props.Inputs; + } + if (props.Outputs != null) + { + foreach (var kvp in props.Outputs) + { + Debug.LogDebug(this, "props.Outputs: {0}-{1}", kvp.Key, kvp.Value); + } + OutputNames = props.Outputs; + } + + DeviceNameFeedback = new StringFeedback("DeviceName", ()=>Name); + + VideoInputSyncFeedbacks = new FeedbackCollection(); + VideoOutputRouteFeedbacks = new FeedbackCollection(); + InputNameFeedbacks = new FeedbackCollection(); + OutputNameFeedbacks = new FeedbackCollection(); + OutputRouteNameFeedbacks = new FeedbackCollection(); + InputHdcpEnableFeedback = new FeedbackCollection(); + + InputPorts = new RoutingPortCollection(); + OutputPorts = new RoutingPortCollection(); + + if (_Chassis is HdMd4x14kzE _chassis) + { + AutoRouteFeedback = new BoolFeedback(() => _chassis.AutoRouteOnFeedback.BoolValue); + } + + for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) + { + var index = i; + var inputName = InputNames[index]; + _Chassis.Inputs[index].Name.StringValue = inputName; + _Chassis.HdmiInputs[index].Name.StringValue = inputName; + + InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, _Chassis.HdmiInputs[index], this) + { + FeedbackMatchObject = _Chassis.HdmiInputs[index] + }); + + VideoInputSyncFeedbacks.Add(new BoolFeedback(inputName, () => _Chassis.Inputs[index].VideoDetectedFeedback.BoolValue)); + InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].NameFeedback.StringValue)); + InputNameFeedbacks.Add(new StringFeedback(inputName, () => InputNames[index])); + InputHdcpEnableFeedback.Add(new BoolFeedback(inputName, () => _Chassis.HdmiInputs[index].HdmiInputPort.HdcpSupportOnFeedback.BoolValue)); + } + + for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) + { + var index = i; + var outputName = OutputNames[index]; + //_Chassis.Outputs[index].Name.StringValue = outputName; + //_Chassis.HdmiOutputs[index].Name.StringValue = outputName; + + OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, _Chassis.HdmiOutputs[index], this) + { + FeedbackMatchObject = _Chassis.HdmiOutputs[index] + }); + VideoOutputRouteFeedbacks.Add(new IntFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback == null ? 0 : (int)_Chassis.Outputs[index].VideoOutFeedback.Number)); + OutputNameFeedbacks.Add(new StringFeedback(outputName, () => OutputNames[index])); + OutputRouteNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback.NameFeedback.StringValue)); + } + + _Chassis.DMInputChange += Chassis_DMInputChange; + _Chassis.DMOutputChange += Chassis_DMOutputChange; + + AddPostActivationAction(AddFeedbackCollections); + } + + #endregion + + #region Methods + + /// + /// Raise an event when the status of a switch object changes. + /// + /// Arguments defined as IKeyName sender, output, input, and eRoutingSignalType + private void OnSwitchChange(RoutingNumericEventArgs e) + { + var newEvent = NumericSwitchChange; + if (newEvent != null) newEvent(this, e); + } + + public void EnableHdcp(uint port) + { + if (port > _Chassis.NumberOfInputs) return; + if (port <= 0) return; + + _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOn(); + InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); + } + + public void DisableHdcp(uint port) + { + if (port > _Chassis.NumberOfInputs) return; + if (port <= 0) return; + + _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOff(); + InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); + } + + public void EnableAutoRoute() + { + if (_Chassis.NumberOfOutputs > 1) return; + + if (!(_Chassis is HdMd4x14kzE _chassis)) + { + return; + } + + _chassis.AutoRouteOn(); + } + + public void DisableAutoRoute() + { + if (_Chassis.NumberOfOutputs > 1) return; + + if (!(_Chassis is HdMd4x14kzE _chassis)) + { + return; + } + + _chassis.AutoRouteOff(); + } + + #region PostActivate + + public void AddFeedbackCollections() + { + AddFeedbackToList(DeviceNameFeedback); + AddCollectionsToList(VideoInputSyncFeedbacks, InputHdcpEnableFeedback); + AddCollectionsToList(VideoOutputRouteFeedbacks); + AddCollectionsToList(InputNameFeedbacks, OutputNameFeedbacks, OutputRouteNameFeedbacks); + } + + #endregion + + #region FeedbackCollection Methods + + //Add arrays of collections + public void AddCollectionsToList(params FeedbackCollection[] newFbs) + { + foreach (FeedbackCollection fbCollection in newFbs) + { + foreach (var item in newFbs) + { + AddCollectionToList(item); + } + } + } + public void AddCollectionsToList(params FeedbackCollection[] newFbs) + { + foreach (FeedbackCollection fbCollection in newFbs) + { + foreach (var item in newFbs) + { + AddCollectionToList(item); + } + } + } + + public void AddCollectionsToList(params FeedbackCollection[] newFbs) + { + foreach (FeedbackCollection fbCollection in newFbs) + { + foreach (var item in newFbs) + { + AddCollectionToList(item); + } + } + } + + //Add Collections + public void AddCollectionToList(FeedbackCollection newFbs) + { + foreach (var f in newFbs) + { + if (f == null) continue; + + AddFeedbackToList(f); + } + } + + public void AddCollectionToList(FeedbackCollection newFbs) + { + foreach (var f in newFbs) + { + if (f == null) continue; + + AddFeedbackToList(f); + } + } + + public void AddCollectionToList(FeedbackCollection newFbs) + { + foreach (var f in newFbs) + { + if (f == null) continue; + + AddFeedbackToList(f); + } + } + + //Add Individual Feedbacks + public void AddFeedbackToList(PepperDash.Essentials.Core.Feedback newFb) + { + if (newFb == null) return; + + if (!Feedbacks.Contains(newFb)) + { + Feedbacks.Add(newFb); + } + } + + #endregion + + #region IRouting Members + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + var input = inputSelector as HdMdNxMHdmiInput; + var output = outputSelector as HdMdNxMHdmiOutput; + Debug.LogVerbose(this, "ExecuteSwitch: input={0} output={1}", input, output); + + if (output == null) + { + Debug.LogInformation(this, "Unable to make switch. output selector is not HdMdNxMHdmiOutput"); + return; + } + + // Try to make switch only when necessary. The unit appears to toggle when already selected. + var current = output.VideoOut; + if (current != input) + output.VideoOut = input; + } + + #endregion + + #region IRoutingNumeric Members + + public void ExecuteNumericSwitch(ushort inputSelector, ushort outputSelector, eRoutingSignalType signalType) + { + var input = inputSelector == 0 ? null : _Chassis.HdmiInputs[inputSelector]; + var output = _Chassis.HdmiOutputs[outputSelector]; + + Debug.LogVerbose(this, "ExecuteNumericSwitch: input={0} output={1}", input, output); + + ExecuteSwitch(input, output, signalType); + } + + #endregion + + #endregion + + #region Bridge Linking + + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = new HdMdNxM4kEControllerJoinMap(joinStart); + + var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); + + if (!string.IsNullOrEmpty(joinMapSerialized)) + joinMap = JsonConvert.DeserializeObject(joinMapSerialized); + + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + else + { + Debug.LogInformation(this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); + } + + IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + DeviceNameFeedback.LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); + + if (_Chassis != null) + { + trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _Chassis.AutoRouteOn()); + trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _Chassis.AutoRouteOff()); + AutoRouteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); + } + + for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) + { + var joinIndex = i - 1; + var input = i; + //Digital + VideoInputSyncFeedbacks[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinMap.InputSync.JoinNumber + joinIndex]); + InputHdcpEnableFeedback[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinMap.EnableInputHdcp.JoinNumber + joinIndex]); + InputHdcpEnableFeedback[InputNames[input]].LinkComplementInputSig(trilist.BooleanInput[joinMap.DisableInputHdcp.JoinNumber + joinIndex]); + trilist.SetSigTrueAction(joinMap.EnableInputHdcp.JoinNumber + joinIndex, () => EnableHdcp(input)); + trilist.SetSigTrueAction(joinMap.DisableInputHdcp.JoinNumber + joinIndex, () => DisableHdcp(input)); + + //Serial + InputNameFeedbacks[InputNames[input]].LinkInputSig(trilist.StringInput[joinMap.InputName.JoinNumber + joinIndex]); + } + + for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) + { + var joinIndex = i - 1; + var output = i; + //Analog + VideoOutputRouteFeedbacks[OutputNames[output]].LinkInputSig(trilist.UShortInput[joinMap.OutputRoute.JoinNumber + joinIndex]); + trilist.SetUShortSigAction(joinMap.OutputRoute.JoinNumber + joinIndex, (a) => ExecuteNumericSwitch(a, (ushort) output, eRoutingSignalType.AudioVideo)); + + //Serial + OutputNameFeedbacks[OutputNames[output]].LinkInputSig(trilist.StringInput[joinMap.OutputName.JoinNumber + joinIndex]); + OutputRouteNameFeedbacks[OutputNames[output]].LinkInputSig(trilist.StringInput[joinMap.OutputRoutedName.JoinNumber + joinIndex]); + } + + _Chassis.OnlineStatusChange += Chassis_OnlineStatusChange; + + trilist.OnlineStatusChange += (d, args) => + { + if (!args.DeviceOnLine) return; + + // feedback updates was moved to the Chassis_OnlineStatusChange + // due to the amount of time it takes for the device to come online + }; + } + + + #endregion + + #region Events + + void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice, Crestron.SimplSharpPro.OnlineOfflineEventArgs args) + { + IsOnline.FireUpdate(); + + if (!args.DeviceOnLine) return; + + foreach (var feedback in Feedbacks) + { + feedback.FireUpdate(); + } + + if (_Chassis != null) + AutoRouteFeedback.FireUpdate(); + } + + void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) + { + if (args.EventId != DMOutputEventIds.VideoOutEventId) return; + + var output = args.Number; + + var inputNumber = _Chassis.HdmiOutputs[output].VideoOutFeedback == null + ? 0 + : _Chassis.HdmiOutputs[output].VideoOutFeedback.Number; + + var outputName = OutputNames[output]; + + var feedback = VideoOutputRouteFeedbacks[outputName]; + + if (feedback == null) + { + return; + } + var inPort = InputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output].VideoOutFeedback); + var outPort = OutputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output]); + + feedback.FireUpdate(); + OnSwitchChange(new RoutingNumericEventArgs(output, inputNumber, outPort, inPort, eRoutingSignalType.AudioVideo)); + } + + void Chassis_DMInputChange(Switch device, DMInputEventArgs args) + { + switch (args.EventId) + { + case DMInputEventIds.VideoDetectedEventId: + { + Debug.LogDebug(this, "Event ID {0}: Updating VideoInputSyncFeedbacks", args.EventId); + foreach (var item in VideoInputSyncFeedbacks) + { + item.FireUpdate(); + } + break; + } + case DMInputEventIds.InputNameFeedbackEventId: + case DMInputEventIds.InputNameEventId: + case DMInputEventIds.NameFeedbackEventId: + { + Debug.LogDebug(this, "Event ID {0}: Updating name feedbacks.", args.EventId); + Debug.LogDebug(this, "Input {0} Name {1}", args.Number, + _Chassis.HdmiInputs[args.Number].NameFeedback.StringValue); + foreach (var item in InputNameFeedbacks) + { + item.FireUpdate(); + } + break; + } + default: + { + Debug.LogDebug(this, "Unhandled DM Input Event ID {0}", args.EventId); + break; + } + } + } + + #endregion + + #region Factory + + public class HdMdNxM4kZEControllerFactory : EssentialsPluginDeviceFactory + { + public HdMdNxM4kZEControllerFactory() + { + MinimumEssentialsFrameworkVersion = "2.24.4"; + TypeNames = new List() { "hdmd4x14kze", "hdmd4x24kze", "hdmd8x84kze" }; + } + + public override EssentialsDevice BuildDevice(DeviceConfig dc) + { + Debug.LogDebug("Factory Attempting to create new HD-MD-NxM-4KZ-E Device"); + + var props = JsonConvert.DeserializeObject(dc.Properties.ToString()); + + var type = dc.Type.ToLower(); + var control = props.Control; + var ipid = control.IpIdInt; + var address = control.TcpSshProperties.Address; + + switch (type) + { + case ("hdmd4x14kze"): + return new HdMdNxM4kZEController(dc.Key, dc.Name, new HdMd4x14kzE(ipid, Global.ControlSystem), props); + case ("hdmd4x24kze"): + return new HdMdNxM4kZEController(dc.Key, dc.Name, new HdMd4x24kzE(ipid, Global.ControlSystem), props); + case ("hdmd8x84kze"): + return new HdMdNxM4kZEController(dc.Key, dc.Name, new HdMd8x84kzE(ipid, Global.ControlSystem), props); + default: + return null; + } + } + } + + #endregion + + + + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.DM.csproj b/src/PepperDash.Essentials.DM.csproj index 3faf164..11240b0 100644 --- a/src/PepperDash.Essentials.DM.csproj +++ b/src/PepperDash.Essentials.DM.csproj @@ -1,21 +1,21 @@ - - ProgramLibrary - + + ProgramLibrary + net472 PepperDash.Essentials.DM false PepperDash.Essentials.DM - This software is a PepperDash Essentials Plugin for PepperDash.Essentials.DM. + This software is a PepperDash Essentials Plugin for PepperDash.Essentials.DM. true - 4Series\bin\$(Configuration)\ + 4Series\bin\$(Configuration)\ PepperDash.Essentials.Plugin.4Series.DM https://github.com/PepperDash/PepperDash.Essentials.DM.git crestron 4series essentials plugin PepperDash Essentials DM True - PepperDash Essentials DM 4Series Plugin + PepperDash Essentials DM 4Series Plugin git https://github.com/PepperDash/PepperDash.Essentials.DM crestron;4series; From 4a4bbde36e8e579ed7cc4bb7698dd9554bdfc4c9 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Fri, 16 Jan 2026 11:52:18 -0600 Subject: [PATCH 02/50] refactor: clean up commented code in HdMdNxM4KzEController --- src/Chassis/HdMdNxM4KzEController.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 3f8ceab..17c89f5 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -20,9 +20,7 @@ public class HdMdNxM4kZEController : CrestronGenericBridgeableBaseDevice, IRouti { private HdMdNxM4kzE _Chassis; - //private HdMd4x14kzE _Chassis4x1; - - //IroutingNumericEvent + //IroutingNumericEvent public event EventHandler NumericSwitchChange; public Dictionary InputNames { get; set; } From 954f5940bd1c7d13b8cac11fbfb3efcf88b04887 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Fri, 16 Jan 2026 12:02:55 -0600 Subject: [PATCH 03/50] refactor: update feedback initialization and comment out unused input name assignments in HdMdNxM4KzEController --- src/Chassis/HdMdNxM4KzEController.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 17c89f5..15567d4 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -85,14 +85,14 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, if (_Chassis is HdMd4x14kzE _chassis) { - AutoRouteFeedback = new BoolFeedback(() => _chassis.AutoRouteOnFeedback.BoolValue); + AutoRouteFeedback = new BoolFeedback("autoRouteFeedback", () => _chassis.AutoRouteOnFeedback.BoolValue); } for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) { var index = i; var inputName = InputNames[index]; - _Chassis.Inputs[index].Name.StringValue = inputName; + // _Chassis.Inputs[index].Name.StringValue = inputName; _Chassis.HdmiInputs[index].Name.StringValue = inputName; InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, @@ -102,7 +102,7 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, }); VideoInputSyncFeedbacks.Add(new BoolFeedback(inputName, () => _Chassis.Inputs[index].VideoDetectedFeedback.BoolValue)); - InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].NameFeedback.StringValue)); + //InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].NameFeedback.StringValue)); InputNameFeedbacks.Add(new StringFeedback(inputName, () => InputNames[index])); InputHdcpEnableFeedback.Add(new BoolFeedback(inputName, () => _Chassis.HdmiInputs[index].HdmiInputPort.HdcpSupportOnFeedback.BoolValue)); } From a6246f633f615d0a4270090093aa7af6ae372d4e Mon Sep 17 00:00:00 2001 From: jkdevito Date: Fri, 16 Jan 2026 12:03:05 -0600 Subject: [PATCH 04/50] refactor: streamline chassis handling and feedback updates in HdMdNxM4kEBridgeableController --- src/Chassis/HdMdNxM4kEBridgeableController.cs | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/Chassis/HdMdNxM4kEBridgeableController.cs b/src/Chassis/HdMdNxM4kEBridgeableController.cs index e416443..ecbbeec 100644 --- a/src/Chassis/HdMdNxM4kEBridgeableController.cs +++ b/src/Chassis/HdMdNxM4kEBridgeableController.cs @@ -19,8 +19,7 @@ namespace PepperDash.Essentials.DM.Chassis public class HdMdNxM4kEBridgeableController : CrestronGenericBridgeableBaseDevice, IRoutingNumericWithFeedback, IHasFeedback { private HdMdNxM _Chassis; - private HdMd4x14kE _Chassis4x1; - + //IroutingNumericEvent public event EventHandler NumericSwitchChange; @@ -84,10 +83,9 @@ public HdMdNxM4kEBridgeableController(string key, string name, HdMdNxM chassis, InputPorts = new RoutingPortCollection(); OutputPorts = new RoutingPortCollection(); - if (_Chassis.NumberOfInputs == 1) + if(_Chassis is HdMd4x14kE _chssis) { - _Chassis4x1 = _Chassis as HdMd4x14kE; - AutoRouteFeedback = new BoolFeedback(() => _Chassis4x1.AutoModeOnFeedback.BoolValue); + AutoRouteFeedback = new BoolFeedback("autoRouteFeedback", () => _chssis.AutoModeOnFeedback.BoolValue); } for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) @@ -102,6 +100,7 @@ public HdMdNxM4kEBridgeableController(string key, string name, HdMdNxM chassis, { FeedbackMatchObject = _Chassis.HdmiInputs[index] }); + VideoInputSyncFeedbacks.Add(new BoolFeedback(inputName, () => _Chassis.Inputs[index].VideoDetectedFeedback.BoolValue)); //InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].NameFeedback.StringValue)); InputNameFeedbacks.Add(new StringFeedback(inputName, () => InputNames[index])); @@ -165,20 +164,20 @@ public void DisableHdcp(uint port) public void EnableAutoRoute() { - if (_Chassis.NumberOfInputs != 1) return; + if (_Chassis.NumberOfOutputs > 1) return; - if (_Chassis4x1 == null) return; + if(!(_Chassis is HdMd4x14kE _chassis)) return; - _Chassis4x1.AutoModeOn(); + _chassis.AutoModeOn(); } public void DisableAutoRoute() { - if (_Chassis.NumberOfInputs != 1) return; + if (_Chassis.NumberOfOutputs > 1) return; - if (_Chassis4x1 == null) return; + if (!(_Chassis is HdMd4x14kE _chassis)) return; - _Chassis4x1.AutoModeOff(); + _chassis.AutoModeOff(); } #region PostActivate @@ -333,10 +332,10 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); DeviceNameFeedback.LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); - if (_Chassis4x1 != null) + if (_Chassis != null && _Chassis is HdMd4x14kE _chassis) { - trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _Chassis4x1.AutoModeOn()); - trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _Chassis4x1.AutoModeOff()); + trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoModeOn()); + trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoModeOff()); AutoRouteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); } @@ -389,14 +388,16 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice IsOnline.FireUpdate(); if (!args.DeviceOnLine) return; - - foreach (var feedback in Feedbacks) - { - feedback.FireUpdate(); - } - - if (_Chassis4x1 != null) - AutoRouteFeedback.FireUpdate(); + + foreach (var feedback in Feedbacks) + { + feedback.FireUpdate(); + } + + if(_Chassis == null && _Chassis is HdMd4x14kE _chassis) + { + AutoRouteFeedback.FireUpdate(); + } } void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) From 468bb7397a151421ba90f340dd899c966f5d36e5 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Fri, 16 Jan 2026 12:18:29 -0600 Subject: [PATCH 05/50] refactor: HdMdNxM4Ke controller class - Remove the obsolete HdMdNxM4Ke class. - Rename HdMdNxM4KeBridgeableController to HdMdNxM4Ke. - Add `hdmd4x14ke` to the supported types. - Keep `hdmd4x14ke-bridgeable` as a supported type for backward compatibility. BREAKING CHANGE: Remove the obsolete HdMd4NxM4ke class. --- src/Chassis/HdMdNxM4KzEController.cs | 4 +- src/Chassis/HdMdNxM4kEBridgeableController.cs | 505 -------------- src/Chassis/HdMdNxM4kEController.cs | 642 +++++++++++++----- src/Config/HdMdNxM4kEPropertiesConfig.cs | 9 - 4 files changed, 492 insertions(+), 668 deletions(-) delete mode 100644 src/Chassis/HdMdNxM4kEBridgeableController.cs diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 15567d4..a040837 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -41,7 +41,7 @@ public class HdMdNxM4kZEController : CrestronGenericBridgeableBaseDevice, IRouti #region Constructor public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, - HdMdNxM4kEBridgeablePropertiesConfig props) + HdMdNxM4kEPropertiesConfig props) : base(key, name, chassis) { _Chassis = chassis; @@ -479,7 +479,7 @@ public override EssentialsDevice BuildDevice(DeviceConfig dc) { Debug.LogDebug("Factory Attempting to create new HD-MD-NxM-4KZ-E Device"); - var props = JsonConvert.DeserializeObject(dc.Properties.ToString()); + var props = JsonConvert.DeserializeObject(dc.Properties.ToString()); var type = dc.Type.ToLower(); var control = props.Control; diff --git a/src/Chassis/HdMdNxM4kEBridgeableController.cs b/src/Chassis/HdMdNxM4kEBridgeableController.cs deleted file mode 100644 index ecbbeec..0000000 --- a/src/Chassis/HdMdNxM4kEBridgeableController.cs +++ /dev/null @@ -1,505 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using Newtonsoft.Json; -using Crestron.SimplSharp; -using Crestron.SimplSharpPro.DeviceSupport; -using Crestron.SimplSharpPro.DM; -using PepperDash.Core; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.DM.Config; -using PepperDash.Essentials.Core.Bridges; -using PepperDash.Essentials.Core.Config; - -namespace PepperDash.Essentials.DM.Chassis -{ - [Description("Wrapper class for all HdMdNxM4E switchers")] - public class HdMdNxM4kEBridgeableController : CrestronGenericBridgeableBaseDevice, IRoutingNumericWithFeedback, IHasFeedback - { - private HdMdNxM _Chassis; - - //IroutingNumericEvent - public event EventHandler NumericSwitchChange; - - public Dictionary InputNames { get; set; } - public Dictionary OutputNames { get; set; } - - public RoutingPortCollection InputPorts { get; private set; } - public RoutingPortCollection OutputPorts { get; private set; } - - public FeedbackCollection VideoInputSyncFeedbacks { get; private set; } - public FeedbackCollection VideoOutputRouteFeedbacks { get; private set; } - public FeedbackCollection InputNameFeedbacks { get; private set; } - public FeedbackCollection OutputNameFeedbacks { get; private set; } - public FeedbackCollection OutputRouteNameFeedbacks { get; private set; } - public FeedbackCollection InputHdcpEnableFeedback { get; private set; } - public StringFeedback DeviceNameFeedback { get; private set; } - public BoolFeedback AutoRouteFeedback { get; private set; } - - #region Constructor - - public HdMdNxM4kEBridgeableController(string key, string name, HdMdNxM chassis, - HdMdNxM4kEBridgeablePropertiesConfig props) - : base(key, name, chassis) - { - _Chassis = chassis; - Name = name; - - if (props == null) - { - Debug.LogDebug(this, "HdMdNx4keBridgeableController properties are null, failed to build the device"); - return; - } - - - if (props.Inputs != null) - { - foreach (var kvp in props.Inputs) - { - Debug.LogDebug(this, "props.Inputs: {0}-{1}", kvp.Key, kvp.Value); - } - InputNames = props.Inputs; - } - if (props.Outputs != null) - { - foreach (var kvp in props.Outputs) - { - Debug.LogDebug(this, "props.Outputs: {0}-{1}", kvp.Key, kvp.Value); - } - OutputNames = props.Outputs; - } - - DeviceNameFeedback = new StringFeedback(()=>Name); - - VideoInputSyncFeedbacks = new FeedbackCollection(); - VideoOutputRouteFeedbacks = new FeedbackCollection(); - InputNameFeedbacks = new FeedbackCollection(); - OutputNameFeedbacks = new FeedbackCollection(); - OutputRouteNameFeedbacks = new FeedbackCollection(); - InputHdcpEnableFeedback = new FeedbackCollection(); - - InputPorts = new RoutingPortCollection(); - OutputPorts = new RoutingPortCollection(); - - if(_Chassis is HdMd4x14kE _chssis) - { - AutoRouteFeedback = new BoolFeedback("autoRouteFeedback", () => _chssis.AutoModeOnFeedback.BoolValue); - } - - for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) - { - var index = i; - var inputName = InputNames[index]; - //_Chassis.Inputs[index].Name.StringValue = inputName; - _Chassis.HdmiInputs[index].Name.StringValue = inputName; - - InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, _Chassis.HdmiInputs[index], this) - { - FeedbackMatchObject = _Chassis.HdmiInputs[index] - }); - - VideoInputSyncFeedbacks.Add(new BoolFeedback(inputName, () => _Chassis.Inputs[index].VideoDetectedFeedback.BoolValue)); - //InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].NameFeedback.StringValue)); - InputNameFeedbacks.Add(new StringFeedback(inputName, () => InputNames[index])); - InputHdcpEnableFeedback.Add(new BoolFeedback(inputName, () => _Chassis.HdmiInputs[index].HdmiInputPort.HdcpSupportOnFeedback.BoolValue)); - } - - for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) - { - var index = i; - var outputName = OutputNames[index]; - //_Chassis.Outputs[index].Name.StringValue = outputName; - //_Chassis.HdmiOutputs[index].Name.StringValue = outputName; - - OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, _Chassis.HdmiOutputs[index], this) - { - FeedbackMatchObject = _Chassis.HdmiOutputs[index] - }); - VideoOutputRouteFeedbacks.Add(new IntFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback == null ? 0 : (int)_Chassis.Outputs[index].VideoOutFeedback.Number)); - OutputNameFeedbacks.Add(new StringFeedback(outputName, () => OutputNames[index])); - OutputRouteNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback.NameFeedback.StringValue)); - } - - _Chassis.DMInputChange += Chassis_DMInputChange; - _Chassis.DMOutputChange += Chassis_DMOutputChange; - - AddPostActivationAction(AddFeedbackCollections); - } - - #endregion - - #region Methods - - /// - /// Raise an event when the status of a switch object changes. - /// - /// Arguments defined as IKeyName sender, output, input, and eRoutingSignalType - private void OnSwitchChange(RoutingNumericEventArgs e) - { - var newEvent = NumericSwitchChange; - if (newEvent != null) newEvent(this, e); - } - - public void EnableHdcp(uint port) - { - if (port > _Chassis.NumberOfInputs) return; - if (port <= 0) return; - - _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOn(); - InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); - } - - public void DisableHdcp(uint port) - { - if (port > _Chassis.NumberOfInputs) return; - if (port <= 0) return; - - _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOff(); - InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); - } - - public void EnableAutoRoute() - { - if (_Chassis.NumberOfOutputs > 1) return; - - if(!(_Chassis is HdMd4x14kE _chassis)) return; - - _chassis.AutoModeOn(); - } - - public void DisableAutoRoute() - { - if (_Chassis.NumberOfOutputs > 1) return; - - if (!(_Chassis is HdMd4x14kE _chassis)) return; - - _chassis.AutoModeOff(); - } - - #region PostActivate - - public void AddFeedbackCollections() - { - AddFeedbackToList(DeviceNameFeedback); - AddCollectionsToList(VideoInputSyncFeedbacks, InputHdcpEnableFeedback); - AddCollectionsToList(VideoOutputRouteFeedbacks); - AddCollectionsToList(InputNameFeedbacks, OutputNameFeedbacks, OutputRouteNameFeedbacks); - } - - #endregion - - #region FeedbackCollection Methods - - //Add arrays of collections - public void AddCollectionsToList(params FeedbackCollection[] newFbs) - { - foreach (FeedbackCollection fbCollection in newFbs) - { - foreach (var item in newFbs) - { - AddCollectionToList(item); - } - } - } - public void AddCollectionsToList(params FeedbackCollection[] newFbs) - { - foreach (FeedbackCollection fbCollection in newFbs) - { - foreach (var item in newFbs) - { - AddCollectionToList(item); - } - } - } - - public void AddCollectionsToList(params FeedbackCollection[] newFbs) - { - foreach (FeedbackCollection fbCollection in newFbs) - { - foreach (var item in newFbs) - { - AddCollectionToList(item); - } - } - } - - //Add Collections - public void AddCollectionToList(FeedbackCollection newFbs) - { - foreach (var f in newFbs) - { - if (f == null) continue; - - AddFeedbackToList(f); - } - } - - public void AddCollectionToList(FeedbackCollection newFbs) - { - foreach (var f in newFbs) - { - if (f == null) continue; - - AddFeedbackToList(f); - } - } - - public void AddCollectionToList(FeedbackCollection newFbs) - { - foreach (var f in newFbs) - { - if (f == null) continue; - - AddFeedbackToList(f); - } - } - - //Add Individual Feedbacks - public void AddFeedbackToList(PepperDash.Essentials.Core.Feedback newFb) - { - if (newFb == null) return; - - if (!Feedbacks.Contains(newFb)) - { - Feedbacks.Add(newFb); - } - } - - #endregion - - #region IRouting Members - - public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) - { - var input = inputSelector as HdMdNxMHdmiInput; //changed from HdMdNxM4kzEHdmiInput; - var output = outputSelector as HdMdNxMHdmiOutput; - Debug.LogVerbose(this, "ExecuteSwitch: input={0} output={1}", input, output); - - if (output == null) - { - Debug.LogInformation(this, "Unable to make switch. output selector is not HdMdNxMHdmiOutput"); - return; - } - - // Try to make switch only when necessary. The unit appears to toggle when already selected. - var current = output.VideoOut; - if (current != input) - output.VideoOut = input; - } - - #endregion - - #region IRoutingNumeric Members - - public void ExecuteNumericSwitch(ushort inputSelector, ushort outputSelector, eRoutingSignalType signalType) - { - var input = inputSelector == 0 ? null : _Chassis.HdmiInputs[inputSelector]; - var output = _Chassis.HdmiOutputs[outputSelector]; - - Debug.LogVerbose(this, "ExecuteNumericSwitch: input={0} output={1}", input, output); - - ExecuteSwitch(input, output, signalType); - } - - #endregion - - #endregion - - #region Bridge Linking - - public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) - { - var joinMap = new HdMdNxM4kEControllerJoinMap(joinStart); - - var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); - - if (!string.IsNullOrEmpty(joinMapSerialized)) - joinMap = JsonConvert.DeserializeObject(joinMapSerialized); - - if (bridge != null) - { - bridge.AddJoinMap(Key, joinMap); - } - else - { - Debug.LogInformation(this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); - } - - IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); - DeviceNameFeedback.LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); - - if (_Chassis != null && _Chassis is HdMd4x14kE _chassis) - { - trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoModeOn()); - trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoModeOff()); - AutoRouteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); - } - - for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) - { - var joinIndex = i - 1; - var input = i; - //Digital - VideoInputSyncFeedbacks[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinMap.InputSync.JoinNumber + joinIndex]); - InputHdcpEnableFeedback[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinMap.EnableInputHdcp.JoinNumber + joinIndex]); - InputHdcpEnableFeedback[InputNames[input]].LinkComplementInputSig(trilist.BooleanInput[joinMap.DisableInputHdcp.JoinNumber + joinIndex]); - trilist.SetSigTrueAction(joinMap.EnableInputHdcp.JoinNumber + joinIndex, () => EnableHdcp(input)); - trilist.SetSigTrueAction(joinMap.DisableInputHdcp.JoinNumber + joinIndex, () => DisableHdcp(input)); - - //Serial - InputNameFeedbacks[InputNames[input]].LinkInputSig(trilist.StringInput[joinMap.InputName.JoinNumber + joinIndex]); - } - - for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) - { - var joinIndex = i - 1; - var output = i; - //Analog - VideoOutputRouteFeedbacks[OutputNames[output]].LinkInputSig(trilist.UShortInput[joinMap.OutputRoute.JoinNumber + joinIndex]); - trilist.SetUShortSigAction(joinMap.OutputRoute.JoinNumber + joinIndex, (a) => ExecuteNumericSwitch(a, (ushort) output, eRoutingSignalType.AudioVideo)); - - //Serial - OutputNameFeedbacks[OutputNames[output]].LinkInputSig(trilist.StringInput[joinMap.OutputName.JoinNumber + joinIndex]); - OutputRouteNameFeedbacks[OutputNames[output]].LinkInputSig(trilist.StringInput[joinMap.OutputRoutedName.JoinNumber + joinIndex]); - } - - _Chassis.OnlineStatusChange += Chassis_OnlineStatusChange; - - trilist.OnlineStatusChange += (d, args) => - { - if (!args.DeviceOnLine) return; - - // feedback updates was moved to the Chassis_OnlineStatusChange - // due to the amount of time it takes for the device to come online - }; - } - - - #endregion - - #region Events - - void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice, Crestron.SimplSharpPro.OnlineOfflineEventArgs args) - { - IsOnline.FireUpdate(); - - if (!args.DeviceOnLine) return; - - foreach (var feedback in Feedbacks) - { - feedback.FireUpdate(); - } - - if(_Chassis == null && _Chassis is HdMd4x14kE _chassis) - { - AutoRouteFeedback.FireUpdate(); - } - } - - void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) - { - if (args.EventId != DMOutputEventIds.VideoOutEventId) return; - - var output = args.Number; - - var inputNumber = _Chassis.HdmiOutputs[output].VideoOutFeedback == null - ? 0 - : _Chassis.HdmiOutputs[output].VideoOutFeedback.Number; - - var outputName = OutputNames[output]; - - var feedback = VideoOutputRouteFeedbacks[outputName]; - - if (feedback == null) - { - return; - } - var inPort = - InputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output].VideoOutFeedback); - var outPort = OutputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output]); - - feedback.FireUpdate(); - OnSwitchChange(new RoutingNumericEventArgs(output, inputNumber, outPort, inPort, eRoutingSignalType.AudioVideo)); - } - - void Chassis_DMInputChange(Switch device, DMInputEventArgs args) - { - switch (args.EventId) - { - case DMInputEventIds.VideoDetectedEventId: - { - Debug.LogDebug(this, "Event ID {0}: Updating VideoInputSyncFeedbacks", args.EventId); - foreach (var item in VideoInputSyncFeedbacks) - { - item.FireUpdate(); - } - break; - } - case DMInputEventIds.InputNameFeedbackEventId: - case DMInputEventIds.InputNameEventId: - case DMInputEventIds.NameFeedbackEventId: - { - Debug.LogDebug(this, "Event ID {0}: Updating name feedbacks.", args.EventId); - Debug.LogDebug(this, "Input {0} Name {1}", args.Number, - _Chassis.HdmiInputs[args.Number].NameFeedback.StringValue); - foreach (var item in InputNameFeedbacks) - { - item.FireUpdate(); - } - break; - } - default: - { - Debug.LogDebug(this, "Unhandled DM Input Event ID {0}", args.EventId); - break; - } - } - } - - #endregion - - #region Factory - - public class HdMdNxM4kEControllerFactory : EssentialsPluginDeviceFactory - { - public HdMdNxM4kEControllerFactory() - { - MinimumEssentialsFrameworkVersion = "2.4.5"; - TypeNames = new List() { "hdmd4x14ke-bridgeable", "hdmd4x24ke", "hdmd6x24ke" }; - } - - public override EssentialsDevice BuildDevice(DeviceConfig dc) - { - Debug.LogDebug("Factory Attempting to create new HD-MD-NxM-4K-E Device"); - - var props = JsonConvert.DeserializeObject(dc.Properties.ToString()); - - var type = dc.Type.ToLower(); - var control = props.Control; - var ipid = control.IpIdInt; - var address = control.TcpSshProperties.Address; - - switch (type) - { - case ("hdmd4x14ke-bridgeable"): - return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd4x14kE(ipid, address, Global.ControlSystem), props); - case ("hdmd4x24ke"): - return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd4x24kE(ipid, address, Global.ControlSystem), props); - case ("hdmd6x24ke"): - return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd6x24kE(ipid, address, Global.ControlSystem), props); - default: - return null; - } - } - } - - #endregion - - - - } -} \ No newline at end of file diff --git a/src/Chassis/HdMdNxM4kEController.cs b/src/Chassis/HdMdNxM4kEController.cs index 9eb53d8..21c4c30 100644 --- a/src/Chassis/HdMdNxM4kEController.cs +++ b/src/Chassis/HdMdNxM4kEController.cs @@ -3,166 +3,504 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; +using Newtonsoft.Json; using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DM; -using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.DM.Config; +using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.Config; namespace PepperDash.Essentials.DM.Chassis { - [Obsolete("Please use HdMdNxM4kEBridgeable Controller")] - public class HdMdNxM4kEController : CrestronGenericBaseDevice, IRoutingInputsOutputs, IRouting - { - public HdMdNxM Chassis { get; private set; } - - public RoutingPortCollection InputPorts { get; private set; } - public RoutingPortCollection OutputPorts { get; private set; } - - - /// - /// - /// - /// - /// - /// - public HdMdNxM4kEController(string key, string name, HdMdNxM chassis, - HdMdNxM4kEPropertiesConfig props) - : base(key, name, chassis) - { - Debug.LogInformation(this, "Type hdmd4x14ke is obsolete. Please use hdmd4x14ke-bridgeable"); - Chassis = chassis; - - // logical ports - InputPorts = new RoutingPortCollection(); - for (uint i = 1; i <= 4; i++) - { - InputPorts.Add(new RoutingInputPort("hdmiIn" + i, eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.Hdmi, i, this)); - } - OutputPorts = new RoutingPortCollection(); - OutputPorts.Add(new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.Hdmi, null, this)); - - // physical settings - if (props != null && props.Inputs != null) - { - var inputRegex = new Regex(@"(?\d)", RegexOptions.IgnoreCase); - foreach (var kvp in props.Inputs) - { - // get numnbers from key and convert to int - //var inputNum = Convert.ToUInt32(kvp.Key.Substring(6)); - var inputMatch = inputRegex.Match(kvp.Key); - if (inputMatch == null) continue; - - var inputNum = Convert.ToUInt32(inputMatch.Groups["InputNum"].Value); - - var port = chassis.HdmiInputs[inputNum].HdmiInputPort; - // set hdcp disables - if (kvp.Value.DisableHdcp) + [Description("Wrapper class for all HdMdNxM4E switchers")] + public class HdMdNxM4kEBridgeableController : CrestronGenericBridgeableBaseDevice, IRoutingNumericWithFeedback, IHasFeedback + { + private HdMdNxM _Chassis; + + //IroutingNumericEvent + public event EventHandler NumericSwitchChange; + + public Dictionary InputNames { get; set; } + public Dictionary OutputNames { get; set; } + + public RoutingPortCollection InputPorts { get; private set; } + public RoutingPortCollection OutputPorts { get; private set; } + + public FeedbackCollection VideoInputSyncFeedbacks { get; private set; } + public FeedbackCollection VideoOutputRouteFeedbacks { get; private set; } + public FeedbackCollection InputNameFeedbacks { get; private set; } + public FeedbackCollection OutputNameFeedbacks { get; private set; } + public FeedbackCollection OutputRouteNameFeedbacks { get; private set; } + public FeedbackCollection InputHdcpEnableFeedback { get; private set; } + public StringFeedback DeviceNameFeedback { get; private set; } + public BoolFeedback AutoRouteFeedback { get; private set; } + + #region Constructor + + public HdMdNxM4kEBridgeableController(string key, string name, HdMdNxM chassis, + HdMdNxM4kEPropertiesConfig props) + : base(key, name, chassis) + { + _Chassis = chassis; + Name = name; + + if (props == null) + { + Debug.LogDebug(this, "HdMdNx4keBridgeableController properties are null, failed to build the device"); + return; + } + + + if (props.Inputs != null) + { + foreach (var kvp in props.Inputs) + { + Debug.LogDebug(this, "props.Inputs: {0}-{1}", kvp.Key, kvp.Value); + } + InputNames = props.Inputs; + } + if (props.Outputs != null) + { + foreach (var kvp in props.Outputs) + { + Debug.LogDebug(this, "props.Outputs: {0}-{1}", kvp.Key, kvp.Value); + } + OutputNames = props.Outputs; + } + + DeviceNameFeedback = new StringFeedback(()=>Name); + + VideoInputSyncFeedbacks = new FeedbackCollection(); + VideoOutputRouteFeedbacks = new FeedbackCollection(); + InputNameFeedbacks = new FeedbackCollection(); + OutputNameFeedbacks = new FeedbackCollection(); + OutputRouteNameFeedbacks = new FeedbackCollection(); + InputHdcpEnableFeedback = new FeedbackCollection(); + + InputPorts = new RoutingPortCollection(); + OutputPorts = new RoutingPortCollection(); + + if(_Chassis is HdMd4x14kE _chssis) + { + AutoRouteFeedback = new BoolFeedback("autoRouteFeedback", () => _chssis.AutoModeOnFeedback.BoolValue); + } + + for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) + { + var index = i; + var inputName = InputNames[index]; + //_Chassis.Inputs[index].Name.StringValue = inputName; + _Chassis.HdmiInputs[index].Name.StringValue = inputName; + + InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, _Chassis.HdmiInputs[index], this) + { + FeedbackMatchObject = _Chassis.HdmiInputs[index] + }); + + VideoInputSyncFeedbacks.Add(new BoolFeedback(inputName, () => _Chassis.Inputs[index].VideoDetectedFeedback.BoolValue)); + //InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].NameFeedback.StringValue)); + InputNameFeedbacks.Add(new StringFeedback(inputName, () => InputNames[index])); + InputHdcpEnableFeedback.Add(new BoolFeedback(inputName, () => _Chassis.HdmiInputs[index].HdmiInputPort.HdcpSupportOnFeedback.BoolValue)); + } + + for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) + { + var index = i; + var outputName = OutputNames[index]; + //_Chassis.Outputs[index].Name.StringValue = outputName; + //_Chassis.HdmiOutputs[index].Name.StringValue = outputName; + + OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, _Chassis.HdmiOutputs[index], this) + { + FeedbackMatchObject = _Chassis.HdmiOutputs[index] + }); + VideoOutputRouteFeedbacks.Add(new IntFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback == null ? 0 : (int)_Chassis.Outputs[index].VideoOutFeedback.Number)); + OutputNameFeedbacks.Add(new StringFeedback(outputName, () => OutputNames[index])); + OutputRouteNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback.NameFeedback.StringValue)); + } + + _Chassis.DMInputChange += Chassis_DMInputChange; + _Chassis.DMOutputChange += Chassis_DMOutputChange; + + AddPostActivationAction(AddFeedbackCollections); + } + + #endregion + + #region Methods + + /// + /// Raise an event when the status of a switch object changes. + /// + /// Arguments defined as IKeyName sender, output, input, and eRoutingSignalType + private void OnSwitchChange(RoutingNumericEventArgs e) + { + var newEvent = NumericSwitchChange; + if (newEvent != null) newEvent(this, e); + } + + public void EnableHdcp(uint port) + { + if (port > _Chassis.NumberOfInputs) return; + if (port <= 0) return; + + _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOn(); + InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); + } + + public void DisableHdcp(uint port) + { + if (port > _Chassis.NumberOfInputs) return; + if (port <= 0) return; + + _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOff(); + InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); + } + + public void EnableAutoRoute() + { + if (_Chassis.NumberOfOutputs > 1) return; + + if(!(_Chassis is HdMd4x14kE _chassis)) return; + + _chassis.AutoModeOn(); + } + + public void DisableAutoRoute() + { + if (_Chassis.NumberOfOutputs > 1) return; + + if (!(_Chassis is HdMd4x14kE _chassis)) return; + + _chassis.AutoModeOff(); + } + + #region PostActivate + + public void AddFeedbackCollections() + { + AddFeedbackToList(DeviceNameFeedback); + AddCollectionsToList(VideoInputSyncFeedbacks, InputHdcpEnableFeedback); + AddCollectionsToList(VideoOutputRouteFeedbacks); + AddCollectionsToList(InputNameFeedbacks, OutputNameFeedbacks, OutputRouteNameFeedbacks); + } + + #endregion + + #region FeedbackCollection Methods + + //Add arrays of collections + public void AddCollectionsToList(params FeedbackCollection[] newFbs) + { + foreach (FeedbackCollection fbCollection in newFbs) + { + foreach (var item in newFbs) + { + AddCollectionToList(item); + } + } + } + public void AddCollectionsToList(params FeedbackCollection[] newFbs) + { + foreach (FeedbackCollection fbCollection in newFbs) + { + foreach (var item in newFbs) + { + AddCollectionToList(item); + } + } + } + + public void AddCollectionsToList(params FeedbackCollection[] newFbs) + { + foreach (FeedbackCollection fbCollection in newFbs) + { + foreach (var item in newFbs) + { + AddCollectionToList(item); + } + } + } + + //Add Collections + public void AddCollectionToList(FeedbackCollection newFbs) + { + foreach (var f in newFbs) + { + if (f == null) continue; + + AddFeedbackToList(f); + } + } + + public void AddCollectionToList(FeedbackCollection newFbs) + { + foreach (var f in newFbs) + { + if (f == null) continue; + + AddFeedbackToList(f); + } + } + + public void AddCollectionToList(FeedbackCollection newFbs) + { + foreach (var f in newFbs) + { + if (f == null) continue; + + AddFeedbackToList(f); + } + } + + //Add Individual Feedbacks + public void AddFeedbackToList(PepperDash.Essentials.Core.Feedback newFb) + { + if (newFb == null) return; + + if (!Feedbacks.Contains(newFb)) + { + Feedbacks.Add(newFb); + } + } + + #endregion + + #region IRouting Members + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + var input = inputSelector as HdMdNxMHdmiInput; //changed from HdMdNxM4kzEHdmiInput; + var output = outputSelector as HdMdNxMHdmiOutput; + Debug.LogVerbose(this, "ExecuteSwitch: input={0} output={1}", input, output); + + if (output == null) + { + Debug.LogInformation(this, "Unable to make switch. output selector is not HdMdNxMHdmiOutput"); + return; + } + + // Try to make switch only when necessary. The unit appears to toggle when already selected. + var current = output.VideoOut; + if (current != input) + output.VideoOut = input; + } + + #endregion + + #region IRoutingNumeric Members + + public void ExecuteNumericSwitch(ushort inputSelector, ushort outputSelector, eRoutingSignalType signalType) + { + var input = inputSelector == 0 ? null : _Chassis.HdmiInputs[inputSelector]; + var output = _Chassis.HdmiOutputs[outputSelector]; + + Debug.LogVerbose(this, "ExecuteNumericSwitch: input={0} output={1}", input, output); + + ExecuteSwitch(input, output, signalType); + } + + #endregion + + #endregion + + #region Bridge Linking + + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = new HdMdNxM4kEControllerJoinMap(joinStart); + + var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); + + if (!string.IsNullOrEmpty(joinMapSerialized)) + joinMap = JsonConvert.DeserializeObject(joinMapSerialized); + + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + else + { + Debug.LogInformation(this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); + } + + IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + DeviceNameFeedback.LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); + + if (_Chassis != null && _Chassis is HdMd4x14kE _chassis) + { + trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoModeOn()); + trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoModeOff()); + AutoRouteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); + } + + for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) + { + var joinIndex = i - 1; + var input = i; + //Digital + VideoInputSyncFeedbacks[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinMap.InputSync.JoinNumber + joinIndex]); + InputHdcpEnableFeedback[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinMap.EnableInputHdcp.JoinNumber + joinIndex]); + InputHdcpEnableFeedback[InputNames[input]].LinkComplementInputSig(trilist.BooleanInput[joinMap.DisableInputHdcp.JoinNumber + joinIndex]); + trilist.SetSigTrueAction(joinMap.EnableInputHdcp.JoinNumber + joinIndex, () => EnableHdcp(input)); + trilist.SetSigTrueAction(joinMap.DisableInputHdcp.JoinNumber + joinIndex, () => DisableHdcp(input)); + + //Serial + InputNameFeedbacks[InputNames[input]].LinkInputSig(trilist.StringInput[joinMap.InputName.JoinNumber + joinIndex]); + } + + for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) + { + var joinIndex = i - 1; + var output = i; + //Analog + VideoOutputRouteFeedbacks[OutputNames[output]].LinkInputSig(trilist.UShortInput[joinMap.OutputRoute.JoinNumber + joinIndex]); + trilist.SetUShortSigAction(joinMap.OutputRoute.JoinNumber + joinIndex, (a) => ExecuteNumericSwitch(a, (ushort) output, eRoutingSignalType.AudioVideo)); + + //Serial + OutputNameFeedbacks[OutputNames[output]].LinkInputSig(trilist.StringInput[joinMap.OutputName.JoinNumber + joinIndex]); + OutputRouteNameFeedbacks[OutputNames[output]].LinkInputSig(trilist.StringInput[joinMap.OutputRoutedName.JoinNumber + joinIndex]); + } + + _Chassis.OnlineStatusChange += Chassis_OnlineStatusChange; + + trilist.OnlineStatusChange += (d, args) => + { + if (!args.DeviceOnLine) return; + + // feedback updates was moved to the Chassis_OnlineStatusChange + // due to the amount of time it takes for the device to come online + }; + } + + + #endregion + + #region Events + + void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice, Crestron.SimplSharpPro.OnlineOfflineEventArgs args) + { + IsOnline.FireUpdate(); + + if (!args.DeviceOnLine) return; + + foreach (var feedback in Feedbacks) + { + feedback.FireUpdate(); + } + + if(_Chassis == null && _Chassis is HdMd4x14kE _chassis) + { + AutoRouteFeedback.FireUpdate(); + } + } + + void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) + { + if (args.EventId != DMOutputEventIds.VideoOutEventId) return; + + var output = args.Number; + + var inputNumber = _Chassis.HdmiOutputs[output].VideoOutFeedback == null + ? 0 + : _Chassis.HdmiOutputs[output].VideoOutFeedback.Number; + + var outputName = OutputNames[output]; + + var feedback = VideoOutputRouteFeedbacks[outputName]; + + if (feedback == null) + { + return; + } + var inPort = + InputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output].VideoOutFeedback); + var outPort = OutputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output]); + + feedback.FireUpdate(); + OnSwitchChange(new RoutingNumericEventArgs(output, inputNumber, outPort, inPort, eRoutingSignalType.AudioVideo)); + } + + void Chassis_DMInputChange(Switch device, DMInputEventArgs args) + { + switch (args.EventId) + { + case DMInputEventIds.VideoDetectedEventId: + { + Debug.LogDebug(this, "Event ID {0}: Updating VideoInputSyncFeedbacks", args.EventId); + foreach (var item in VideoInputSyncFeedbacks) { - Debug.LogInformation(this, "Configuration disables HDCP support on {0}", kvp.Key); - port.HdcpSupportOff(); + item.FireUpdate(); } - else - port.HdcpSupportOn(); - } - } - } - - public override bool CustomActivate() - { - var result = Chassis.Register(); - if (result != Crestron.SimplSharpPro.eDeviceRegistrationUnRegistrationResponse.Success) - { - Debug.LogInformation(this, "Device registration failed: {0}", result); - return false; - } - - return base.CustomActivate(); - } - - - - #region IRouting Members - - public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) - { - // Try to make switch only when necessary. The unit appears to toggle when already selected. - var current = Chassis.HdmiOutputs[1].VideoOut; - if (current != Chassis.HdmiInputs[(uint)inputSelector]) - Chassis.HdmiOutputs[1].VideoOut = Chassis.HdmiInputs[(uint)inputSelector]; - } - - #endregion - - ///////////////////////////////////////////////////// - - /// - /// - /// - /// - /// - /// - /// - /// - /// /* - /* - public static HdMdNxM4kEController GetController(string key, string name, - string type, HdMdNxM4kEPropertiesConfig properties) - { - try - { - var ipid = properties.Control.IpIdInt; - var address = properties.Control.TcpSshProperties.Address; - - type = type.ToLower(); - if (type == "hdmd4x14ke") - { - Debug.Console(0, @"The 'hdmd4x14ke' device is not an Essentials Bridgeable device. - If an essentials Bridgeable Device is required, use the 'hdmd4x14ke-bridgeable' type"); - - var chassis = new HdMd4x14kE(ipid, address, Global.ControlSystem); - return new HdMdNxM4kEController(key, name, chassis, properties); - } - return null; - } - catch (Exception e) - { - Debug.LogInformation("ERROR Creating device key {0}: \r{1}", key, e); - return null; - } - }*/ - - #region Factory - - public class HdMdNxM4kEFactory : EssentialsPluginDeviceFactory - { - public HdMdNxM4kEFactory() - { - TypeNames = new List() {"hdmd4x14ke"}; - } - - - public override EssentialsDevice BuildDevice(DeviceConfig dc) - { - Debug.LogDebug("Factory Attempting to create new HD-MD-NxM-4K-E Device"); - - var props = JsonConvert.DeserializeObject(dc.Properties.ToString()); - - var type = dc.Type.ToLower(); - var control = props.Control; - var ipid = control.IpIdInt; - var address = control.TcpSshProperties.Address; - - return new HdMdNxM4kEController(dc.Key, dc.Name, new HdMd4x14kE(ipid, address, Global.ControlSystem), props); - - } - } - - #endregion - - } + break; + } + case DMInputEventIds.InputNameFeedbackEventId: + case DMInputEventIds.InputNameEventId: + case DMInputEventIds.NameFeedbackEventId: + { + Debug.LogDebug(this, "Event ID {0}: Updating name feedbacks.", args.EventId); + Debug.LogDebug(this, "Input {0} Name {1}", args.Number, + _Chassis.HdmiInputs[args.Number].NameFeedback.StringValue); + foreach (var item in InputNameFeedbacks) + { + item.FireUpdate(); + } + break; + } + default: + { + Debug.LogDebug(this, "Unhandled DM Input Event ID {0}", args.EventId); + break; + } + } + } + + #endregion + + #region Factory + + public class HdMdNxM4kEControllerFactory : EssentialsPluginDeviceFactory + { + public HdMdNxM4kEControllerFactory() + { + MinimumEssentialsFrameworkVersion = "2.4.5"; + TypeNames = new List() { "hdmd4x14ke-bridgeable", "hdmd4x14ke", "hdmd4x24ke", "hdmd6x24ke" }; + } + + public override EssentialsDevice BuildDevice(DeviceConfig dc) + { + Debug.LogDebug("Factory Attempting to create new HD-MD-NxM-4K-E Device"); + + var props = JsonConvert.DeserializeObject(dc.Properties.ToString()); + + var type = dc.Type.ToLower(); + var control = props.Control; + var ipid = control.IpIdInt; + var address = control.TcpSshProperties.Address; + + switch (type) + { + case ("hdmd4x14ke-bridgeable"): + case ("hdmd4x14ke"): + return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd4x14kE(ipid, address, Global.ControlSystem), props); + case ("hdmd4x24ke"): + return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd4x24kE(ipid, address, Global.ControlSystem), props); + case ("hdmd6x24ke"): + return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd6x24kE(ipid, address, Global.ControlSystem), props); + default: + return null; + } + } + } + + #endregion + + + + } } \ No newline at end of file diff --git a/src/Config/HdMdNxM4kEPropertiesConfig.cs b/src/Config/HdMdNxM4kEPropertiesConfig.cs index 2873e2b..5577429 100644 --- a/src/Config/HdMdNxM4kEPropertiesConfig.cs +++ b/src/Config/HdMdNxM4kEPropertiesConfig.cs @@ -17,15 +17,6 @@ public class HdMdNxM4kEPropertiesConfig [JsonProperty("control")] public ControlPropertiesConfig Control { get; set; } - [JsonProperty("inputs")] - public Dictionary Inputs { get; set; } - } - - public class HdMdNxM4kEBridgeablePropertiesConfig - { - [JsonProperty("control")] - public ControlPropertiesConfig Control { get; set; } - [JsonProperty("inputs")] public Dictionary Inputs { get; set; } From 690910986e7c205ff6d367ab46bca61324edbc87 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Fri, 16 Jan 2026 14:03:47 -0600 Subject: [PATCH 06/50] refactor: enhance input/output name handling and add error logging in HdMdNxM4kEController --- src/Chassis/HdMdNxM4KzEController.cs | 34 +++++++++++++++++++++++----- src/Chassis/HdMdNxM4kEController.cs | 28 ++++++++++++++++++++--- 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index a040837..4e1b7e1 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -87,11 +87,28 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, { AutoRouteFeedback = new BoolFeedback("autoRouteFeedback", () => _chassis.AutoRouteOnFeedback.BoolValue); } + + if (InputNames == null) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "InputNames is null. Ensure 'inputs' is defined in the device configuration.", this); + return; + } + + if (OutputNames == null) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "OutputNames is null. Ensure 'outputs' is defined in the device configuration.", this); + return; + } for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) { var index = i; - var inputName = InputNames[index]; + if (!InputNames.TryGetValue(index, out var inputName)) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "No input name defined for input {index}. Using default name.", this, index); + inputName = $"Input {index}"; + InputNames[index] = inputName; + } // _Chassis.Inputs[index].Name.StringValue = inputName; _Chassis.HdmiInputs[index].Name.StringValue = inputName; @@ -110,7 +127,12 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) { var index = i; - var outputName = OutputNames[index]; + if (!OutputNames.TryGetValue(index, out var outputName)) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "No output name defined for output {index}. Using default name.", this, index); + outputName = $"Output {index}"; + OutputNames[index] = outputName; + } //_Chassis.Outputs[index].Name.StringValue = outputName; //_Chassis.HdmiOutputs[index].Name.StringValue = outputName; @@ -338,11 +360,11 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); DeviceNameFeedback.LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); - if (_Chassis != null) + if (_Chassis != null && _Chassis is HdMd4x14kzE _chassis) { - trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _Chassis.AutoRouteOn()); - trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _Chassis.AutoRouteOff()); - AutoRouteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); + trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoRouteOn()); + trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoRouteOff()); + AutoRouteFeedback?.LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); } for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) diff --git a/src/Chassis/HdMdNxM4kEController.cs b/src/Chassis/HdMdNxM4kEController.cs index 21c4c30..2ead7e9 100644 --- a/src/Chassis/HdMdNxM4kEController.cs +++ b/src/Chassis/HdMdNxM4kEController.cs @@ -88,10 +88,27 @@ public HdMdNxM4kEBridgeableController(string key, string name, HdMdNxM chassis, AutoRouteFeedback = new BoolFeedback("autoRouteFeedback", () => _chssis.AutoModeOnFeedback.BoolValue); } + if (InputNames == null) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "InputNames is null. Ensure 'inputs' is defined in the device configuration.", this); + return; + } + + if (OutputNames == null) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "OutputNames is null. Ensure 'outputs' is defined in the device configuration.", this); + return; + } + for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) { var index = i; - var inputName = InputNames[index]; + if (!InputNames.TryGetValue(index, out var inputName)) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "No input name defined for input {index}. Using default name.", this, index); + inputName = $"Input {index}"; + InputNames[index] = inputName; + } //_Chassis.Inputs[index].Name.StringValue = inputName; _Chassis.HdmiInputs[index].Name.StringValue = inputName; @@ -110,7 +127,12 @@ public HdMdNxM4kEBridgeableController(string key, string name, HdMdNxM chassis, for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) { var index = i; - var outputName = OutputNames[index]; + if (!OutputNames.TryGetValue(index, out var outputName)) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "No output name defined for output {index}. Using default name.", this, index); + outputName = $"Output {index}"; + OutputNames[index] = outputName; + } //_Chassis.Outputs[index].Name.StringValue = outputName; //_Chassis.HdmiOutputs[index].Name.StringValue = outputName; @@ -336,7 +358,7 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join { trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoModeOn()); trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoModeOff()); - AutoRouteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); + AutoRouteFeedback?.LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); } for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) From 9420ea42652805894f5e6fffa00e162e09996086 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Fri, 16 Jan 2026 14:07:52 -0600 Subject: [PATCH 07/50] refactor: add NoRouteText property and update output route name feedback handling in HdMdNxM4kEController --- src/Chassis/HdMdNxM4KzEController.cs | 5 ++++- src/Chassis/HdMdNxM4kEController.cs | 5 ++++- src/Config/HdMdNxM4kEPropertiesConfig.cs | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 4e1b7e1..9894763 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -37,6 +37,7 @@ public class HdMdNxM4kZEController : CrestronGenericBridgeableBaseDevice, IRouti public FeedbackCollection InputHdcpEnableFeedback { get; private set; } public StringFeedback DeviceNameFeedback { get; private set; } public BoolFeedback AutoRouteFeedback { get; private set; } + public string NoRouteText { get; private set; } #region Constructor @@ -53,6 +54,8 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, return; } + NoRouteText = props.NoRouteText ?? "None"; + if (props.Inputs != null) { @@ -143,7 +146,7 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, }); VideoOutputRouteFeedbacks.Add(new IntFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback == null ? 0 : (int)_Chassis.Outputs[index].VideoOutFeedback.Number)); OutputNameFeedbacks.Add(new StringFeedback(outputName, () => OutputNames[index])); - OutputRouteNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback.NameFeedback.StringValue)); + OutputRouteNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback == null ? NoRouteText : _Chassis.Outputs[index].VideoOutFeedback.NameFeedback.StringValue)); } _Chassis.DMInputChange += Chassis_DMInputChange; diff --git a/src/Chassis/HdMdNxM4kEController.cs b/src/Chassis/HdMdNxM4kEController.cs index 2ead7e9..9a82897 100644 --- a/src/Chassis/HdMdNxM4kEController.cs +++ b/src/Chassis/HdMdNxM4kEController.cs @@ -37,6 +37,7 @@ public class HdMdNxM4kEBridgeableController : CrestronGenericBridgeableBaseDevic public FeedbackCollection InputHdcpEnableFeedback { get; private set; } public StringFeedback DeviceNameFeedback { get; private set; } public BoolFeedback AutoRouteFeedback { get; private set; } + public string NoRouteText { get; private set; } #region Constructor @@ -53,6 +54,8 @@ public HdMdNxM4kEBridgeableController(string key, string name, HdMdNxM chassis, return; } + NoRouteText = props.NoRouteText ?? "None"; + if (props.Inputs != null) { @@ -143,7 +146,7 @@ public HdMdNxM4kEBridgeableController(string key, string name, HdMdNxM chassis, }); VideoOutputRouteFeedbacks.Add(new IntFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback == null ? 0 : (int)_Chassis.Outputs[index].VideoOutFeedback.Number)); OutputNameFeedbacks.Add(new StringFeedback(outputName, () => OutputNames[index])); - OutputRouteNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback.NameFeedback.StringValue)); + OutputRouteNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback == null ? NoRouteText : _Chassis.Outputs[index].VideoOutFeedback.NameFeedback.StringValue)); } _Chassis.DMInputChange += Chassis_DMInputChange; diff --git a/src/Config/HdMdNxM4kEPropertiesConfig.cs b/src/Config/HdMdNxM4kEPropertiesConfig.cs index 5577429..d7f43d5 100644 --- a/src/Config/HdMdNxM4kEPropertiesConfig.cs +++ b/src/Config/HdMdNxM4kEPropertiesConfig.cs @@ -17,6 +17,9 @@ public class HdMdNxM4kEPropertiesConfig [JsonProperty("control")] public ControlPropertiesConfig Control { get; set; } + [JsonProperty("noRouteText")] + public string NoRouteText { get; set; } + [JsonProperty("inputs")] public Dictionary Inputs { get; set; } From e434621d865e3e89b746422107968ba6861fb8fb Mon Sep 17 00:00:00 2001 From: Jason DeVito Date: Fri, 16 Jan 2026 14:27:16 -0600 Subject: [PATCH 08/50] ci(force-patch): increment patch by force From 3d820dafc64be0aaf47e6d42fe26e42665a80ad1 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Mon, 19 Jan 2026 08:27:57 -0600 Subject: [PATCH 09/50] fix: update feedback and chassis implementation - improve chassis null handling - update feedback collection methods --- src/Chassis/HdMdNxM4KzEController.cs | 73 ++++++++++++++++++------- src/Chassis/HdMdNxM4kEController.cs | 79 +++++++++++++++++++--------- 2 files changed, 108 insertions(+), 44 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 9894763..994fc29 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -45,8 +45,13 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, HdMdNxM4kEPropertiesConfig props) : base(key, name, chassis) { + Name = name; _Chassis = chassis; - Name = name; + if(_Chassis == null) + { + Debug.LogDebug(this, "HdMdNxM4kZEController chassis is null, failed to build the device"); + return; + } if (props == null) { @@ -88,7 +93,7 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, if (_Chassis is HdMd4x14kzE _chassis) { - AutoRouteFeedback = new BoolFeedback("autoRouteFeedback", () => _chassis.AutoRouteOnFeedback.BoolValue); + AutoRouteFeedback = new BoolFeedback("AutoRouteFeedback", () => _chassis.AutoRouteOnFeedback.BoolValue); } if (InputNames == null) @@ -215,16 +220,42 @@ public void DisableAutoRoute() public void AddFeedbackCollections() { - AddFeedbackToList(DeviceNameFeedback); - AddCollectionsToList(VideoInputSyncFeedbacks, InputHdcpEnableFeedback); - AddCollectionsToList(VideoOutputRouteFeedbacks); - AddCollectionsToList(InputNameFeedbacks, OutputNameFeedbacks, OutputRouteNameFeedbacks); + // AddFeedbackToList(DeviceNameFeedback); + // AddCollectionsToList(VideoInputSyncFeedbacks, InputHdcpEnableFeedback); + // AddCollectionsToList(VideoOutputRouteFeedbacks); + // AddCollectionsToList(InputNameFeedbacks, OutputNameFeedbacks, OutputRouteNameFeedbacks); + + AddFeedbackToList(DeviceNameFeedback); + foreach (var fb in VideoInputSyncFeedbacks) + { + AddFeedbackToList(fb); + } + foreach (var fb in InputHdcpEnableFeedback) + { + AddFeedbackToList(fb); + } + foreach (var fb in VideoOutputRouteFeedbacks) + { + AddFeedbackToList(fb); + } + foreach (var fb in InputNameFeedbacks) + { + AddFeedbackToList(fb); + } + foreach (var fb in OutputNameFeedbacks) + { + AddFeedbackToList(fb); + } + foreach (var fb in OutputRouteNameFeedbacks) + { + AddFeedbackToList(fb); + } } #endregion #region FeedbackCollection Methods - +/* //Add arrays of collections public void AddCollectionsToList(params FeedbackCollection[] newFbs) { @@ -232,7 +263,7 @@ public void AddCollectionsToList(params FeedbackCollection[] newFb { foreach (var item in newFbs) { - AddCollectionToList(item); + AddFeedbackToList(item); } } } @@ -242,7 +273,7 @@ public void AddCollectionsToList(params FeedbackCollection[] newFbs { foreach (var item in newFbs) { - AddCollectionToList(item); + AddFeedbackToList(item); } } } @@ -253,7 +284,7 @@ public void AddCollectionsToList(params FeedbackCollection[] new { foreach (var item in newFbs) { - AddCollectionToList(item); + AddFeedbackToList(item); } } } @@ -261,9 +292,9 @@ public void AddCollectionsToList(params FeedbackCollection[] new //Add Collections public void AddCollectionToList(FeedbackCollection newFbs) { - foreach (var f in newFbs) + foreach (var f in newFbs.Where(f => f != null)) { - if (f == null) continue; + //if (f == null) continue; AddFeedbackToList(f); } @@ -271,9 +302,9 @@ public void AddCollectionToList(FeedbackCollection newFbs) public void AddCollectionToList(FeedbackCollection newFbs) { - foreach (var f in newFbs) + foreach (var f in newFbs.Where(f => f != null)) { - if (f == null) continue; + //if (f == null) continue; AddFeedbackToList(f); } @@ -281,14 +312,14 @@ public void AddCollectionToList(FeedbackCollection newFbs) public void AddCollectionToList(FeedbackCollection newFbs) { - foreach (var f in newFbs) + foreach (var f in newFbs.Where(f => f != null)) { - if (f == null) continue; + //if (f == null) continue; AddFeedbackToList(f); } } - +*/ //Add Individual Feedbacks public void AddFeedbackToList(PepperDash.Essentials.Core.Feedback newFb) { @@ -363,7 +394,7 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); DeviceNameFeedback.LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); - if (_Chassis != null && _Chassis is HdMd4x14kzE _chassis) + if (_Chassis is HdMd4x14kzE _chassis) { trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoRouteOn()); trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoRouteOff()); @@ -425,8 +456,10 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice feedback.FireUpdate(); } - if (_Chassis != null) - AutoRouteFeedback.FireUpdate(); + if (_Chassis is HdMd4x14kzE) + { + AutoRouteFeedback.FireUpdate(); + } } void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) diff --git a/src/Chassis/HdMdNxM4kEController.cs b/src/Chassis/HdMdNxM4kEController.cs index 9a82897..2f61749 100644 --- a/src/Chassis/HdMdNxM4kEController.cs +++ b/src/Chassis/HdMdNxM4kEController.cs @@ -18,7 +18,7 @@ namespace PepperDash.Essentials.DM.Chassis [Description("Wrapper class for all HdMdNxM4E switchers")] public class HdMdNxM4kEBridgeableController : CrestronGenericBridgeableBaseDevice, IRoutingNumericWithFeedback, IHasFeedback { - private HdMdNxM _Chassis; + private readonly HdMdNxM _Chassis; //IroutingNumericEvent public event EventHandler NumericSwitchChange; @@ -45,12 +45,17 @@ public HdMdNxM4kEBridgeableController(string key, string name, HdMdNxM chassis, HdMdNxM4kEPropertiesConfig props) : base(key, name, chassis) { + Name = name; _Chassis = chassis; - Name = name; + if(_Chassis == null) + { + Debug.LogDebug(this, "HdMdNxM4kEBridgeableController chassis is null, failed to build the device"); + return; + } if (props == null) { - Debug.LogDebug(this, "HdMdNx4keBridgeableController properties are null, failed to build the device"); + Debug.LogDebug(this, "HdMdNxM4kEBridgeableController properties are null, failed to build the device"); return; } @@ -74,7 +79,7 @@ public HdMdNxM4kEBridgeableController(string key, string name, HdMdNxM chassis, OutputNames = props.Outputs; } - DeviceNameFeedback = new StringFeedback(()=>Name); + DeviceNameFeedback = new StringFeedback("DeviceNameFeedback",()=>Name); VideoInputSyncFeedbacks = new FeedbackCollection(); VideoOutputRouteFeedbacks = new FeedbackCollection(); @@ -88,7 +93,7 @@ public HdMdNxM4kEBridgeableController(string key, string name, HdMdNxM chassis, if(_Chassis is HdMd4x14kE _chssis) { - AutoRouteFeedback = new BoolFeedback("autoRouteFeedback", () => _chssis.AutoModeOnFeedback.BoolValue); + AutoRouteFeedback = new BoolFeedback("AutoRouteFeedback", () => _chssis.AutoModeOnFeedback.BoolValue); } if (InputNames == null) @@ -209,16 +214,42 @@ public void DisableAutoRoute() public void AddFeedbackCollections() { - AddFeedbackToList(DeviceNameFeedback); - AddCollectionsToList(VideoInputSyncFeedbacks, InputHdcpEnableFeedback); - AddCollectionsToList(VideoOutputRouteFeedbacks); - AddCollectionsToList(InputNameFeedbacks, OutputNameFeedbacks, OutputRouteNameFeedbacks); + // AddFeedbackToList(DeviceNameFeedback); + // AddCollectionsToList(VideoInputSyncFeedbacks, InputHdcpEnableFeedback); + // AddCollectionsToList(VideoOutputRouteFeedbacks); + // AddCollectionsToList(InputNameFeedbacks, OutputNameFeedbacks, OutputRouteNameFeedbacks); + + AddFeedbackToList(DeviceNameFeedback); + foreach (var fb in VideoInputSyncFeedbacks) + { + AddFeedbackToList(fb); + } + foreach (var fb in InputHdcpEnableFeedback) + { + AddFeedbackToList(fb); + } + foreach (var fb in VideoOutputRouteFeedbacks) + { + AddFeedbackToList(fb); + } + foreach (var fb in InputNameFeedbacks) + { + AddFeedbackToList(fb); + } + foreach (var fb in OutputNameFeedbacks) + { + AddFeedbackToList(fb); + } + foreach (var fb in OutputRouteNameFeedbacks) + { + AddFeedbackToList(fb); + } } #endregion #region FeedbackCollection Methods - +/* //Add arrays of collections public void AddCollectionsToList(params FeedbackCollection[] newFbs) { @@ -226,7 +257,7 @@ public void AddCollectionsToList(params FeedbackCollection[] newFb { foreach (var item in newFbs) { - AddCollectionToList(item); + AddFeedbackToList(item); } } } @@ -236,7 +267,7 @@ public void AddCollectionsToList(params FeedbackCollection[] newFbs { foreach (var item in newFbs) { - AddCollectionToList(item); + AddFeedbackToList(item); } } } @@ -247,7 +278,7 @@ public void AddCollectionsToList(params FeedbackCollection[] new { foreach (var item in newFbs) { - AddCollectionToList(item); + AddFeedbackToList(item); } } } @@ -255,9 +286,9 @@ public void AddCollectionsToList(params FeedbackCollection[] new //Add Collections public void AddCollectionToList(FeedbackCollection newFbs) { - foreach (var f in newFbs) + foreach (var f in newFbs.Where(f => f != null)) { - if (f == null) continue; + //if (f == null) continue; AddFeedbackToList(f); } @@ -265,9 +296,9 @@ public void AddCollectionToList(FeedbackCollection newFbs) public void AddCollectionToList(FeedbackCollection newFbs) { - foreach (var f in newFbs) + foreach (var f in newFbs.Where(f => f != null)) { - if (f == null) continue; + //if (f == null) continue; AddFeedbackToList(f); } @@ -275,14 +306,14 @@ public void AddCollectionToList(FeedbackCollection newFbs) public void AddCollectionToList(FeedbackCollection newFbs) { - foreach (var f in newFbs) + foreach (var f in newFbs.Where(f => f != null)) { - if (f == null) continue; + //if (f == null) continue; AddFeedbackToList(f); } } - +*/ //Add Individual Feedbacks public void AddFeedbackToList(PepperDash.Essentials.Core.Feedback newFb) { @@ -357,7 +388,7 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); DeviceNameFeedback.LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); - if (_Chassis != null && _Chassis is HdMd4x14kE _chassis) + if (_Chassis is HdMd4x14kE _chassis) { trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoModeOn()); trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoModeOff()); @@ -419,9 +450,9 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice feedback.FireUpdate(); } - if(_Chassis == null && _Chassis is HdMd4x14kE _chassis) - { - AutoRouteFeedback.FireUpdate(); + if(_Chassis is HdMd4x14kE) + { + AutoRouteFeedback?.FireUpdate(); } } From 6d0bb96abc05232e22450d860a16a2d041c4628a Mon Sep 17 00:00:00 2001 From: jkdevito Date: Mon, 19 Jan 2026 08:30:12 -0600 Subject: [PATCH 10/50] fix: rename hdmdnxm4ke controller class - Removed `Bridgeable` from controller class name --- src/Chassis/HdMdNxM4kEController.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Chassis/HdMdNxM4kEController.cs b/src/Chassis/HdMdNxM4kEController.cs index 2f61749..c9de40d 100644 --- a/src/Chassis/HdMdNxM4kEController.cs +++ b/src/Chassis/HdMdNxM4kEController.cs @@ -16,7 +16,7 @@ namespace PepperDash.Essentials.DM.Chassis { [Description("Wrapper class for all HdMdNxM4E switchers")] - public class HdMdNxM4kEBridgeableController : CrestronGenericBridgeableBaseDevice, IRoutingNumericWithFeedback, IHasFeedback + public class HdMdNxM4kEController : CrestronGenericBridgeableBaseDevice, IRoutingNumericWithFeedback, IHasFeedback { private readonly HdMdNxM _Chassis; @@ -41,7 +41,7 @@ public class HdMdNxM4kEBridgeableController : CrestronGenericBridgeableBaseDevic #region Constructor - public HdMdNxM4kEBridgeableController(string key, string name, HdMdNxM chassis, + public HdMdNxM4kEController(string key, string name, HdMdNxM chassis, HdMdNxM4kEPropertiesConfig props) : base(key, name, chassis) { @@ -520,7 +520,7 @@ void Chassis_DMInputChange(Switch device, DMInputEventArgs args) #region Factory - public class HdMdNxM4kEControllerFactory : EssentialsPluginDeviceFactory + public class HdMdNxM4kEControllerFactory : EssentialsPluginDeviceFactory { public HdMdNxM4kEControllerFactory() { @@ -543,11 +543,11 @@ public override EssentialsDevice BuildDevice(DeviceConfig dc) { case ("hdmd4x14ke-bridgeable"): case ("hdmd4x14ke"): - return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd4x14kE(ipid, address, Global.ControlSystem), props); + return new HdMdNxM4kEController(dc.Key, dc.Name, new HdMd4x14kE(ipid, address, Global.ControlSystem), props); case ("hdmd4x24ke"): - return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd4x24kE(ipid, address, Global.ControlSystem), props); + return new HdMdNxM4kEController(dc.Key, dc.Name, new HdMd4x24kE(ipid, address, Global.ControlSystem), props); case ("hdmd6x24ke"): - return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd6x24kE(ipid, address, Global.ControlSystem), props); + return new HdMdNxM4kEController(dc.Key, dc.Name, new HdMd6x24kE(ipid, address, Global.ControlSystem), props); default: return null; } From f1e6a3b91008f1caac4e24f13c00fc6b498a837a Mon Sep 17 00:00:00 2001 From: jkdevito Date: Mon, 19 Jan 2026 08:35:40 -0600 Subject: [PATCH 11/50] feat: add Key and Name properties - Added Key and Name to DmCardAudioOutputController and DmpsAudioOutputController to resolve plugin factory exceptions: ``` Loading plugin factory: PepperDash.Essentials.DM Load Plugin not found. PepperDash.Essentials.DM.DmpsAudioOutputWithMixerAndEq is not a plugin factory. Exception: Could not set up parent class, due to: Parent class vtable failed to initialize, due to: VTable setup of type PepperDash.Essentials.DM.DmpsAudioOutput failed assembly:/user/program1/plugins/loadedAssemblies/PepperDash.Essentials.DM.dll type:DmpsAudioOutput member:(null) assembly:/user/program1/plugins/loadedAssemblies/PepperDash.Essentials.DM.dll type:DmpsAudioOutputWithMixer member:(null) Load Plugin not found. PepperDash.Essentials.DM.DmpsAudioOutputWithMixer is not a plugin factory. Exception: Parent class vtable failed to initialize, due to: VTable setup of type PepperDash.Essentials.DM.DmpsAudioOutput failed assembly:/user/program1/plugins/loadedAssemblies/PepperDash.Essentials.DM.dll type:DmpsAudioOutput member:(null) Load Plugin not found. PepperDash.Essentials.DM.DmpsAudioOutput is not a plugin factory. Exception: VTable setup of type PepperDash.Essentials.DM.DmpsAudioOutput failed ``` --- src/Chassis/DmCardAudioOutput.cs | 5 +++++ src/Chassis/DmpsAudioOutputController.cs | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/Chassis/DmCardAudioOutput.cs b/src/Chassis/DmCardAudioOutput.cs index 3d2469d..ef4beeb 100644 --- a/src/Chassis/DmCardAudioOutput.cs +++ b/src/Chassis/DmCardAudioOutput.cs @@ -14,6 +14,9 @@ public class DmCardAudioOutputController : IBasicVolumeWithFeedback { public Audio.Output Output { get; private set; } + public string Key { get; private set; } + public string Name { get; private set; } + public IntFeedback VolumeLevelFeedback { get; private set; } public BoolFeedback MuteFeedback { get; private set; } @@ -24,6 +27,8 @@ public class DmCardAudioOutputController : IBasicVolumeWithFeedback public DmCardAudioOutputController(Audio.Output output) { Output = output; + Key = string.Format("DmCardAudioOutput-{0}", output.Number); + Name = Key; VolumeLevelFeedback = new IntFeedback(() => Output.VolumeFeedback.UShortValue); MuteFeedback = new BoolFeedback(() => IsMuted); } diff --git a/src/Chassis/DmpsAudioOutputController.cs b/src/Chassis/DmpsAudioOutputController.cs index 995984b..f83c598 100644 --- a/src/Chassis/DmpsAudioOutputController.cs +++ b/src/Chassis/DmpsAudioOutputController.cs @@ -352,6 +352,8 @@ public class DmpsAudioOutput : IBasicVolumeWithFeedback protected short MinLevel { get; set; } protected short MaxLevel { get; set; } + public string Key { get; private set; } + public string Name { get; private set; } public eDmpsLevelType Type { get; private set; } public BoolFeedback MuteFeedback { get; private set; } public IntFeedback VolumeLevelFeedback { get; private set; } @@ -367,6 +369,8 @@ public DmpsAudioOutput(Dmps3AudioOutputBase output, eDmpsLevelType type) VolumeLevelInput = 0; EnableVolumeSend = false; Type = type; + Key = string.Format("DmpsAudioOutput-{0}", type); + Name = Key; MinLevel = -800; MaxLevel = 100; From 1e6a2c4be2e49243deaa0cdec30e1d31d5cad804 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 22 Jan 2026 07:22:36 -0600 Subject: [PATCH 12/50] fix: add null checks for feedback properties in HdMdNxM4KzEController --- src/Chassis/HdMdNxM4KzEController.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 994fc29..8a79322 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -93,7 +93,7 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, if (_Chassis is HdMd4x14kzE _chassis) { - AutoRouteFeedback = new BoolFeedback("AutoRouteFeedback", () => _chassis.AutoRouteOnFeedback.BoolValue); + AutoRouteFeedback = new BoolFeedback("AutoRouteFeedback", () => _chassis.AutoRouteOnFeedback?.BoolValue ?? false); } if (InputNames == null) @@ -126,10 +126,10 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, FeedbackMatchObject = _Chassis.HdmiInputs[index] }); - VideoInputSyncFeedbacks.Add(new BoolFeedback(inputName, () => _Chassis.Inputs[index].VideoDetectedFeedback.BoolValue)); + VideoInputSyncFeedbacks.Add(new BoolFeedback(inputName, () => _Chassis.Inputs[index].VideoDetectedFeedback?.BoolValue ?? false)); //InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].NameFeedback.StringValue)); InputNameFeedbacks.Add(new StringFeedback(inputName, () => InputNames[index])); - InputHdcpEnableFeedback.Add(new BoolFeedback(inputName, () => _Chassis.HdmiInputs[index].HdmiInputPort.HdcpSupportOnFeedback.BoolValue)); + InputHdcpEnableFeedback.Add(new BoolFeedback(inputName, () => _Chassis.HdmiInputs[index].HdmiInputPort.HdcpSupportOnFeedback?.BoolValue ?? false)); } for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) From 6c7977390b9aa931f48c87cb530006a3848eaf15 Mon Sep 17 00:00:00 2001 From: Jason DeVito Date: Thu, 22 Jan 2026 09:58:04 -0600 Subject: [PATCH 13/50] fix: updates to feedback collections --- src/Chassis/HdMdNxM4KzEController.cs | 191 +++++++++++++++++---------- 1 file changed, 118 insertions(+), 73 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 8a79322..6a07c41 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -12,6 +12,7 @@ using PepperDash.Essentials.DM.Config; using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Config; +using System.Threading; namespace PepperDash.Essentials.DM.Chassis { @@ -159,10 +160,10 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, AddPostActivationAction(AddFeedbackCollections); } - + #endregion - #region Methods + #region Methods /// /// Raise an event when the status of a switch object changes. @@ -225,105 +226,83 @@ public void AddFeedbackCollections() // AddCollectionsToList(VideoOutputRouteFeedbacks); // AddCollectionsToList(InputNameFeedbacks, OutputNameFeedbacks, OutputRouteNameFeedbacks); + // TODO - Remove after testing + //Debug.LogInformation(this, $"AddFeedbackCollections: VideoInputSyncFeedbacks has {VideoInputSyncFeedbacks.Count} FBs"); + //Debug.LogInformation(this, $"AddFeedbackCollections: InputHdcpEnableFeedback has {InputHdcpEnableFeedback.Count} FBs"); + //Debug.LogInformation(this, $"AddFeedbackCollections: VideoOutputRouteFeedbacks has {VideoOutputRouteFeedbacks.Count} FBs"); + //Debug.LogInformation(this, $"AddFeedbackCollections: InputNameFeedbacks has {InputNameFeedbacks.Count} FBs"); + //Debug.LogInformation(this, $"AddFeedbackCollections: OutputNameFeedbacks has {OutputNameFeedbacks.Count} FBs"); + //Debug.LogInformation(this, $"AddFeedbackCollections: OutputRouteNameFeedbacks has {OutputRouteNameFeedbacks.Count} FBs"); + AddFeedbackToList(DeviceNameFeedback); foreach (var fb in VideoInputSyncFeedbacks) { + // TODO - Remove after testing + //Debug.LogInformation(this, $"AddFeedbackCollections: adding VideoInputSyncFeedbaks {fb.Key} to collection"); + AddFeedbackToList(fb); } foreach (var fb in InputHdcpEnableFeedback) { + // TODO - Remove after testing + //Debug.LogInformation(this, $"AddFeedbackCollections: adding InputHdcpEnableFeedback {fb.Key} to collection"); + AddFeedbackToList(fb); } foreach (var fb in VideoOutputRouteFeedbacks) { + // TODO - Remove after testing + //Debug.LogInformation(this, $"AddFeedbackCollections: adding VideoOutputRouteFeedbacks {fb.Key} to collection"); + AddFeedbackToList(fb); } foreach (var fb in InputNameFeedbacks) { + // TODO - Remove after testing + //Debug.LogInformation(this, $"AddFeedbackCollections: adding InputNameFeedbacks {fb.Key} to collection"); + AddFeedbackToList(fb); } foreach (var fb in OutputNameFeedbacks) { + // TODO - Remove after testing + //Debug.LogInformation(this, $"AddFeedbackCollections: adding OutputNameFeedbacks {fb.Key} to collection"); + AddFeedbackToList(fb); } foreach (var fb in OutputRouteNameFeedbacks) { + // TODO - Remove after testing + //Debug.LogInformation(this, $"AddFeedbackCollections: adding OutputRouteNameFeedbacks {fb.Key} to collection"); + AddFeedbackToList(fb); - } - } - - #endregion - - #region FeedbackCollection Methods -/* - //Add arrays of collections - public void AddCollectionsToList(params FeedbackCollection[] newFbs) - { - foreach (FeedbackCollection fbCollection in newFbs) - { - foreach (var item in newFbs) - { - AddFeedbackToList(item); - } - } - } - public void AddCollectionsToList(params FeedbackCollection[] newFbs) - { - foreach (FeedbackCollection fbCollection in newFbs) - { - foreach (var item in newFbs) - { - AddFeedbackToList(item); - } } - } - public void AddCollectionsToList(params FeedbackCollection[] newFbs) - { - foreach (FeedbackCollection fbCollection in newFbs) + Debug.LogInformation(this, $"AddFeedbackCollections: Feedbacks contains {Feedbacks.Count} items"); + foreach(var fb in Feedbacks) { - foreach (var item in newFbs) - { - AddFeedbackToList(item); - } + // TODO - Remove after testing + Debug.LogInformation(this, $"AddFeedbackCollections: Feedbacks = {fb.Key}"); + } } - //Add Collections - public void AddCollectionToList(FeedbackCollection newFbs) - { - foreach (var f in newFbs.Where(f => f != null)) - { - //if (f == null) continue; + #endregion - AddFeedbackToList(f); - } - } + #region FeedbackCollection Methods - public void AddCollectionToList(FeedbackCollection newFbs) + //Add Individual Feedbacks + public void AddFeedbackToList(PepperDash.Essentials.Core.Feedback newFb) { - foreach (var f in newFbs.Where(f => f != null)) + if (newFb == null) { - //if (f == null) continue; - - AddFeedbackToList(f); + // TODO - Remove after testing + Debug.LogInformation(this, $"AddFeedbackToList: newFb is null"); + return; } - } - public void AddCollectionToList(FeedbackCollection newFbs) - { - foreach (var f in newFbs.Where(f => f != null)) - { - //if (f == null) continue; - - AddFeedbackToList(f); - } - } -*/ - //Add Individual Feedbacks - public void AddFeedbackToList(PepperDash.Essentials.Core.Feedback newFb) - { - if (newFb == null) return; + // TODO - Remove after testing + Debug.LogInformation(this, $"AddFeedbackToList: adding {newFb.Key} "); if (!Feedbacks.Contains(newFb)) { @@ -359,10 +338,12 @@ public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingS public void ExecuteNumericSwitch(ushort inputSelector, ushort outputSelector, eRoutingSignalType signalType) { - var input = inputSelector == 0 ? null : _Chassis.HdmiInputs[inputSelector]; + Debug.LogInformation(this, $"ExecuteNumericSwitch: inputSelector={inputSelector} outputSelector={outputSelector}"); + + var input = inputSelector == 0 ? null : _Chassis.HdmiInputs[inputSelector]; var output = _Chassis.HdmiOutputs[outputSelector]; - Debug.LogVerbose(this, "ExecuteNumericSwitch: input={0} output={1}", input, output); + Debug.LogVerbose(this, $"ExecuteNumericSwitch: input={input} output={output}"); ExecuteSwitch(input, output, signalType); } @@ -396,6 +377,8 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join if (_Chassis is HdMd4x14kzE _chassis) { + Debug.LogInformation(this, $"LinkToApi: _Chassis is HdMd4x14kzE, setting up AutoRoute links"); + trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoRouteOn()); trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoRouteOff()); AutoRouteFeedback?.LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); @@ -405,6 +388,9 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join { var joinIndex = i - 1; var input = i; + + Debug.LogInformation(this, $"LinkToApi: _Chassis.NumberOfInputs > input = {input}, joinIndex = {joinIndex}"); + //Digital VideoInputSyncFeedbacks[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinMap.InputSync.JoinNumber + joinIndex]); InputHdcpEnableFeedback[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinMap.EnableInputHdcp.JoinNumber + joinIndex]); @@ -420,6 +406,9 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join { var joinIndex = i - 1; var output = i; + + Debug.LogInformation(this, $"LinkToApi: _Chassis.NumberOfOutputs > output = {output}, joinIndex = {joinIndex}"); + //Analog VideoOutputRouteFeedbacks[OutputNames[output]].LinkInputSig(trilist.UShortInput[joinMap.OutputRoute.JoinNumber + joinIndex]); trilist.SetUShortSigAction(joinMap.OutputRoute.JoinNumber + joinIndex, (a) => ExecuteNumericSwitch(a, (ushort) output, eRoutingSignalType.AudioVideo)); @@ -441,18 +430,72 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join } + private void UpdateFeedbacks() + { + IsOnline?.FireUpdate(); + DeviceNameFeedback?.FireUpdate(); + AutoRouteFeedback?.FireUpdate(); + + foreach(var item in VideoInputSyncFeedbacks) + { + item.FireUpdate(); + } + + foreach(var item in VideoOutputRouteFeedbacks) + { + item.FireUpdate(); + } + + foreach(var item in InputHdcpEnableFeedback) + { + item.FireUpdate(); + } + + foreach(var item in InputNameFeedbacks) + { + item.FireUpdate(); + } + + foreach(var item in OutputNameFeedbacks) + { + item.FireUpdate(); + } + + foreach(var item in OutputRouteNameFeedbacks) + { + item.FireUpdate(); + } + + foreach(var item in Feedbacks) + { + // TODO - Remove after testing + Debug.LogInformation(this, $"UpdateFeedbacks: Firing feedback for {item.Key}"); + item.FireUpdate(); + } + } + #endregion #region Events void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice, Crestron.SimplSharpPro.OnlineOfflineEventArgs args) { + // TODO - Remove after testing + Debug.LogInformation(this, $"Chassis_OnlineStatusChange: DeviceOnline = {args.DeviceOnLine}"); + IsOnline.FireUpdate(); if (!args.DeviceOnLine) return; + + // TODO - Remove after testing + Debug.LogInformation(this, $"Chassis_OnlineStatusChange: Feedbacks has {Feedbacks.Count} items in the collection"); + foreach (var feedback in Feedbacks) { + // TODO - Remove after testing + Debug.LogInformation(this, $"Chassis_OnlineStatusChange: Firing update for {feedback.Key}"); + feedback.FireUpdate(); } @@ -466,6 +509,9 @@ void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) { if (args.EventId != DMOutputEventIds.VideoOutEventId) return; + // TODO - Remove after testing + Debug.LogInformation(this, $"Chassis_DMOutputChange: EventId = {args.EventId}; Index = {args.Index}; Number = {args.Number}; Stream = {args.Stream} "); + var output = args.Number; var inputNumber = _Chassis.HdmiOutputs[output].VideoOutFeedback == null @@ -493,20 +539,19 @@ void Chassis_DMInputChange(Switch device, DMInputEventArgs args) { case DMInputEventIds.VideoDetectedEventId: { - Debug.LogDebug(this, "Event ID {0}: Updating VideoInputSyncFeedbacks", args.EventId); + Debug.LogDebug(this, $"Chassis_DMInputChange: Event ID {args.EventId}: Updating VideoInputSyncFeedbacks"); foreach (var item in VideoInputSyncFeedbacks) { item.FireUpdate(); } break; - } + } case DMInputEventIds.InputNameFeedbackEventId: case DMInputEventIds.InputNameEventId: case DMInputEventIds.NameFeedbackEventId: { - Debug.LogDebug(this, "Event ID {0}: Updating name feedbacks.", args.EventId); - Debug.LogDebug(this, "Input {0} Name {1}", args.Number, - _Chassis.HdmiInputs[args.Number].NameFeedback.StringValue); + Debug.LogDebug(this, $"Chassis_DMInputChange: Event ID {args.EventId}: Updating name feedbacks."); + Debug.LogDebug(this, $"Chassis_DMInputChange: Input {args.Number} Name {_Chassis.HdmiInputs[args.Number].NameFeedback.StringValue}"); foreach (var item in InputNameFeedbacks) { item.FireUpdate(); @@ -515,7 +560,7 @@ void Chassis_DMInputChange(Switch device, DMInputEventArgs args) } default: { - Debug.LogDebug(this, "Unhandled DM Input Event ID {0}", args.EventId); + Debug.LogDebug(this, $"Chassis_DMInputChange: Unhandled DM Input Event ID {args.EventId}"); break; } } From dea90c2fbe2e8f38209f13e186d1931af2cdb31b Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 22 Jan 2026 10:18:49 -0600 Subject: [PATCH 14/50] refactor: improve code formatting and readability in HdMdNxM4kZEController --- src/Chassis/HdMdNxM4KzEController.cs | 360 ++++++++++++--------------- 1 file changed, 166 insertions(+), 194 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 6a07c41..d425025 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -18,10 +18,10 @@ namespace PepperDash.Essentials.DM.Chassis { [Description("Wrapper class for all HdMdNxM4ZE switchers")] public class HdMdNxM4kZEController : CrestronGenericBridgeableBaseDevice, IRoutingNumericWithFeedback, IHasFeedback - { - private HdMdNxM4kzE _Chassis; - - //IroutingNumericEvent + { + private HdMdNxM4kzE _Chassis; + + //IroutingNumericEvent public event EventHandler NumericSwitchChange; public Dictionary InputNames { get; set; } @@ -37,7 +37,7 @@ public class HdMdNxM4kZEController : CrestronGenericBridgeableBaseDevice, IRouti public FeedbackCollection OutputRouteNameFeedbacks { get; private set; } public FeedbackCollection InputHdcpEnableFeedback { get; private set; } public StringFeedback DeviceNameFeedback { get; private set; } - public BoolFeedback AutoRouteFeedback { get; private set; } + public BoolFeedback AutoRouteFeedback { get; private set; } public string NoRouteText { get; private set; } #region Constructor @@ -48,7 +48,7 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, { Name = name; _Chassis = chassis; - if(_Chassis == null) + if (_Chassis == null) { Debug.LogDebug(this, "HdMdNxM4kZEController chassis is null, failed to build the device"); return; @@ -80,7 +80,7 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, OutputNames = props.Outputs; } - DeviceNameFeedback = new StringFeedback("DeviceName", ()=>Name); + DeviceNameFeedback = new StringFeedback("DeviceName", () => Name); VideoInputSyncFeedbacks = new FeedbackCollection(); VideoOutputRouteFeedbacks = new FeedbackCollection(); @@ -88,14 +88,14 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, OutputNameFeedbacks = new FeedbackCollection(); OutputRouteNameFeedbacks = new FeedbackCollection(); InputHdcpEnableFeedback = new FeedbackCollection(); - + InputPorts = new RoutingPortCollection(); OutputPorts = new RoutingPortCollection(); - if (_Chassis is HdMd4x14kzE _chassis) - { - AutoRouteFeedback = new BoolFeedback("AutoRouteFeedback", () => _chassis.AutoRouteOnFeedback?.BoolValue ?? false); - } + if (_Chassis is HdMd4x14kzE _chassis) + { + AutoRouteFeedback = new BoolFeedback("AutoRouteFeedback", () => _chassis.AutoRouteOnFeedback?.BoolValue ?? false); + } if (InputNames == null) { @@ -108,28 +108,28 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "OutputNames is null. Ensure 'outputs' is defined in the device configuration.", this); return; } - - for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) + + for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) { var index = i; - if (!InputNames.TryGetValue(index, out var inputName)) + if (!InputNames.TryGetValue(index, out var inputName)) { Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "No input name defined for input {index}. Using default name.", this, index); inputName = $"Input {index}"; InputNames[index] = inputName; } - // _Chassis.Inputs[index].Name.StringValue = inputName; - _Chassis.HdmiInputs[index].Name.StringValue = inputName; - - InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, _Chassis.HdmiInputs[index], this) - { - FeedbackMatchObject = _Chassis.HdmiInputs[index] - }); - - VideoInputSyncFeedbacks.Add(new BoolFeedback(inputName, () => _Chassis.Inputs[index].VideoDetectedFeedback?.BoolValue ?? false)); - //InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].NameFeedback.StringValue)); - InputNameFeedbacks.Add(new StringFeedback(inputName, () => InputNames[index])); + // _Chassis.Inputs[index].Name.StringValue = inputName; + _Chassis.HdmiInputs[index].Name.StringValue = inputName; + + InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, _Chassis.HdmiInputs[index], this) + { + FeedbackMatchObject = _Chassis.HdmiInputs[index] + }); + + VideoInputSyncFeedbacks.Add(new BoolFeedback(inputName, () => _Chassis.Inputs[index].VideoDetectedFeedback?.BoolValue ?? false)); + //InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].NameFeedback.StringValue)); + InputNameFeedbacks.Add(new StringFeedback(inputName, () => InputNames[index])); InputHdcpEnableFeedback.Add(new BoolFeedback(inputName, () => _Chassis.HdmiInputs[index].HdmiInputPort.HdcpSupportOnFeedback?.BoolValue ?? false)); } @@ -143,7 +143,7 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, OutputNames[index] = outputName; } //_Chassis.Outputs[index].Name.StringValue = outputName; - //_Chassis.HdmiOutputs[index].Name.StringValue = outputName; + //_Chassis.HdmiOutputs[index].Name.StringValue = outputName; OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, _Chassis.HdmiOutputs[index], this) @@ -160,7 +160,7 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, AddPostActivationAction(AddFeedbackCollections); } - + #endregion #region Methods @@ -171,8 +171,7 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, /// Arguments defined as IKeyName sender, output, input, and eRoutingSignalType private void OnSwitchChange(RoutingNumericEventArgs e) { - var newEvent = NumericSwitchChange; - if (newEvent != null) newEvent(this, e); + NumericSwitchChange?.Invoke(this, e); } public void EnableHdcp(uint port) @@ -196,118 +195,83 @@ public void DisableHdcp(uint port) public void EnableAutoRoute() { if (_Chassis.NumberOfOutputs > 1) return; + if (_Chassis is HdMd4x14kzE _chassis) + { + _chassis.AutoRouteOn(); + return; + } - if (!(_Chassis is HdMd4x14kzE _chassis)) - { - return; - } - - _chassis.AutoRouteOn(); + Debug.LogVerbose(this, "EnableAutoRoute: AutoRoute is not supported on this chassis."); } - public void DisableAutoRoute() - { - if (_Chassis.NumberOfOutputs > 1) return; - - if (!(_Chassis is HdMd4x14kzE _chassis)) - { - return; - } + public void DisableAutoRoute() + { + if (_Chassis.NumberOfOutputs > 1) return; + if (_Chassis is HdMd4x14kzE _chassis) + { + _chassis.AutoRouteOff(); + return; + } - _chassis.AutoRouteOff(); + Debug.LogVerbose(this, "DisableAutoRoute: AutoRoute is not supported on this chassis."); } + - #region PostActivate + #region FeedbackCollection Methods public void AddFeedbackCollections() { - // AddFeedbackToList(DeviceNameFeedback); - // AddCollectionsToList(VideoInputSyncFeedbacks, InputHdcpEnableFeedback); - // AddCollectionsToList(VideoOutputRouteFeedbacks); - // AddCollectionsToList(InputNameFeedbacks, OutputNameFeedbacks, OutputRouteNameFeedbacks); + AddFeedbackToList(DeviceNameFeedback); - // TODO - Remove after testing - //Debug.LogInformation(this, $"AddFeedbackCollections: VideoInputSyncFeedbacks has {VideoInputSyncFeedbacks.Count} FBs"); - //Debug.LogInformation(this, $"AddFeedbackCollections: InputHdcpEnableFeedback has {InputHdcpEnableFeedback.Count} FBs"); - //Debug.LogInformation(this, $"AddFeedbackCollections: VideoOutputRouteFeedbacks has {VideoOutputRouteFeedbacks.Count} FBs"); - //Debug.LogInformation(this, $"AddFeedbackCollections: InputNameFeedbacks has {InputNameFeedbacks.Count} FBs"); - //Debug.LogInformation(this, $"AddFeedbackCollections: OutputNameFeedbacks has {OutputNameFeedbacks.Count} FBs"); - //Debug.LogInformation(this, $"AddFeedbackCollections: OutputRouteNameFeedbacks has {OutputRouteNameFeedbacks.Count} FBs"); + if (AutoRouteFeedback != null) + { + AddFeedbackToList(AutoRouteFeedback); + } - AddFeedbackToList(DeviceNameFeedback); foreach (var fb in VideoInputSyncFeedbacks) { - // TODO - Remove after testing - //Debug.LogInformation(this, $"AddFeedbackCollections: adding VideoInputSyncFeedbaks {fb.Key} to collection"); - AddFeedbackToList(fb); } foreach (var fb in InputHdcpEnableFeedback) { - // TODO - Remove after testing - //Debug.LogInformation(this, $"AddFeedbackCollections: adding InputHdcpEnableFeedback {fb.Key} to collection"); - AddFeedbackToList(fb); } foreach (var fb in VideoOutputRouteFeedbacks) { - // TODO - Remove after testing - //Debug.LogInformation(this, $"AddFeedbackCollections: adding VideoOutputRouteFeedbacks {fb.Key} to collection"); - AddFeedbackToList(fb); } foreach (var fb in InputNameFeedbacks) { - // TODO - Remove after testing - //Debug.LogInformation(this, $"AddFeedbackCollections: adding InputNameFeedbacks {fb.Key} to collection"); - AddFeedbackToList(fb); } foreach (var fb in OutputNameFeedbacks) { - // TODO - Remove after testing - //Debug.LogInformation(this, $"AddFeedbackCollections: adding OutputNameFeedbacks {fb.Key} to collection"); - AddFeedbackToList(fb); } foreach (var fb in OutputRouteNameFeedbacks) { - // TODO - Remove after testing - //Debug.LogInformation(this, $"AddFeedbackCollections: adding OutputRouteNameFeedbacks {fb.Key} to collection"); - AddFeedbackToList(fb); } + // TODO - Remove after testing Debug.LogInformation(this, $"AddFeedbackCollections: Feedbacks contains {Feedbacks.Count} items"); - foreach(var fb in Feedbacks) + foreach (var fb in Feedbacks) { // TODO - Remove after testing Debug.LogInformation(this, $"AddFeedbackCollections: Feedbacks = {fb.Key}"); - } } - #endregion - - #region FeedbackCollection Methods - //Add Individual Feedbacks - public void AddFeedbackToList(PepperDash.Essentials.Core.Feedback newFb) + public void AddFeedbackToList(Essentials.Core.Feedback newFb) { - if (newFb == null) - { - // TODO - Remove after testing - Debug.LogInformation(this, $"AddFeedbackToList: newFb is null"); - return; - } + if (newFb == null) return; - // TODO - Remove after testing - Debug.LogInformation(this, $"AddFeedbackToList: adding {newFb.Key} "); + if (Feedbacks.Any(f => f.Key == newFb.Key)) return; - if (!Feedbacks.Contains(newFb)) - { - Feedbacks.Add(newFb); - } + // TODO - Remove after testing + Debug.LogVerbose(this, $"AddFeedbackToList: adding {newFb.Key} to Feedbacks collection"); + Feedbacks.Add(newFb); } #endregion @@ -315,21 +279,21 @@ public void AddFeedbackToList(PepperDash.Essentials.Core.Feedback newFb) #region IRouting Members public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) - { - var input = inputSelector as HdMdNxMHdmiInput; - var output = outputSelector as HdMdNxMHdmiOutput; - Debug.LogVerbose(this, "ExecuteSwitch: input={0} output={1}", input, output); + { + var input = inputSelector as HdMdNxMHdmiInput; + var output = outputSelector as HdMdNxMHdmiOutput; + Debug.LogVerbose(this, "ExecuteSwitch: input={0} output={1}", input, output); - if (output == null) - { - Debug.LogInformation(this, "Unable to make switch. output selector is not HdMdNxMHdmiOutput"); - return; - } + if (output == null) + { + Debug.LogInformation(this, "Unable to make switch. output selector is not HdMdNxMHdmiOutput"); + return; + } // Try to make switch only when necessary. The unit appears to toggle when already selected. var current = output.VideoOut; - if (current != input) - output.VideoOut = input; + if (current != input) + output.VideoOut = input; } #endregion @@ -338,12 +302,12 @@ public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingS public void ExecuteNumericSwitch(ushort inputSelector, ushort outputSelector, eRoutingSignalType signalType) { - Debug.LogInformation(this, $"ExecuteNumericSwitch: inputSelector={inputSelector} outputSelector={outputSelector}"); + Debug.LogInformation(this, $"ExecuteNumericSwitch: inputSelector={inputSelector} outputSelector={outputSelector}"); var input = inputSelector == 0 ? null : _Chassis.HdmiInputs[inputSelector]; - var output = _Chassis.HdmiOutputs[outputSelector]; + var output = _Chassis.HdmiOutputs[outputSelector]; - Debug.LogVerbose(this, $"ExecuteNumericSwitch: input={input} output={output}"); + Debug.LogVerbose(this, $"ExecuteNumericSwitch: input={input} output={output}"); ExecuteSwitch(input, output, signalType); } @@ -377,7 +341,7 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join if (_Chassis is HdMd4x14kzE _chassis) { - Debug.LogInformation(this, $"LinkToApi: _Chassis is HdMd4x14kzE, setting up AutoRoute links"); + Debug.LogInformation(this, $"LinkToApi: _Chassis is HdMd4x14kzE, setting up AutoRoute links > joinNumber = {joinMap.EnableAutoRoute.JoinNumber}"); trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoRouteOn()); trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoRouteOff()); @@ -387,45 +351,53 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) { var joinIndex = i - 1; - var input = i; + var input = i; + + var joinNumberInputSync = joinMap.InputSync.JoinNumber + joinIndex; + var joinNumberEnableInputHdcp = joinMap.EnableInputHdcp.JoinNumber + joinIndex; + var joinNumberDisableInputHdcp = joinMap.DisableInputHdcp.JoinNumber + joinIndex; + var joinNumberInputName = joinMap.InputName.JoinNumber + joinIndex; - Debug.LogInformation(this, $"LinkToApi: _Chassis.NumberOfInputs > input = {input}, joinIndex = {joinIndex}"); + Debug.LogInformation(this, $"LinkToApi: _Chassis.NumberOfInputs > {input} | joinIndex = {joinIndex}, InputSyncJoin = {joinNumberInputSync}, EnableHdcpJoin = {joinNumberEnableInputHdcp}, DisableHdcpJoin = {joinNumberDisableInputHdcp}, InputNameJoin = {joinNumberInputName}"); //Digital - VideoInputSyncFeedbacks[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinMap.InputSync.JoinNumber + joinIndex]); - InputHdcpEnableFeedback[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinMap.EnableInputHdcp.JoinNumber + joinIndex]); - InputHdcpEnableFeedback[InputNames[input]].LinkComplementInputSig(trilist.BooleanInput[joinMap.DisableInputHdcp.JoinNumber + joinIndex]); - trilist.SetSigTrueAction(joinMap.EnableInputHdcp.JoinNumber + joinIndex, () => EnableHdcp(input)); - trilist.SetSigTrueAction(joinMap.DisableInputHdcp.JoinNumber + joinIndex, () => DisableHdcp(input)); + VideoInputSyncFeedbacks[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinNumberInputSync]); + InputHdcpEnableFeedback[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinNumberEnableInputHdcp]); + InputHdcpEnableFeedback[InputNames[input]].LinkComplementInputSig(trilist.BooleanInput[joinNumberDisableInputHdcp]); + trilist.SetSigTrueAction(joinNumberEnableInputHdcp, () => EnableHdcp(input)); + trilist.SetSigTrueAction(joinNumberDisableInputHdcp, () => DisableHdcp(input)); //Serial - InputNameFeedbacks[InputNames[input]].LinkInputSig(trilist.StringInput[joinMap.InputName.JoinNumber + joinIndex]); + InputNameFeedbacks[InputNames[input]].LinkInputSig(trilist.StringInput[joinNumberInputName]); } for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) { var joinIndex = i - 1; - var output = i; + var output = i; - Debug.LogInformation(this, $"LinkToApi: _Chassis.NumberOfOutputs > output = {output}, joinIndex = {joinIndex}"); + var joinNumberOutputRoute = joinMap.OutputRoute.JoinNumber + joinIndex; + var joinNumberOutputName = joinMap.OutputName.JoinNumber + joinIndex; + var joinNumberOutputRouteName = joinMap.OutputRoutedName.JoinNumber + joinIndex; - //Analog - VideoOutputRouteFeedbacks[OutputNames[output]].LinkInputSig(trilist.UShortInput[joinMap.OutputRoute.JoinNumber + joinIndex]); - trilist.SetUShortSigAction(joinMap.OutputRoute.JoinNumber + joinIndex, (a) => ExecuteNumericSwitch(a, (ushort) output, eRoutingSignalType.AudioVideo)); + Debug.LogInformation(this, $"LinkToApi: _Chassis.NumberOfOutputs > {output} | joinIndex = {joinIndex}, OutputRouteJoin = {joinNumberOutputRoute}, OutputNameJoin = {joinNumberOutputName}, OutputRouteNameJoin = {joinNumberOutputRouteName}"); + //Analog + VideoOutputRouteFeedbacks[OutputNames[output]].LinkInputSig(trilist.UShortInput[joinNumberOutputRoute]); + trilist.SetUShortSigAction(joinNumberOutputRoute, (a) => ExecuteNumericSwitch(a, (ushort)output, eRoutingSignalType.AudioVideo)); //Serial - OutputNameFeedbacks[OutputNames[output]].LinkInputSig(trilist.StringInput[joinMap.OutputName.JoinNumber + joinIndex]); - OutputRouteNameFeedbacks[OutputNames[output]].LinkInputSig(trilist.StringInput[joinMap.OutputRoutedName.JoinNumber + joinIndex]); + OutputNameFeedbacks[OutputNames[output]].LinkInputSig(trilist.StringInput[joinNumberOutputName]); + OutputRouteNameFeedbacks[OutputNames[output]].LinkInputSig(trilist.StringInput[joinNumberOutputRouteName]); } _Chassis.OnlineStatusChange += Chassis_OnlineStatusChange; trilist.OnlineStatusChange += (d, args) => { - if (!args.DeviceOnLine) return; + if (!args.DeviceOnLine) return; - // feedback updates was moved to the Chassis_OnlineStatusChange - // due to the amount of time it takes for the device to come online + // feedback updates was moved to the Chassis_OnlineStatusChange + // due to the amount of time it takes for the device to come online }; } @@ -435,38 +407,38 @@ private void UpdateFeedbacks() IsOnline?.FireUpdate(); DeviceNameFeedback?.FireUpdate(); AutoRouteFeedback?.FireUpdate(); - - foreach(var item in VideoInputSyncFeedbacks) + + foreach (var item in VideoInputSyncFeedbacks) { item.FireUpdate(); } - foreach(var item in VideoOutputRouteFeedbacks) + foreach (var item in VideoOutputRouteFeedbacks) { item.FireUpdate(); } - foreach(var item in InputHdcpEnableFeedback) + foreach (var item in InputHdcpEnableFeedback) { item.FireUpdate(); } - foreach(var item in InputNameFeedbacks) + foreach (var item in InputNameFeedbacks) { item.FireUpdate(); } - foreach(var item in OutputNameFeedbacks) + foreach (var item in OutputNameFeedbacks) { item.FireUpdate(); } - foreach(var item in OutputRouteNameFeedbacks) + foreach (var item in OutputRouteNameFeedbacks) { item.FireUpdate(); } - foreach(var item in Feedbacks) + foreach (var item in Feedbacks) { // TODO - Remove after testing Debug.LogInformation(this, $"UpdateFeedbacks: Firing feedback for {item.Key}"); @@ -483,21 +455,19 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice // TODO - Remove after testing Debug.LogInformation(this, $"Chassis_OnlineStatusChange: DeviceOnline = {args.DeviceOnLine}"); - IsOnline.FireUpdate(); + IsOnline.FireUpdate(); - if (!args.DeviceOnLine) return; + if (!args.DeviceOnLine) return; // TODO - Remove after testing Debug.LogInformation(this, $"Chassis_OnlineStatusChange: Feedbacks has {Feedbacks.Count} items in the collection"); - - foreach (var feedback in Feedbacks) - { + foreach (var feedback in Feedbacks) + { // TODO - Remove after testing Debug.LogInformation(this, $"Chassis_OnlineStatusChange: Firing update for {feedback.Key}"); - - feedback.FireUpdate(); - } + feedback.FireUpdate(); + } if (_Chassis is HdMd4x14kzE) { @@ -507,63 +477,65 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) { - if (args.EventId != DMOutputEventIds.VideoOutEventId) return; - // TODO - Remove after testing Debug.LogInformation(this, $"Chassis_DMOutputChange: EventId = {args.EventId}; Index = {args.Index}; Number = {args.Number}; Stream = {args.Stream} "); - var output = args.Number; + if (args.EventId != DMOutputEventIds.VideoOutEventId) + { + Debug.LogInformation(this, $"Chassis_DMOutputChange: EventId {args.EventId} != {DMOutputEventIds.VideoOutEventId}, ignoring."); + return; + } - var inputNumber = _Chassis.HdmiOutputs[output].VideoOutFeedback == null - ? 0 - : _Chassis.HdmiOutputs[output].VideoOutFeedback.Number; + var output = args.Number; + var outputName = OutputNames[output]; - var outputName = OutputNames[output]; + var inputNumber = _Chassis.HdmiOutputs[output].VideoOutFeedback?.Number ?? 0; - var feedback = VideoOutputRouteFeedbacks[outputName]; + if (!VideoOutputRouteFeedbacks.ContainsKey(outputName)) + { + Debug.LogInformation(this, $"Chassis_DMOutputChange: {outputName} not found in VideoOutputRouteFeedbacks"); + return; + } - if (feedback == null) - { - return; - } - var inPort = InputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output].VideoOutFeedback); - var outPort = OutputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output]); + var feedback = VideoOutputRouteFeedbacks[outputName]; + var inPort = InputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output].VideoOutFeedback); + var outPort = OutputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output]); - feedback.FireUpdate(); - OnSwitchChange(new RoutingNumericEventArgs(output, inputNumber, outPort, inPort, eRoutingSignalType.AudioVideo)); + feedback.FireUpdate(); + OnSwitchChange(new RoutingNumericEventArgs(output, inputNumber, outPort, inPort, eRoutingSignalType.AudioVideo)); } void Chassis_DMInputChange(Switch device, DMInputEventArgs args) - { - switch (args.EventId) - { - case DMInputEventIds.VideoDetectedEventId: - { - Debug.LogDebug(this, $"Chassis_DMInputChange: Event ID {args.EventId}: Updating VideoInputSyncFeedbacks"); - foreach (var item in VideoInputSyncFeedbacks) - { - item.FireUpdate(); - } - break; - } - case DMInputEventIds.InputNameFeedbackEventId: - case DMInputEventIds.InputNameEventId: - case DMInputEventIds.NameFeedbackEventId: - { - Debug.LogDebug(this, $"Chassis_DMInputChange: Event ID {args.EventId}: Updating name feedbacks."); - Debug.LogDebug(this, $"Chassis_DMInputChange: Input {args.Number} Name {_Chassis.HdmiInputs[args.Number].NameFeedback.StringValue}"); - foreach (var item in InputNameFeedbacks) - { - item.FireUpdate(); - } - break; - } - default: - { - Debug.LogDebug(this, $"Chassis_DMInputChange: Unhandled DM Input Event ID {args.EventId}"); - break; - } - } + { + switch (args.EventId) + { + case DMInputEventIds.VideoDetectedEventId: + { + Debug.LogDebug(this, $"Chassis_DMInputChange: Event ID {args.EventId}: Updating VideoInputSyncFeedbacks"); + foreach (var item in VideoInputSyncFeedbacks) + { + item.FireUpdate(); + } + break; + } + case DMInputEventIds.InputNameFeedbackEventId: + case DMInputEventIds.InputNameEventId: + case DMInputEventIds.NameFeedbackEventId: + { + Debug.LogDebug(this, $"Chassis_DMInputChange: Event ID {args.EventId}: Updating name feedbacks."); + Debug.LogDebug(this, $"Chassis_DMInputChange: Input {args.Number} Name {_Chassis.HdmiInputs[args.Number].NameFeedback.StringValue}"); + foreach (var item in InputNameFeedbacks) + { + item.FireUpdate(); + } + break; + } + default: + { + Debug.LogDebug(this, $"Chassis_DMInputChange: Unhandled DM Input Event ID {args.EventId}"); + break; + } + } } #endregion @@ -574,8 +546,8 @@ public class HdMdNxM4kZEControllerFactory : EssentialsPluginDeviceFactory() { "hdmd4x14kze", "hdmd4x24kze", "hdmd8x84kze" }; + MinimumEssentialsFrameworkVersion = "2.24.4"; + TypeNames = new List() { "hdmd4x14kze", "hdmd4x24kze", "hdmd8x84kze" }; } public override EssentialsDevice BuildDevice(DeviceConfig dc) @@ -591,7 +563,7 @@ public override EssentialsDevice BuildDevice(DeviceConfig dc) switch (type) { - case ("hdmd4x14kze"): + case ("hdmd4x14kze"): return new HdMdNxM4kZEController(dc.Key, dc.Name, new HdMd4x14kzE(ipid, Global.ControlSystem), props); case ("hdmd4x24kze"): return new HdMdNxM4kZEController(dc.Key, dc.Name, new HdMd4x24kzE(ipid, Global.ControlSystem), props); From 657a54d20c4eef992e5636eba55d5ea9154ba5d2 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 22 Jan 2026 10:26:03 -0600 Subject: [PATCH 15/50] fix: enhance input/output handling and error logging in HdMdNxM4KzEController --- src/Chassis/HdMdNxM4KzEController.cs | 130 +++++++++++++++++---------- 1 file changed, 83 insertions(+), 47 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index d425025..ca062c1 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -109,50 +109,84 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, return; } - for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) + foreach (var kvp in InputNames) { - var index = i; - if (!InputNames.TryGetValue(index, out var inputName)) + var index = kvp.Key; + var inputName = kvp.Value; + + if (index < 1 || index > _Chassis.NumberOfInputs) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "Input index {index} is out of range (1-{max}). Skipping.", this, index, _Chassis.NumberOfInputs); + continue; + } + + var hdmiInput = _Chassis.HdmiInputs[index]; + if (hdmiInput == null) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "No input name defined for input {index}. Using default name.", this, index); - inputName = $"Input {index}"; - InputNames[index] = inputName; + Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "HdmiInput at index {index} is null. Skipping.", this, index); + continue; } - // _Chassis.Inputs[index].Name.StringValue = inputName; - _Chassis.HdmiInputs[index].Name.StringValue = inputName; + + var chassisInput = _Chassis.Inputs[index]; + if (chassisInput == null) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "Chassis Input at index {index} is null. Skipping.", this, index); + continue; + } + + hdmiInput.Name.StringValue = inputName; InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, _Chassis.HdmiInputs[index], this) + eRoutingPortConnectionType.Hdmi, hdmiInput, this) { - FeedbackMatchObject = _Chassis.HdmiInputs[index] + FeedbackMatchObject = hdmiInput }); - VideoInputSyncFeedbacks.Add(new BoolFeedback(inputName, () => _Chassis.Inputs[index].VideoDetectedFeedback?.BoolValue ?? false)); - //InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].NameFeedback.StringValue)); + VideoInputSyncFeedbacks.Add(new BoolFeedback(inputName, () => chassisInput?.VideoDetectedFeedback?.BoolValue ?? false)); InputNameFeedbacks.Add(new StringFeedback(inputName, () => InputNames[index])); - InputHdcpEnableFeedback.Add(new BoolFeedback(inputName, () => _Chassis.HdmiInputs[index].HdmiInputPort.HdcpSupportOnFeedback?.BoolValue ?? false)); + + if (hdmiInput.HdmiInputPort == null) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "HdmiInputPort at index {index} is null. HDCP feedback will default to false.", this, index); + } + + InputHdcpEnableFeedback.Add(new BoolFeedback(inputName, () => hdmiInput?.HdmiInputPort?.HdcpSupportOnFeedback?.BoolValue ?? false)); } - for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) + foreach (var kvp in OutputNames) { - var index = i; - if (!OutputNames.TryGetValue(index, out var outputName)) + var index = kvp.Key; + var outputName = kvp.Value; + + if (index < 1 || index > _Chassis.NumberOfOutputs) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "Output index {index} is out of range (1-{max}). Skipping.", this, index, _Chassis.NumberOfOutputs); + continue; + } + + var hdmiOutput = _Chassis.HdmiOutputs[index]; + if (hdmiOutput == null) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "HdmiOutput at index {index} is null. Skipping.", this, index); + continue; + } + + var chassisOutput = _Chassis.Outputs[index]; + if (chassisOutput == null) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "No output name defined for output {index}. Using default name.", this, index); - outputName = $"Output {index}"; - OutputNames[index] = outputName; + Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "Chassis Output at index {index} is null. Skipping.", this, index); + continue; } - //_Chassis.Outputs[index].Name.StringValue = outputName; - //_Chassis.HdmiOutputs[index].Name.StringValue = outputName; OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, _Chassis.HdmiOutputs[index], this) + eRoutingPortConnectionType.Hdmi, hdmiOutput, this) { - FeedbackMatchObject = _Chassis.HdmiOutputs[index] + FeedbackMatchObject = hdmiOutput }); - VideoOutputRouteFeedbacks.Add(new IntFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback == null ? 0 : (int)_Chassis.Outputs[index].VideoOutFeedback.Number)); + + VideoOutputRouteFeedbacks.Add(new IntFeedback(outputName, () => chassisOutput.VideoOutFeedback == null ? 0 : (int)chassisOutput.VideoOutFeedback.Number)); OutputNameFeedbacks.Add(new StringFeedback(outputName, () => OutputNames[index])); - OutputRouteNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback == null ? NoRouteText : _Chassis.Outputs[index].VideoOutFeedback.NameFeedback.StringValue)); + OutputRouteNameFeedbacks.Add(new StringFeedback(outputName, () => chassisOutput.VideoOutFeedback == null ? NoRouteText : chassisOutput.VideoOutFeedback.NameFeedback.StringValue)); } _Chassis.DMInputChange += Chassis_DMInputChange; @@ -215,7 +249,7 @@ public void DisableAutoRoute() Debug.LogVerbose(this, "DisableAutoRoute: AutoRoute is not supported on this chassis."); } - + #region FeedbackCollection Methods @@ -476,36 +510,38 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice } void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) - { - // TODO - Remove after testing - Debug.LogInformation(this, $"Chassis_DMOutputChange: EventId = {args.EventId}; Index = {args.Index}; Number = {args.Number}; Stream = {args.Stream} "); + { + // TODO - Remove after testing + Debug.LogInformation(this, $"Chassis_DMOutputChange: EventId = {args.EventId}; Index = {args.Index}; Number = {args.Number}; Stream = {args.Stream} "); - if (args.EventId != DMOutputEventIds.VideoOutEventId) - { - Debug.LogInformation(this, $"Chassis_DMOutputChange: EventId {args.EventId} != {DMOutputEventIds.VideoOutEventId}, ignoring."); - return; - } + if (args.EventId != DMOutputEventIds.VideoOutEventId) + { + Debug.LogInformation(this, $"Chassis_DMOutputChange: EventId {args.EventId} != {DMOutputEventIds.VideoOutEventId}, ignoring."); + return; + } - var output = args.Number; - var outputName = OutputNames[output]; + var output = args.Number; + var outputName = OutputNames[output]; - var inputNumber = _Chassis.HdmiOutputs[output].VideoOutFeedback?.Number ?? 0; + var inputNumber = _Chassis.HdmiOutputs[output].VideoOutFeedback?.Number ?? 0; - if (!VideoOutputRouteFeedbacks.ContainsKey(outputName)) + var feedback = VideoOutputRouteFeedbacks.FirstOrDefault(f => f.Key == outputName); + if (feedback != null) + { + var inPort = InputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output].VideoOutFeedback); + var outPort = OutputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output]); + + feedback.FireUpdate(); + OnSwitchChange(new RoutingNumericEventArgs(output, inputNumber, outPort, inPort, eRoutingSignalType.AudioVideo)); + } + else { Debug.LogInformation(this, $"Chassis_DMOutputChange: {outputName} not found in VideoOutputRouteFeedbacks"); return; } + } - var feedback = VideoOutputRouteFeedbacks[outputName]; - var inPort = InputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output].VideoOutFeedback); - var outPort = OutputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output]); - - feedback.FireUpdate(); - OnSwitchChange(new RoutingNumericEventArgs(output, inputNumber, outPort, inPort, eRoutingSignalType.AudioVideo)); - } - - void Chassis_DMInputChange(Switch device, DMInputEventArgs args) + void Chassis_DMInputChange(Switch device, DMInputEventArgs args) { switch (args.EventId) { From b9e412fce06fb781d4d819ed16a097525e2c628a Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 22 Jan 2026 10:44:15 -0600 Subject: [PATCH 16/50] fix: update DM output change handling and improve logging for clarity --- src/Chassis/HdMdNxM4KzEController.cs | 100 ++++++++++++++------------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index ca062c1..6e9ec1d 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -510,70 +510,76 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice } void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) - { - // TODO - Remove after testing - Debug.LogInformation(this, $"Chassis_DMOutputChange: EventId = {args.EventId}; Index = {args.Index}; Number = {args.Number}; Stream = {args.Stream} "); - - if (args.EventId != DMOutputEventIds.VideoOutEventId) - { - Debug.LogInformation(this, $"Chassis_DMOutputChange: EventId {args.EventId} != {DMOutputEventIds.VideoOutEventId}, ignoring."); - return; - } - - var output = args.Number; - var outputName = OutputNames[output]; - - var inputNumber = _Chassis.HdmiOutputs[output].VideoOutFeedback?.Number ?? 0; - - var feedback = VideoOutputRouteFeedbacks.FirstOrDefault(f => f.Key == outputName); - if (feedback != null) - { - var inPort = InputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output].VideoOutFeedback); - var outPort = OutputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output]); - - feedback.FireUpdate(); - OnSwitchChange(new RoutingNumericEventArgs(output, inputNumber, outPort, inPort, eRoutingSignalType.AudioVideo)); - } - else - { - Debug.LogInformation(this, $"Chassis_DMOutputChange: {outputName} not found in VideoOutputRouteFeedbacks"); - return; - } - } - - void Chassis_DMInputChange(Switch device, DMInputEventArgs args) { + // TODO - Remove after testing + var eventName = Enum.GetName(typeof(DMOutputEventIds), args.EventId); + Debug.LogInformation(this, $"Chassis_DMOutputChange: received {eventName} (id-{args.EventId}); Index = {args.Index}; Number = {args.Number}; Stream = {args.Stream} "); + switch (args.EventId) { - case DMInputEventIds.VideoDetectedEventId: + case DMOutputEventIds.VideoOutEventId: { - Debug.LogDebug(this, $"Chassis_DMInputChange: Event ID {args.EventId}: Updating VideoInputSyncFeedbacks"); - foreach (var item in VideoInputSyncFeedbacks) + var output = args.Number; + var outputName = OutputNames[output]; + + var inputNumber = _Chassis.HdmiOutputs[output].VideoOutFeedback?.Number ?? 0; + + var feedback = VideoOutputRouteFeedbacks.FirstOrDefault(f => f.Key == outputName); + if (feedback != null) { - item.FireUpdate(); + var inPort = InputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output].VideoOutFeedback); + var outPort = OutputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output]); + + feedback.FireUpdate(); + OnSwitchChange(new RoutingNumericEventArgs(output, inputNumber, outPort, inPort, eRoutingSignalType.AudioVideo)); } - break; - } - case DMInputEventIds.InputNameFeedbackEventId: - case DMInputEventIds.InputNameEventId: - case DMInputEventIds.NameFeedbackEventId: - { - Debug.LogDebug(this, $"Chassis_DMInputChange: Event ID {args.EventId}: Updating name feedbacks."); - Debug.LogDebug(this, $"Chassis_DMInputChange: Input {args.Number} Name {_Chassis.HdmiInputs[args.Number].NameFeedback.StringValue}"); - foreach (var item in InputNameFeedbacks) + else { - item.FireUpdate(); + Debug.LogInformation(this, $"Chassis_DMOutputChange: {outputName} not found in VideoOutputRouteFeedbacks"); } break; } default: { - Debug.LogDebug(this, $"Chassis_DMInputChange: Unhandled DM Input Event ID {args.EventId}"); + Debug.LogDebug(this, $"Chassis_DMOutputChange: Unhandled DM Output Event {eventName} (id-{args.EventId}), ignoring."); break; } } } + void Chassis_DMInputChange(Switch device, DMInputEventArgs args) + { + var eventName = Enum.GetName(typeof(DMInputEventIds), args.EventId); + switch (args.EventId) + { + case DMInputEventIds.VideoDetectedEventId: + { + Debug.LogDebug(this, $"Chassis_DMInputChange: received {eventName} (id-{args.EventId}) | Updating VideoInputSyncFeedbacks"); + foreach (var item in VideoInputSyncFeedbacks) + { + item.FireUpdate(); + } + break; + } + case DMInputEventIds.InputNameFeedbackEventId: + case DMInputEventIds.InputNameEventId: + case DMInputEventIds.NameFeedbackEventId: + { + Debug.LogDebug(this, $"Chassis_DMInputChange: received {eventName} (id-{args.EventId}) | Input {args.Number} Name {_Chassis.HdmiInputs[args.Number].NameFeedback.StringValue}, updating InputNameFeedbacks"); + foreach (var item in InputNameFeedbacks) + { + item.FireUpdate(); + } + break; + } + default: + { + Debug.LogDebug(this, $"Chassis_DMInputChange: Unhandled DM Input Event {eventName} (id-{args.EventId}), ignoring."); + break; + } + } + } + #endregion #region Factory From 55ae9d12170413f70150bfc2129f225f10e5d217 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 22 Jan 2026 11:30:07 -0600 Subject: [PATCH 17/50] fix: improve event name retrieval in DM output/input change handling --- src/Chassis/HdMdNxM4KzEController.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 6e9ec1d..7ed2fb8 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -512,7 +512,8 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) { // TODO - Remove after testing - var eventName = Enum.GetName(typeof(DMOutputEventIds), args.EventId); + var eventIdType = args.EventId.GetType(); + var eventName = eventIdType.IsEnum ? Enum.GetName(eventIdType, args.EventId) : args.EventId.ToString(); Debug.LogInformation(this, $"Chassis_DMOutputChange: received {eventName} (id-{args.EventId}); Index = {args.Index}; Number = {args.Number}; Stream = {args.Stream} "); switch (args.EventId) @@ -549,7 +550,8 @@ void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) void Chassis_DMInputChange(Switch device, DMInputEventArgs args) { - var eventName = Enum.GetName(typeof(DMInputEventIds), args.EventId); + var eventIdType = args.EventId.GetType(); + var eventName = eventIdType.IsEnum ? Enum.GetName(eventIdType, args.EventId) : args.EventId.ToString(); switch (args.EventId) { case DMInputEventIds.VideoDetectedEventId: From 81b8b146b2d0f32e849c5bb5d2708245e13604f3 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 22 Jan 2026 11:32:08 -0600 Subject: [PATCH 18/50] fix: update input/output type references in ExecuteSwitch method for HdMdNxM4kzE support --- src/Chassis/HdMdNxM4KzEController.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 7ed2fb8..f363086 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -314,13 +314,13 @@ public void AddFeedbackToList(Essentials.Core.Feedback newFb) public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) { - var input = inputSelector as HdMdNxMHdmiInput; - var output = outputSelector as HdMdNxMHdmiOutput; + var input = inputSelector as HdMdNxM4kzEHdmiInput; + var output = outputSelector as HdMdNxM4kzEHdmiOutput; Debug.LogVerbose(this, "ExecuteSwitch: input={0} output={1}", input, output); if (output == null) { - Debug.LogInformation(this, "Unable to make switch. output selector is not HdMdNxMHdmiOutput"); + Debug.LogInformation(this, "Unable to make switch. output selector is not HdMdNxM4kzEHdmiOutput"); return; } From 7a7176314f75dbddaeec7dfb406eea87eade54a5 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 22 Jan 2026 11:52:09 -0600 Subject: [PATCH 19/50] fix: update event name retrieval in DM output change handling for clarity --- src/Chassis/HdMdNxM4KzEController.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index f363086..3f28ae3 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -513,7 +513,9 @@ void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) { // TODO - Remove after testing var eventIdType = args.EventId.GetType(); - var eventName = eventIdType.IsEnum ? Enum.GetName(eventIdType, args.EventId) : args.EventId.ToString(); + //var eventName = eventIdType.IsEnum ? Enum.GetName(eventIdType, args.EventId) : args.EventId.ToString(); + var eventName = nameof(args.EventId); + Debug.LogInformation(this, $"Chassis_DMOutputChange: Event Type = {eventIdType}, Event Name = {eventName}"); Debug.LogInformation(this, $"Chassis_DMOutputChange: received {eventName} (id-{args.EventId}); Index = {args.Index}; Number = {args.Number}; Stream = {args.Stream} "); switch (args.EventId) From 5b71f5f6baf80fb409789462638b282e958a8933 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 22 Jan 2026 11:56:01 -0600 Subject: [PATCH 20/50] fix: add IsOnline feedback and update related references in HdMdNxM4KzEController --- src/Chassis/HdMdNxM4KzEController.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 3f28ae3..3618473 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -36,6 +36,7 @@ public class HdMdNxM4kZEController : CrestronGenericBridgeableBaseDevice, IRouti public FeedbackCollection OutputNameFeedbacks { get; private set; } public FeedbackCollection OutputRouteNameFeedbacks { get; private set; } public FeedbackCollection InputHdcpEnableFeedback { get; private set; } + public BoolFeedback IsOnlineFeedback { get; private set;} public StringFeedback DeviceNameFeedback { get; private set; } public BoolFeedback AutoRouteFeedback { get; private set; } public string NoRouteText { get; private set; } @@ -80,6 +81,7 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, OutputNames = props.Outputs; } + IsOnlineFeedback = new BoolFeedback("IsOnline", () => _Chassis.IsOnline); DeviceNameFeedback = new StringFeedback("DeviceName", () => Name); VideoInputSyncFeedbacks = new FeedbackCollection(); @@ -255,6 +257,7 @@ public void DisableAutoRoute() public void AddFeedbackCollections() { + AddFeedbackToList(IsOnlineFeedback); AddFeedbackToList(DeviceNameFeedback); if (AutoRouteFeedback != null) @@ -370,7 +373,7 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join Debug.LogInformation(this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); } - IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); DeviceNameFeedback.LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); if (_Chassis is HdMd4x14kzE _chassis) @@ -438,7 +441,7 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join private void UpdateFeedbacks() { - IsOnline?.FireUpdate(); + IsOnlineFeedback?.FireUpdate(); DeviceNameFeedback?.FireUpdate(); AutoRouteFeedback?.FireUpdate(); @@ -489,7 +492,7 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice // TODO - Remove after testing Debug.LogInformation(this, $"Chassis_OnlineStatusChange: DeviceOnline = {args.DeviceOnLine}"); - IsOnline.FireUpdate(); + IsOnlineFeedback.FireUpdate(); if (!args.DeviceOnLine) return; From 935438437f48cb5f499da7b3ac5eca0a8a28984d Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 22 Jan 2026 12:17:10 -0600 Subject: [PATCH 21/50] fix: enhance event name retrieval in DM output/input change handling --- src/Chassis/HdMdNxM4KzEController.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 3618473..cc91b74 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -13,6 +13,7 @@ using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Config; using System.Threading; +using System.Runtime.InteropServices; namespace PepperDash.Essentials.DM.Chassis { @@ -515,10 +516,9 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) { // TODO - Remove after testing - var eventIdType = args.EventId.GetType(); - //var eventName = eventIdType.IsEnum ? Enum.GetName(eventIdType, args.EventId) : args.EventId.ToString(); - var eventName = nameof(args.EventId); - Debug.LogInformation(this, $"Chassis_DMOutputChange: Event Type = {eventIdType}, Event Name = {eventName}"); + var eventName = typeof(DMOutputEventIds) + .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static) + .FirstOrDefault(f => f.IsLiteral && (int)f.GetValue(null) == args.EventId)?.Name ?? args.EventId.ToString(); Debug.LogInformation(this, $"Chassis_DMOutputChange: received {eventName} (id-{args.EventId}); Index = {args.Index}; Number = {args.Number}; Stream = {args.Stream} "); switch (args.EventId) @@ -555,8 +555,10 @@ void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) void Chassis_DMInputChange(Switch device, DMInputEventArgs args) { - var eventIdType = args.EventId.GetType(); - var eventName = eventIdType.IsEnum ? Enum.GetName(eventIdType, args.EventId) : args.EventId.ToString(); + var eventName = typeof(DMInputEventIds) + .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static) + .FirstOrDefault(f => f.IsLiteral && (int)f.GetValue(null) == args.EventId)?.Name ?? args.EventId.ToString(); + switch (args.EventId) { case DMInputEventIds.VideoDetectedEventId: From 31e328799ef12b46b71b7ad9fc63743fe483da76 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 22 Jan 2026 12:29:39 -0600 Subject: [PATCH 22/50] fix: update IsOnline feedback references in HdMdNxM4KzEController --- src/Chassis/HdMdNxM4KzEController.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index cc91b74..9e5eb4c 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -37,7 +37,6 @@ public class HdMdNxM4kZEController : CrestronGenericBridgeableBaseDevice, IRouti public FeedbackCollection OutputNameFeedbacks { get; private set; } public FeedbackCollection OutputRouteNameFeedbacks { get; private set; } public FeedbackCollection InputHdcpEnableFeedback { get; private set; } - public BoolFeedback IsOnlineFeedback { get; private set;} public StringFeedback DeviceNameFeedback { get; private set; } public BoolFeedback AutoRouteFeedback { get; private set; } public string NoRouteText { get; private set; } @@ -82,7 +81,6 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, OutputNames = props.Outputs; } - IsOnlineFeedback = new BoolFeedback("IsOnline", () => _Chassis.IsOnline); DeviceNameFeedback = new StringFeedback("DeviceName", () => Name); VideoInputSyncFeedbacks = new FeedbackCollection(); @@ -258,7 +256,7 @@ public void DisableAutoRoute() public void AddFeedbackCollections() { - AddFeedbackToList(IsOnlineFeedback); + AddFeedbackToList(IsOnline); AddFeedbackToList(DeviceNameFeedback); if (AutoRouteFeedback != null) @@ -374,7 +372,7 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join Debug.LogInformation(this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); } - IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); DeviceNameFeedback.LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); if (_Chassis is HdMd4x14kzE _chassis) @@ -442,7 +440,7 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join private void UpdateFeedbacks() { - IsOnlineFeedback?.FireUpdate(); + IsOnline?.FireUpdate(); DeviceNameFeedback?.FireUpdate(); AutoRouteFeedback?.FireUpdate(); @@ -493,8 +491,8 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice // TODO - Remove after testing Debug.LogInformation(this, $"Chassis_OnlineStatusChange: DeviceOnline = {args.DeviceOnLine}"); - IsOnlineFeedback.FireUpdate(); - + IsOnline.FireUpdate(); + if (!args.DeviceOnLine) return; // TODO - Remove after testing From 71eb4e244fa252b024284c2ea77b6f644d10bb4d Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 22 Jan 2026 12:36:15 -0600 Subject: [PATCH 23/50] refactor: remove unused using directives in HdMdNxM4KzEController --- src/Chassis/HdMdNxM4KzEController.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 9e5eb4c..28e077b 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Text.RegularExpressions; using Newtonsoft.Json; using Crestron.SimplSharp; using Crestron.SimplSharpPro.DeviceSupport; @@ -12,8 +10,7 @@ using PepperDash.Essentials.DM.Config; using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Config; -using System.Threading; -using System.Runtime.InteropServices; + namespace PepperDash.Essentials.DM.Chassis { From 3ef463035a2e9a79518b6f8c0a5b1747917a97f5 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 22 Jan 2026 12:51:26 -0600 Subject: [PATCH 24/50] fix: remove unnecessary whitespace in solution file --- PepperDash.Essentials.DM.4Series.sln | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/PepperDash.Essentials.DM.4Series.sln b/PepperDash.Essentials.DM.4Series.sln index 4a7c666..58f6bcf 100644 --- a/PepperDash.Essentials.DM.4Series.sln +++ b/PepperDash.Essentials.DM.4Series.sln @@ -1,5 +1,4 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.7.34202.233 MinimumVisualStudioVersion = 10.0.40219.1 From d074a703ae00d4a8c796a73cb002c03c769a83df Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 22 Jan 2026 13:17:50 -0600 Subject: [PATCH 25/50] refactor: improve input/output setup methods and enhance error logging --- src/Chassis/HdMdNxM4KzEController.cs | 60 +++++++++++++++++----------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 28e077b..2b1629d 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -95,15 +95,20 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, AutoRouteFeedback = new BoolFeedback("AutoRouteFeedback", () => _chassis.AutoRouteOnFeedback?.BoolValue ?? false); } - if (InputNames == null) - { - Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "InputNames is null. Ensure 'inputs' is defined in the device configuration.", this); - return; - } + SetupInputs(); + SetupOutputs(); - if (OutputNames == null) + _Chassis.DMInputChange += Chassis_DMInputChange; + _Chassis.DMOutputChange += Chassis_DMOutputChange; + + AddPostActivationAction(AddFeedbackCollections); + } + + private void SetupInputs() + { + if (InputNames == null) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "OutputNames is null. Ensure 'outputs' is defined in the device configuration.", this); + Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "SetupInputs: InputNames is null. Ensure 'inputs' is defined in the device configuration.", this); return; } @@ -111,24 +116,25 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, { var index = kvp.Key; var inputName = kvp.Value; + var inputFbKeyPrefix = inputName.Replace(" ", "").Trim(); if (index < 1 || index > _Chassis.NumberOfInputs) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "Input index {index} is out of range (1-{max}). Skipping.", this, index, _Chassis.NumberOfInputs); + Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "SetupInputs: Input index {index} is out of range (1-{max}). Skipping.", this, index, _Chassis.NumberOfInputs); continue; } var hdmiInput = _Chassis.HdmiInputs[index]; if (hdmiInput == null) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "HdmiInput at index {index} is null. Skipping.", this, index); + Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "SetupInputs: HdmiInput at index {index} is null. Skipping.", this, index); continue; } var chassisInput = _Chassis.Inputs[index]; if (chassisInput == null) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "Chassis Input at index {index} is null. Skipping.", this, index); + Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "SetupInputs: Chassis Input at index {index} is null. Skipping.", this, index); continue; } @@ -140,39 +146,49 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, FeedbackMatchObject = hdmiInput }); - VideoInputSyncFeedbacks.Add(new BoolFeedback(inputName, () => chassisInput?.VideoDetectedFeedback?.BoolValue ?? false)); - InputNameFeedbacks.Add(new StringFeedback(inputName, () => InputNames[index])); + VideoInputSyncFeedbacks.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}VideoInputSyncFeedback"), () => chassisInput?.VideoDetectedFeedback?.BoolValue ?? false)); + InputNameFeedbacks.Add(new StringFeedback(string.Format($"{inputFbKeyPrefix}InputNameFeedback"), () => InputNames[index])); if (hdmiInput.HdmiInputPort == null) { Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "HdmiInputPort at index {index} is null. HDCP feedback will default to false.", this, index); } - InputHdcpEnableFeedback.Add(new BoolFeedback(inputName, () => hdmiInput?.HdmiInputPort?.HdcpSupportOnFeedback?.BoolValue ?? false)); + InputHdcpEnableFeedback.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}HdcpEnableFeedback"), () => hdmiInput?.HdmiInputPort?.HdcpSupportOnFeedback?.BoolValue ?? false)); + } + } + + private void SetupOutputs() + { + if (OutputNames == null) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "SetupOutputs: OutputNames is null. Ensure 'outputs' is defined in the device configuration.", this); + return; } foreach (var kvp in OutputNames) { var index = kvp.Key; var outputName = kvp.Value; + var outputFbKeyPrefix = outputName.Replace(" ", "").Trim(); if (index < 1 || index > _Chassis.NumberOfOutputs) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "Output index {index} is out of range (1-{max}). Skipping.", this, index, _Chassis.NumberOfOutputs); + Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "SetupOutputs: Output index {index} is out of range (1-{max}). Skipping.", this, index, _Chassis.NumberOfOutputs); continue; } var hdmiOutput = _Chassis.HdmiOutputs[index]; if (hdmiOutput == null) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "HdmiOutput at index {index} is null. Skipping.", this, index); + Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "SetupOutputs: HdmiOutput at index {index} is null. Skipping.", this, index); continue; } var chassisOutput = _Chassis.Outputs[index]; if (chassisOutput == null) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "Chassis Output at index {index} is null. Skipping.", this, index); + Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "SetupOutputs: Chassis Output at index {index} is null. Skipping.", this, index); continue; } @@ -182,15 +198,10 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, FeedbackMatchObject = hdmiOutput }); - VideoOutputRouteFeedbacks.Add(new IntFeedback(outputName, () => chassisOutput.VideoOutFeedback == null ? 0 : (int)chassisOutput.VideoOutFeedback.Number)); - OutputNameFeedbacks.Add(new StringFeedback(outputName, () => OutputNames[index])); - OutputRouteNameFeedbacks.Add(new StringFeedback(outputName, () => chassisOutput.VideoOutFeedback == null ? NoRouteText : chassisOutput.VideoOutFeedback.NameFeedback.StringValue)); + VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputFbKeyPrefix}VideoOutputRouteFeedback"), () => chassisOutput.VideoOutFeedback == null ? 0 : (int)chassisOutput.VideoOutFeedback.Number)); + OutputNameFeedbacks.Add(new StringFeedback(string.Format($"{outputFbKeyPrefix}OutputNameFeedback"), () => OutputNames[index])); + OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputFbKeyPrefix}OutputRouteNameFeedback"), () => chassisOutput.VideoOutFeedback == null ? NoRouteText : chassisOutput.VideoOutFeedback.NameFeedback.StringValue)); } - - _Chassis.DMInputChange += Chassis_DMInputChange; - _Chassis.DMOutputChange += Chassis_DMOutputChange; - - AddPostActivationAction(AddFeedbackCollections); } #endregion @@ -250,6 +261,7 @@ public void DisableAutoRoute() #region FeedbackCollection Methods + public void AddFeedbackCollections() { From ddc8827501be611a1bcae05821045be510294a45 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 22 Jan 2026 13:20:24 -0600 Subject: [PATCH 26/50] ci(force-patch): increment patch by force From a88de5cb221b72afe0ca1319feec0465387855ce Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 22 Jan 2026 13:41:01 -0600 Subject: [PATCH 27/50] fix: add null checks for IsOnline and DeviceNameFeedback links; improve input/output linking logic --- src/Chassis/HdMdNxM4KzEController.cs | 95 +++++++++++++++++++--------- 1 file changed, 64 insertions(+), 31 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 2b1629d..86485cd 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -381,8 +381,8 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join Debug.LogInformation(this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); } - IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); - DeviceNameFeedback.LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); + IsOnline?.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + DeviceNameFeedback?.LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); if (_Chassis is HdMd4x14kzE _chassis) { @@ -393,46 +393,79 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join AutoRouteFeedback?.LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); } - for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) + if (InputNames != null) { - var joinIndex = i - 1; - var input = i; + foreach (var kvp in InputNames) + { + var input = kvp.Key; + var inputName = kvp.Value; + + if (input < 1 || input > _Chassis.NumberOfInputs) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "LinkToApi: Input index {index} is out of range (1-{max}). Skipping.", this, input, _Chassis.NumberOfInputs); + continue; + } + + var joinIndex = input - 1; - var joinNumberInputSync = joinMap.InputSync.JoinNumber + joinIndex; - var joinNumberEnableInputHdcp = joinMap.EnableInputHdcp.JoinNumber + joinIndex; - var joinNumberDisableInputHdcp = joinMap.DisableInputHdcp.JoinNumber + joinIndex; - var joinNumberInputName = joinMap.InputName.JoinNumber + joinIndex; + var joinNumberInputSync = joinMap.InputSync.JoinNumber + joinIndex; + var joinNumberEnableInputHdcp = joinMap.EnableInputHdcp.JoinNumber + joinIndex; + var joinNumberDisableInputHdcp = joinMap.DisableInputHdcp.JoinNumber + joinIndex; + var joinNumberInputName = joinMap.InputName.JoinNumber + joinIndex; - Debug.LogInformation(this, $"LinkToApi: _Chassis.NumberOfInputs > {input} | joinIndex = {joinIndex}, InputSyncJoin = {joinNumberInputSync}, EnableHdcpJoin = {joinNumberEnableInputHdcp}, DisableHdcpJoin = {joinNumberDisableInputHdcp}, InputNameJoin = {joinNumberInputName}"); + Debug.LogInformation(this, $"LinkToApi: Input {input} | joinIndex = {joinIndex}, InputSyncJoin = {joinNumberInputSync}, EnableHdcpJoin = {joinNumberEnableInputHdcp}, DisableHdcpJoin = {joinNumberDisableInputHdcp}, InputNameJoin = {joinNumberInputName}"); - //Digital - VideoInputSyncFeedbacks[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinNumberInputSync]); - InputHdcpEnableFeedback[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinNumberEnableInputHdcp]); - InputHdcpEnableFeedback[InputNames[input]].LinkComplementInputSig(trilist.BooleanInput[joinNumberDisableInputHdcp]); - trilist.SetSigTrueAction(joinNumberEnableInputHdcp, () => EnableHdcp(input)); - trilist.SetSigTrueAction(joinNumberDisableInputHdcp, () => DisableHdcp(input)); + //Digital + VideoInputSyncFeedbacks[inputName]?.LinkInputSig(trilist.BooleanInput[joinNumberInputSync]); + InputHdcpEnableFeedback[inputName]?.LinkInputSig(trilist.BooleanInput[joinNumberEnableInputHdcp]); + InputHdcpEnableFeedback[inputName]?.LinkComplementInputSig(trilist.BooleanInput[joinNumberDisableInputHdcp]); - //Serial - InputNameFeedbacks[InputNames[input]].LinkInputSig(trilist.StringInput[joinNumberInputName]); + trilist.SetSigTrueAction(joinNumberEnableInputHdcp, () => EnableHdcp(input)); + trilist.SetSigTrueAction(joinNumberDisableInputHdcp, () => DisableHdcp(input)); + + //Serial + InputNameFeedbacks[inputName]?.LinkInputSig(trilist.StringInput[joinNumberInputName]); + } + } + else + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "LinkToApi: InputNames is null. Skipping input linking.", this); } - for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) + if (OutputNames != null) { - var joinIndex = i - 1; - var output = i; + foreach (var kvp in OutputNames) + { + var output = kvp.Key; + var outputName = kvp.Value; + + if (output < 1 || output > _Chassis.NumberOfOutputs) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "LinkToApi: Output index {index} is out of range (1-{max}). Skipping.", this, output, _Chassis.NumberOfOutputs); + continue; + } + + var joinIndex = output - 1; - var joinNumberOutputRoute = joinMap.OutputRoute.JoinNumber + joinIndex; - var joinNumberOutputName = joinMap.OutputName.JoinNumber + joinIndex; - var joinNumberOutputRouteName = joinMap.OutputRoutedName.JoinNumber + joinIndex; + var joinNumberOutputRoute = joinMap.OutputRoute.JoinNumber + joinIndex; + var joinNumberOutputName = joinMap.OutputName.JoinNumber + joinIndex; + var joinNumberOutputRouteName = joinMap.OutputRoutedName.JoinNumber + joinIndex; - Debug.LogInformation(this, $"LinkToApi: _Chassis.NumberOfOutputs > {output} | joinIndex = {joinIndex}, OutputRouteJoin = {joinNumberOutputRoute}, OutputNameJoin = {joinNumberOutputName}, OutputRouteNameJoin = {joinNumberOutputRouteName}"); + Debug.LogInformation(this, $"LinkToApi: Output {output} | joinIndex = {joinIndex}, OutputRouteJoin = {joinNumberOutputRoute}, OutputNameJoin = {joinNumberOutputName}, OutputRouteNameJoin = {joinNumberOutputRouteName}"); - //Analog - VideoOutputRouteFeedbacks[OutputNames[output]].LinkInputSig(trilist.UShortInput[joinNumberOutputRoute]); - trilist.SetUShortSigAction(joinNumberOutputRoute, (a) => ExecuteNumericSwitch(a, (ushort)output, eRoutingSignalType.AudioVideo)); - //Serial - OutputNameFeedbacks[OutputNames[output]].LinkInputSig(trilist.StringInput[joinNumberOutputName]); - OutputRouteNameFeedbacks[OutputNames[output]].LinkInputSig(trilist.StringInput[joinNumberOutputRouteName]); + //Analog + VideoOutputRouteFeedbacks[outputName]?.LinkInputSig(trilist.UShortInput[joinNumberOutputRoute]); + + trilist.SetUShortSigAction(joinNumberOutputRoute, (a) => ExecuteNumericSwitch(a, (ushort)output, eRoutingSignalType.AudioVideo)); + + //Serial + OutputNameFeedbacks[outputName]?.LinkInputSig(trilist.StringInput[joinNumberOutputName]); + OutputRouteNameFeedbacks[outputName]?.LinkInputSig(trilist.StringInput[joinNumberOutputRouteName]); + } + } + else + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "LinkToApi: OutputNames is null. Skipping output linking.", this); } _Chassis.OnlineStatusChange += Chassis_OnlineStatusChange; From be4b8c7ce75c0fa6f8b6de39ef67ca992b6125fb Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 22 Jan 2026 14:03:49 -0600 Subject: [PATCH 28/50] fix: add try/catch to fireUpdate to isolate bool feedback exception --- src/Chassis/HdMdNxM4KzEController.cs | 299 ++++++++++++++++++++------- 1 file changed, 225 insertions(+), 74 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 86485cd..2f93fae 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -19,7 +19,6 @@ public class HdMdNxM4kZEController : CrestronGenericBridgeableBaseDevice, IRouti { private HdMdNxM4kzE _Chassis; - //IroutingNumericEvent public event EventHandler NumericSwitchChange; public Dictionary InputNames { get; set; } @@ -40,6 +39,13 @@ public class HdMdNxM4kZEController : CrestronGenericBridgeableBaseDevice, IRouti #region Constructor + /// + /// Constructor for the HdMdNxM4kZEController + /// + /// The device key. + /// The device name. + /// The HdMdNxM4kzE chassis instance. + /// The HdMdNxM4kE properties config. public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, HdMdNxM4kEPropertiesConfig props) : base(key, name, chassis) @@ -96,7 +102,7 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, } SetupInputs(); - SetupOutputs(); + SetupOutputs(); _Chassis.DMInputChange += Chassis_DMInputChange; _Chassis.DMOutputChange += Chassis_DMOutputChange; @@ -148,12 +154,12 @@ private void SetupInputs() VideoInputSyncFeedbacks.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}VideoInputSyncFeedback"), () => chassisInput?.VideoDetectedFeedback?.BoolValue ?? false)); InputNameFeedbacks.Add(new StringFeedback(string.Format($"{inputFbKeyPrefix}InputNameFeedback"), () => InputNames[index])); - + if (hdmiInput.HdmiInputPort == null) { Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "HdmiInputPort at index {index} is null. HDCP feedback will default to false.", this, index); } - + InputHdcpEnableFeedback.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}HdcpEnableFeedback"), () => hdmiInput?.HdmiInputPort?.HdcpSupportOnFeedback?.BoolValue ?? false)); } } @@ -217,24 +223,83 @@ private void OnSwitchChange(RoutingNumericEventArgs e) NumericSwitchChange?.Invoke(this, e); } + /// + /// Enables HDCP on the specified input port. + /// + /// The input port number to enable HDCP on. public void EnableHdcp(uint port) { if (port > _Chassis.NumberOfInputs) return; if (port <= 0) return; - _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOn(); - InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); + var hdmiInput = _Chassis.HdmiInputs[port]; + if (hdmiInput?.HdmiInputPort == null) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "EnableHdcp: HdmiInputPort at index {port} is null. Cannot enable HDCP.", this, port); + return; + } + + hdmiInput.HdmiInputPort.HdcpSupportOn(); + + if (InputNames.ContainsKey(port)) + { + var inputName = InputNames[port]; + var feedback = InputHdcpEnableFeedback.FirstOrDefault(f => f.Key == inputName); + if (feedback == null) + { + return; + } + try + { + feedback.FireUpdate(); + } + catch (Exception ex) + { + Debug.LogError(this, $"EnableHdcp: Exception occurred while updating HDCP feedback for input {inputName}: {ex.Message}"); + } + } } + /// + /// Disables HDCP on the specified input port. + /// + /// The input port number to disable HDCP on. public void DisableHdcp(uint port) { if (port > _Chassis.NumberOfInputs) return; if (port <= 0) return; - _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOff(); - InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); + var hdmiInput = _Chassis.HdmiInputs[port]; + if (hdmiInput?.HdmiInputPort == null) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "DisableHdcp: HdmiInputPort at index {port} is null. Cannot disable HDCP.", this, port); + return; + } + + hdmiInput.HdmiInputPort.HdcpSupportOff(); + + if (InputNames.ContainsKey(port)) + { + var inputName = InputNames[port]; + var feedback = InputHdcpEnableFeedback.FirstOrDefault(f => f.Key == inputName); + if (feedback == null) + { + return; + } + try + { + feedback.FireUpdate(); + } + catch (Exception ex) + { + Debug.LogError(this, $"DisableHdcp: Exception occurred while updating HDCP feedback for input {inputName}: {ex.Message}"); + } + } } + /// + /// Enables AutoRoute on the chassis if supported. + /// public void EnableAutoRoute() { if (_Chassis.NumberOfOutputs > 1) return; @@ -247,6 +312,9 @@ public void EnableAutoRoute() Debug.LogVerbose(this, "EnableAutoRoute: AutoRoute is not supported on this chassis."); } + /// + /// Disables AutoRoute on the chassis if supported. + /// public void DisableAutoRoute() { if (_Chassis.NumberOfOutputs > 1) return; @@ -261,8 +329,11 @@ public void DisableAutoRoute() #region FeedbackCollection Methods - + + /// + /// Adds all feedback collections to the Feedbacks collection. + /// public void AddFeedbackCollections() { AddFeedbackToList(IsOnline); @@ -307,7 +378,9 @@ public void AddFeedbackCollections() } } - //Add Individual Feedbacks + /// + /// Adds a feedback to the Feedbacks collection if it does not already exist. + /// public void AddFeedbackToList(Essentials.Core.Feedback newFb) { if (newFb == null) return; @@ -323,6 +396,12 @@ public void AddFeedbackToList(Essentials.Core.Feedback newFb) #region IRouting Members + /// + /// Executes a switch from input to output for the specified signal type. + /// + /// The input selector object. + /// The output selector object. + /// The type of signal to switch. public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) { var input = inputSelector as HdMdNxM4kzEHdmiInput; @@ -345,6 +424,12 @@ public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingS #region IRoutingNumeric Members + /// + /// Executes a numeric switch from input to output for the specified signal type. + /// + /// The input selector number. + /// The output selector number. + /// The type of signal to switch. public void ExecuteNumericSwitch(ushort inputSelector, ushort outputSelector, eRoutingSignalType signalType) { Debug.LogInformation(this, $"ExecuteNumericSwitch: inputSelector={inputSelector} outputSelector={outputSelector}"); @@ -363,6 +448,13 @@ public void ExecuteNumericSwitch(ushort inputSelector, ushort outputSelector, eR #region Bridge Linking + /// + /// Links the device to the API bridge. + /// + /// The trilist to link to. + /// The join start number. + /// The join map key. + /// The EISC API bridge. public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) { var joinMap = new HdMdNxM4kEControllerJoinMap(joinStart); @@ -457,7 +549,7 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join VideoOutputRouteFeedbacks[outputName]?.LinkInputSig(trilist.UShortInput[joinNumberOutputRoute]); trilist.SetUShortSigAction(joinNumberOutputRoute, (a) => ExecuteNumericSwitch(a, (ushort)output, eRoutingSignalType.AudioVideo)); - + //Serial OutputNameFeedbacks[outputName]?.LinkInputSig(trilist.StringInput[joinNumberOutputName]); OutputRouteNameFeedbacks[outputName]?.LinkInputSig(trilist.StringInput[joinNumberOutputRouteName]); @@ -479,51 +571,58 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join }; } - + /* private void UpdateFeedbacks() { - IsOnline?.FireUpdate(); - DeviceNameFeedback?.FireUpdate(); - AutoRouteFeedback?.FireUpdate(); - - foreach (var item in VideoInputSyncFeedbacks) + try { - item.FireUpdate(); - } + IsOnline.FireUpdate(); + DeviceNameFeedback.FireUpdate(); + AutoRouteFeedback.FireUpdate(); - foreach (var item in VideoOutputRouteFeedbacks) - { - item.FireUpdate(); - } + foreach (var item in VideoInputSyncFeedbacks) + { + item.FireUpdate(); + } - foreach (var item in InputHdcpEnableFeedback) - { - item.FireUpdate(); - } + foreach (var item in VideoOutputRouteFeedbacks) + { + item.FireUpdate(); + } - foreach (var item in InputNameFeedbacks) - { - item.FireUpdate(); - } + foreach (var item in InputHdcpEnableFeedback) + { + item.FireUpdate(); + } - foreach (var item in OutputNameFeedbacks) - { - item.FireUpdate(); - } + foreach (var item in InputNameFeedbacks) + { + item.FireUpdate(); + } - foreach (var item in OutputRouteNameFeedbacks) - { - item.FireUpdate(); - } + foreach (var item in OutputNameFeedbacks) + { + item.FireUpdate(); + } + + foreach (var item in OutputRouteNameFeedbacks) + { + item.FireUpdate(); + } - foreach (var item in Feedbacks) + foreach (var item in Feedbacks) + { + // TODO - Remove after testing + Debug.LogInformation(this, $"UpdateFeedbacks: Firing feedback for {item.Key}"); + item.FireUpdate(); + } + } + catch (Exception ex) { - // TODO - Remove after testing - Debug.LogInformation(this, $"UpdateFeedbacks: Firing feedback for {item.Key}"); - item.FireUpdate(); + Debug.LogError(this, $"UpdateFeedbacks: Exception occurred while updating feedbacks: {ex.Message}"); } } - + */ #endregion #region Events @@ -533,8 +632,15 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice // TODO - Remove after testing Debug.LogInformation(this, $"Chassis_OnlineStatusChange: DeviceOnline = {args.DeviceOnLine}"); - IsOnline.FireUpdate(); - + try + { + IsOnline.FireUpdate(); + } + catch (Exception ex) + { + Debug.LogError(this, $"Chassis_OnlineStatusChange: Exception occurred while updating IsOnline feedback: {ex.Message}"); + } + if (!args.DeviceOnLine) return; // TODO - Remove after testing @@ -542,14 +648,28 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice foreach (var feedback in Feedbacks) { - // TODO - Remove after testing - Debug.LogInformation(this, $"Chassis_OnlineStatusChange: Firing update for {feedback.Key}"); - feedback.FireUpdate(); + try + { + // TODO - Remove after testing + Debug.LogInformation(this, $"Chassis_OnlineStatusChange: Firing update for {feedback.Key}"); + feedback.FireUpdate(); + } + catch (Exception ex) + { + Debug.LogError(this, $"Chassis_OnlineStatusChange: Exception occurred while updating feedback {feedback.Key}: {ex.Message}"); + } } if (_Chassis is HdMd4x14kzE) { - AutoRouteFeedback.FireUpdate(); + try + { + AutoRouteFeedback.FireUpdate(); + } + catch (Exception ex) + { + Debug.LogError(this, $"Chassis_OnlineStatusChange: Exception occurred while updating AutoRouteFeedback: {ex.Message}"); + } } } @@ -557,8 +677,8 @@ void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) { // TODO - Remove after testing var eventName = typeof(DMOutputEventIds) - .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static) - .FirstOrDefault(f => f.IsLiteral && (int)f.GetValue(null) == args.EventId)?.Name ?? args.EventId.ToString(); + .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static) + .FirstOrDefault(f => f.IsLiteral && (int)f.GetValue(null) == args.EventId)?.Name ?? args.EventId.ToString(); Debug.LogInformation(this, $"Chassis_DMOutputChange: received {eventName} (id-{args.EventId}); Index = {args.Index}; Number = {args.Number}; Stream = {args.Stream} "); switch (args.EventId) @@ -576,8 +696,15 @@ void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) var inPort = InputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output].VideoOutFeedback); var outPort = OutputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output]); - feedback.FireUpdate(); - OnSwitchChange(new RoutingNumericEventArgs(output, inputNumber, outPort, inPort, eRoutingSignalType.AudioVideo)); + try + { + feedback.FireUpdate(); + OnSwitchChange(new RoutingNumericEventArgs(output, inputNumber, outPort, inPort, eRoutingSignalType.AudioVideo)); + } + catch (Exception ex) + { + Debug.LogError(this, $"Chassis_DMOutputChange: Exception occurred while updating {eventName} (id-{args.EventId}) {feedback.Key}: {ex.Message}"); + } } else { @@ -596,51 +723,75 @@ void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) void Chassis_DMInputChange(Switch device, DMInputEventArgs args) { var eventName = typeof(DMInputEventIds) - .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static) - .FirstOrDefault(f => f.IsLiteral && (int)f.GetValue(null) == args.EventId)?.Name ?? args.EventId.ToString(); + .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static) + .FirstOrDefault(f => f.IsLiteral && (int)f.GetValue(null) == args.EventId)?.Name ?? args.EventId.ToString(); - switch (args.EventId) - { - case DMInputEventIds.VideoDetectedEventId: + switch (args.EventId) + { + case DMInputEventIds.VideoDetectedEventId: + { + Debug.LogDebug(this, $"Chassis_DMInputChange: received {eventName} (id-{args.EventId}) | Updating VideoInputSyncFeedbacks"); + foreach (var item in VideoInputSyncFeedbacks) { - Debug.LogDebug(this, $"Chassis_DMInputChange: received {eventName} (id-{args.EventId}) | Updating VideoInputSyncFeedbacks"); - foreach (var item in VideoInputSyncFeedbacks) + try { item.FireUpdate(); } - break; + catch (Exception ex) + { + Debug.LogError(this, $"Chassis_DMInputChange: Exception occurred while updating {eventName} (id-{args.EventId}) {item.Key}: {ex.Message}"); + } } - case DMInputEventIds.InputNameFeedbackEventId: - case DMInputEventIds.InputNameEventId: - case DMInputEventIds.NameFeedbackEventId: + break; + } + case DMInputEventIds.InputNameFeedbackEventId: + case DMInputEventIds.InputNameEventId: + case DMInputEventIds.NameFeedbackEventId: + { + Debug.LogDebug(this, $"Chassis_DMInputChange: received {eventName} (id-{args.EventId}) | Input {args.Number} Name {_Chassis.HdmiInputs[args.Number].NameFeedback.StringValue}, updating InputNameFeedbacks"); + foreach (var item in InputNameFeedbacks) { - Debug.LogDebug(this, $"Chassis_DMInputChange: received {eventName} (id-{args.EventId}) | Input {args.Number} Name {_Chassis.HdmiInputs[args.Number].NameFeedback.StringValue}, updating InputNameFeedbacks"); - foreach (var item in InputNameFeedbacks) + try { item.FireUpdate(); } - break; - } - default: - { - Debug.LogDebug(this, $"Chassis_DMInputChange: Unhandled DM Input Event {eventName} (id-{args.EventId}), ignoring."); - break; + catch (Exception ex) + { + Debug.LogError(this, $"Chassis_DMInputChange: Exception occurred while updating {eventName} (id-{args.EventId}) {item.Key}: {ex.Message}"); + } } - } + break; + } + default: + { + Debug.LogDebug(this, $"Chassis_DMInputChange: Unhandled DM Input Event {eventName} (id-{args.EventId}), ignoring."); + break; + } } + } #endregion #region Factory + /// + /// Factory for creating HdMdNxM4kZEController devices + /// public class HdMdNxM4kZEControllerFactory : EssentialsPluginDeviceFactory { + /// + /// Constructor + /// public HdMdNxM4kZEControllerFactory() { MinimumEssentialsFrameworkVersion = "2.24.4"; TypeNames = new List() { "hdmd4x14kze", "hdmd4x24kze", "hdmd8x84kze" }; } + /// + /// Builds a HdMdNxM4kZEController device + /// + /// The device config public override EssentialsDevice BuildDevice(DeviceConfig dc) { Debug.LogDebug("Factory Attempting to create new HD-MD-NxM-4KZ-E Device"); From 53651bb523bd5fcb31f79f92290baa2da53dfbda Mon Sep 17 00:00:00 2001 From: Jason DeVito Date: Thu, 22 Jan 2026 14:26:06 -0600 Subject: [PATCH 29/50] fix: debug log levels and messages --- src/Chassis/HdMdNxM4KzEController.cs | 191 +++++++++++++++------------ 1 file changed, 106 insertions(+), 85 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 2f93fae..6fab833 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -69,37 +69,45 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, if (props.Inputs != null) { - foreach (var kvp in props.Inputs) + InputNames = props.Inputs; + foreach (var kvp in InputNames) { - Debug.LogDebug(this, "props.Inputs: {0}-{1}", kvp.Key, kvp.Value); + Debug.LogDebug(this, "InputNames: {0}-{1}", kvp.Key, kvp.Value); } - InputNames = props.Inputs; } if (props.Outputs != null) { - foreach (var kvp in props.Outputs) + OutputNames = props.Outputs; + foreach (var kvp in OutputNames) { - Debug.LogDebug(this, "props.Outputs: {0}-{1}", kvp.Key, kvp.Value); + Debug.LogDebug(this, "OutputNamess: {0}-{1}", kvp.Key, kvp.Value); } - OutputNames = props.Outputs; } - DeviceNameFeedback = new StringFeedback("DeviceName", () => Name); + try + { + DeviceNameFeedback = new StringFeedback("DeviceName", () => Name); - VideoInputSyncFeedbacks = new FeedbackCollection(); - VideoOutputRouteFeedbacks = new FeedbackCollection(); - InputNameFeedbacks = new FeedbackCollection(); - OutputNameFeedbacks = new FeedbackCollection(); - OutputRouteNameFeedbacks = new FeedbackCollection(); - InputHdcpEnableFeedback = new FeedbackCollection(); + VideoInputSyncFeedbacks = new FeedbackCollection(); + VideoOutputRouteFeedbacks = new FeedbackCollection(); + InputNameFeedbacks = new FeedbackCollection(); + OutputNameFeedbacks = new FeedbackCollection(); + OutputRouteNameFeedbacks = new FeedbackCollection(); + InputHdcpEnableFeedback = new FeedbackCollection(); - InputPorts = new RoutingPortCollection(); - OutputPorts = new RoutingPortCollection(); + InputPorts = new RoutingPortCollection(); + OutputPorts = new RoutingPortCollection(); - if (_Chassis is HdMd4x14kzE _chassis) + if (_Chassis is HdMd4x14kzE _chassis) + { + AutoRouteFeedback = new BoolFeedback("AutoRouteFeedback", () => _chassis.AutoRouteOnFeedback?.BoolValue ?? false); + } + } + catch(Exception ex) { - AutoRouteFeedback = new BoolFeedback("AutoRouteFeedback", () => _chassis.AutoRouteOnFeedback?.BoolValue ?? false); + Debug.LogError(this, "Constructor Exception: {ex}", ex); } + SetupInputs(); SetupOutputs(); @@ -114,100 +122,113 @@ private void SetupInputs() { if (InputNames == null) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "SetupInputs: InputNames is null. Ensure 'inputs' is defined in the device configuration.", this); + Debug.LogError(this, "SetupInputs: InputNames is null. Ensure 'inputs' is defined in the device configuration."); return; } - - foreach (var kvp in InputNames) + try { - var index = kvp.Key; - var inputName = kvp.Value; - var inputFbKeyPrefix = inputName.Replace(" ", "").Trim(); - - if (index < 1 || index > _Chassis.NumberOfInputs) + foreach (var kvp in InputNames) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "SetupInputs: Input index {index} is out of range (1-{max}). Skipping.", this, index, _Chassis.NumberOfInputs); - continue; - } + var index = kvp.Key; + var inputName = kvp.Value; + var inputFbKeyPrefix = inputName.Replace(" ", "").Trim(); - var hdmiInput = _Chassis.HdmiInputs[index]; - if (hdmiInput == null) - { - Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "SetupInputs: HdmiInput at index {index} is null. Skipping.", this, index); - continue; - } + if (index < 1 || index > _Chassis.NumberOfInputs) + { + Debug.LogWarning(this, "SetupInputs: Input index {index} is out of range (1-{max}). Skipping.", index, _Chassis.NumberOfInputs); + continue; + } - var chassisInput = _Chassis.Inputs[index]; - if (chassisInput == null) - { - Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "SetupInputs: Chassis Input at index {index} is null. Skipping.", this, index); - continue; - } + var hdmiInput = _Chassis.HdmiInputs[index]; + if (hdmiInput == null) + { + Debug.LogError(this, "SetupInputs: HdmiInput at index {index} is null. Skipping.", index); + continue; + } - hdmiInput.Name.StringValue = inputName; + var chassisInput = _Chassis.Inputs[index]; + if (chassisInput == null) + { + Debug.LogError(this, "SetupInputs: Chassis Input at index {index} is null. Skipping.", index); + continue; + } - InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, hdmiInput, this) - { - FeedbackMatchObject = hdmiInput - }); + hdmiInput.Name.StringValue = inputName; - VideoInputSyncFeedbacks.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}VideoInputSyncFeedback"), () => chassisInput?.VideoDetectedFeedback?.BoolValue ?? false)); - InputNameFeedbacks.Add(new StringFeedback(string.Format($"{inputFbKeyPrefix}InputNameFeedback"), () => InputNames[index])); + InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, hdmiInput, this) + { + FeedbackMatchObject = hdmiInput + }); - if (hdmiInput.HdmiInputPort == null) - { - Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "HdmiInputPort at index {index} is null. HDCP feedback will default to false.", this, index); - } + VideoInputSyncFeedbacks.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}VideoInputSyncFeedback"), () => chassisInput?.VideoDetectedFeedback?.BoolValue ?? false)); + InputNameFeedbacks.Add(new StringFeedback(string.Format($"{inputFbKeyPrefix}InputNameFeedback"), () => InputNames[index])); + + if (hdmiInput.HdmiInputPort == null) + { + Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "HdmiInputPort at index {index} is null. HDCP feedback will default to false.", this, index); + } - InputHdcpEnableFeedback.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}HdcpEnableFeedback"), () => hdmiInput?.HdmiInputPort?.HdcpSupportOnFeedback?.BoolValue ?? false)); + InputHdcpEnableFeedback.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}HdcpEnableFeedback"), () => hdmiInput?.HdmiInputPort?.HdcpSupportOnFeedback?.BoolValue ?? false)); + } } + catch(Exception ex) + { + Debug.LogError(this, "SetupInputs: Exception {ex}", ex); + } } private void SetupOutputs() { if (OutputNames == null) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "SetupOutputs: OutputNames is null. Ensure 'outputs' is defined in the device configuration.", this); + Debug.LogWarning(this, "SetupOutputs: OutputNames is null. Ensure 'outputs' is defined in the device configuration."); return; } - foreach (var kvp in OutputNames) + try { - var index = kvp.Key; - var outputName = kvp.Value; - var outputFbKeyPrefix = outputName.Replace(" ", "").Trim(); - - if (index < 1 || index > _Chassis.NumberOfOutputs) + foreach (var kvp in OutputNames) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "SetupOutputs: Output index {index} is out of range (1-{max}). Skipping.", this, index, _Chassis.NumberOfOutputs); - continue; - } + var index = kvp.Key; + var outputName = kvp.Value; + var outputFbKeyPrefix = outputName.Replace(" ", "").Trim(); - var hdmiOutput = _Chassis.HdmiOutputs[index]; - if (hdmiOutput == null) - { - Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "SetupOutputs: HdmiOutput at index {index} is null. Skipping.", this, index); - continue; - } + if (index < 1 || index > _Chassis.NumberOfOutputs) + { + Debug.LogWarning(this, "SetupOutputs: Output index {index} is out of range (1-{max}). Skipping.", index, _Chassis.NumberOfOutputs); + continue; + } - var chassisOutput = _Chassis.Outputs[index]; - if (chassisOutput == null) - { - Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "SetupOutputs: Chassis Output at index {index} is null. Skipping.", this, index); - continue; - } + var hdmiOutput = _Chassis.HdmiOutputs[index]; + if (hdmiOutput == null) + { + Debug.LogWarning(this, "SetupOutputs: HdmiOutput at index {index} is null. Skipping.", index); + continue; + } - OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, hdmiOutput, this) - { - FeedbackMatchObject = hdmiOutput - }); + var chassisOutput = _Chassis.Outputs[index]; + if (chassisOutput == null) + { + Debug.LogError(this, "SetupOutputs: Chassis Output at index {index} is null. Skipping.", index); + continue; + } - VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputFbKeyPrefix}VideoOutputRouteFeedback"), () => chassisOutput.VideoOutFeedback == null ? 0 : (int)chassisOutput.VideoOutFeedback.Number)); - OutputNameFeedbacks.Add(new StringFeedback(string.Format($"{outputFbKeyPrefix}OutputNameFeedback"), () => OutputNames[index])); - OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputFbKeyPrefix}OutputRouteNameFeedback"), () => chassisOutput.VideoOutFeedback == null ? NoRouteText : chassisOutput.VideoOutFeedback.NameFeedback.StringValue)); + OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, hdmiOutput, this) + { + FeedbackMatchObject = hdmiOutput + }); + + VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputFbKeyPrefix}VideoOutputRouteFeedback"), () => chassisOutput.VideoOutFeedback == null ? 0 : (int)chassisOutput.VideoOutFeedback.Number)); + OutputNameFeedbacks.Add(new StringFeedback(string.Format($"{outputFbKeyPrefix}OutputNameFeedback"), () => OutputNames[index])); + OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputFbKeyPrefix}OutputRouteNameFeedback"), () => chassisOutput.VideoOutFeedback == null ? NoRouteText : chassisOutput.VideoOutFeedback.NameFeedback.StringValue)); + } } + catch(Exception ex) + { + Debug.LogError("SetupOutputs: Exception {ex}", ex); + } } #endregion @@ -235,7 +256,7 @@ public void EnableHdcp(uint port) var hdmiInput = _Chassis.HdmiInputs[port]; if (hdmiInput?.HdmiInputPort == null) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "EnableHdcp: HdmiInputPort at index {port} is null. Cannot enable HDCP.", this, port); + Debug.LogWarning(this, "EnableHdcp: HdmiInputPort at index {port} is null. Cannot enable HDCP.", port); return; } @@ -272,7 +293,7 @@ public void DisableHdcp(uint port) var hdmiInput = _Chassis.HdmiInputs[port]; if (hdmiInput?.HdmiInputPort == null) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "DisableHdcp: HdmiInputPort at index {port} is null. Cannot disable HDCP.", this, port); + Debug.LogWarning(this, "DisableHdcp: HdmiInputPort at index {port} is null. Cannot disable HDCP.", port); return; } From c058f9f3889a8a32c15d821c5a36a3538751e7bc Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 22 Jan 2026 16:06:27 -0600 Subject: [PATCH 30/50] feat: add support for HdMdNxM4kzE and PriorityRoute feedback --- src/Chassis/HdMdNxM4KzEController.cs | 173 +++++++++++++++++++++++---- 1 file changed, 149 insertions(+), 24 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 6fab833..b069b1f 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -10,6 +10,7 @@ using PepperDash.Essentials.DM.Config; using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Config; +using Crestron.SimplSharpPro; namespace PepperDash.Essentials.DM.Chassis @@ -35,6 +36,7 @@ public class HdMdNxM4kZEController : CrestronGenericBridgeableBaseDevice, IRouti public FeedbackCollection InputHdcpEnableFeedback { get; private set; } public StringFeedback DeviceNameFeedback { get; private set; } public BoolFeedback AutoRouteFeedback { get; private set; } + public BoolFeedback PriorityRouteFeedback { get; private set; } public string NoRouteText { get; private set; } #region Constructor @@ -66,7 +68,6 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, NoRouteText = props.NoRouteText ?? "None"; - if (props.Inputs != null) { InputNames = props.Inputs; @@ -84,7 +85,7 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, } } - try + try { DeviceNameFeedback = new StringFeedback("DeviceName", () => Name); @@ -98,22 +99,27 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, InputPorts = new RoutingPortCollection(); OutputPorts = new RoutingPortCollection(); - if (_Chassis is HdMd4x14kzE _chassis) + if (_Chassis is HdMdNxM4kzE _chassis_M4kzE) + { + AutoRouteFeedback = new BoolFeedback("AutoRouteFeedback", () => _chassis_M4kzE.AutoRouteOnFeedback?.BoolValue ?? false); + } + if(_Chassis is HdMd4xX4kzE _chassis_X4kzE) { - AutoRouteFeedback = new BoolFeedback("AutoRouteFeedback", () => _chassis.AutoRouteOnFeedback?.BoolValue ?? false); + PriorityRouteFeedback = new BoolFeedback("PriorityRouteFeedback", () => _chassis_X4kzE.PriorityRouteOnFeedback?.BoolValue ?? false); } } - catch(Exception ex) + catch (Exception ex) { Debug.LogError(this, "Constructor Exception: {ex}", ex); } - + SetupInputs(); SetupOutputs(); _Chassis.DMInputChange += Chassis_DMInputChange; _Chassis.DMOutputChange += Chassis_DMOutputChange; + _Chassis.BaseEvent += Chassis_BaseEvent; AddPostActivationAction(AddFeedbackCollections); } @@ -125,7 +131,7 @@ private void SetupInputs() Debug.LogError(this, "SetupInputs: InputNames is null. Ensure 'inputs' is defined in the device configuration."); return; } - try + try { foreach (var kvp in InputNames) { @@ -172,10 +178,10 @@ private void SetupInputs() InputHdcpEnableFeedback.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}HdcpEnableFeedback"), () => hdmiInput?.HdmiInputPort?.HdcpSupportOnFeedback?.BoolValue ?? false)); } } - catch(Exception ex) + catch (Exception ex) { Debug.LogError(this, "SetupInputs: Exception {ex}", ex); - } + } } private void SetupOutputs() @@ -225,10 +231,10 @@ private void SetupOutputs() OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputFbKeyPrefix}OutputRouteNameFeedback"), () => chassisOutput.VideoOutFeedback == null ? NoRouteText : chassisOutput.VideoOutFeedback.NameFeedback.StringValue)); } } - catch(Exception ex) + catch (Exception ex) { Debug.LogError("SetupOutputs: Exception {ex}", ex); - } + } } #endregion @@ -319,14 +325,14 @@ public void DisableHdcp(uint port) } /// - /// Enables AutoRoute on the chassis if supported. + /// Enables AutoRoute on the chassis if supported. Auto route is supported by HdMdNxM4kzE /// public void EnableAutoRoute() { if (_Chassis.NumberOfOutputs > 1) return; - if (_Chassis is HdMd4x14kzE _chassis) + if (_Chassis is HdMdNxM4kzE _chassis_M4kzE) { - _chassis.AutoRouteOn(); + _chassis_M4kzE.AutoRouteOn(); return; } @@ -334,20 +340,48 @@ public void EnableAutoRoute() } /// - /// Disables AutoRoute on the chassis if supported. + /// Disables AutoRoute on the chassis if supported. Auto route is supported by HdMdNxM4kzE /// public void DisableAutoRoute() { if (_Chassis.NumberOfOutputs > 1) return; - if (_Chassis is HdMd4x14kzE _chassis) + if (_Chassis is HdMdNxM4kzE _chassis_M4kzE) { - _chassis.AutoRouteOff(); + _chassis_M4kzE.AutoRouteOff(); return; } Debug.LogVerbose(this, "DisableAutoRoute: AutoRoute is not supported on this chassis."); } + /// + /// Enables Priority Route on the chassis if supported. Priority route is support by HdMd4xX4kzE + /// + public void EnablePriorityRoute() + { + if (_Chassis is HdMd4xX4kzE _chassis_X4kzE) + { + _chassis_X4kzE.PriorityRouteOn(); + return; + } + + Debug.LogVerbose(this, "EnablePriorityRoute: Priority Route is not supported on this chassis."); + } + + /// + /// Disables Priority Route on the chassis if supported. Priority route is support by HdMd4xX4kzE + /// + public void DisablePriorityRoute() + { + if (_Chassis is HdMd4xX4kzE _chassis_X4kzE) + { + _chassis_X4kzE.PriorityRouteOff(); + return; + } + + Debug.LogVerbose(this, "DisablePriorityRoute: Priority Route is not supported on this chassis."); + } + #region FeedbackCollection Methods @@ -357,7 +391,9 @@ public void DisableAutoRoute() /// public void AddFeedbackCollections() { - AddFeedbackToList(IsOnline); + if(IsOnline != null) + AddFeedbackToList(IsOnline); + AddFeedbackToList(DeviceNameFeedback); if (AutoRouteFeedback != null) @@ -494,18 +530,28 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join Debug.LogInformation(this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); } - IsOnline?.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + if( IsOnline != null) IsOnline?.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + DeviceNameFeedback?.LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); - if (_Chassis is HdMd4x14kzE _chassis) + if (_Chassis is HdMdNxM4kzE _chassis_M4kzE) { - Debug.LogInformation(this, $"LinkToApi: _Chassis is HdMd4x14kzE, setting up AutoRoute links > joinNumber = {joinMap.EnableAutoRoute.JoinNumber}"); + Debug.LogInformation(this, $"LinkToApi: _Chassis is HdMdNxM4kzE, setting up AutoRoute links"); - trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoRouteOn()); - trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis.AutoRouteOff()); + trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis_M4kzE.AutoRouteOn()); + trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis_M4kzE.AutoRouteOff()); AutoRouteFeedback?.LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); } + if(_Chassis is HdMd4xX4kzE _chassis_X4kzE) + { + Debug.LogInformation(this, $"LinkToApi: _Chassis is HdMd4xX4kzE, setting up PriorityRoute links - not implemented"); + + // trilist.SetSigTrueAction(joinMap.EnablePriorityRoute.JoinNumber, () => _chassis_X4kzE.PriorityRouteOn()); + // trilist.SetSigFalseAction(joinMap.EnablePriorityRoute.JoinNumber, () => _chassis_X4kzE.PriorityRouteOff()); + // PriorityRouteFeedback?.LinkInputSig(trilist.BooleanInput[joinMap.EnablePriorityRoute.JoinNumber]); + } + if (InputNames != null) { foreach (var kvp in InputNames) @@ -648,6 +694,15 @@ private void UpdateFeedbacks() #region Events + private void Chassis_BaseEvent(GenericBase device, BaseEventArgs args) + { + var eventName = typeof(BaseEventArgs) + .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static) + .FirstOrDefault(f => f.IsLiteral && (int)f.GetValue(null) == args.EventId)?.Name ?? args.EventId.ToString(); + Debug.LogInformation(this, $"Chassis_BaseEvent: received {eventName} (id-{args.EventId}) received from device {device.GetType().Name}"); + } + + void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice, Crestron.SimplSharpPro.OnlineOfflineEventArgs args) { // TODO - Remove after testing @@ -681,7 +736,7 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice } } - if (_Chassis is HdMd4x14kzE) + if (_Chassis is HdMd4xX4kzE) { try { @@ -692,6 +747,18 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice Debug.LogError(this, $"Chassis_OnlineStatusChange: Exception occurred while updating AutoRouteFeedback: {ex.Message}"); } } + + if(_Chassis is HdMd4xX4kzE) + { + try + { + PriorityRouteFeedback.FireUpdate(); + } + catch (Exception ex) + { + Debug.LogError(this, $"Chassis_OnlineStatusChange: Exception occurred while updating PriorityRouteFeedback: {ex.Message}"); + } + } } void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) @@ -733,6 +800,50 @@ void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) } break; } + case DMOutputEventIds.AutoModeOffEventId: + case DMOutputEventIds.AutoModeOnEventId: + { + Debug.LogDebug(this, $"Chassis_DMOutputChange: received {eventName} (id-{args.EventId}) | Updating AutoRouteFeedback"); + try + { + AutoRouteFeedback?.FireUpdate(); + } + catch (Exception ex) + { + Debug.LogError(this, $"Chassis_DMOutputChange: Exception occurred while updating {eventName} (id-{args.EventId}) AutoRouteFeedback: {ex.Message}"); + } + break; + } + case DMOutputEventIds.InputPrioritiesFeedbackEventId: + { + Debug.LogDebug(this, $"Chassis_DMOutputChange: received {eventName} (id-{args.EventId}) | Updating PriorityRouteFeedback"); + try + { + PriorityRouteFeedback?.FireUpdate(); + } + catch (Exception ex) + { + Debug.LogError(this, $"Chassis_DMOutputChange: Exception occurred while updating {eventName} (id-{args.EventId}) PriorityRouteFeedback: {ex.Message}"); + } + break; + } + case DMOutputEventIds.OutputNameEventId: + case DMOutputEventIds.NameFeedbackEventId: + { + Debug.LogDebug(this, $"Chassis_DMOutputChange: received {eventName} (id-{args.EventId}) | Output {args.Number} Name {_Chassis.HdmiOutputs[args.Number].NameFeedback.StringValue}, updating OutputNameFeedbacks and OutputRouteNameFeedbacks"); + foreach (var item in OutputNameFeedbacks) + { + try + { + item.FireUpdate(); + } + catch (Exception ex) + { + Debug.LogError(this, $"Chassis_DMOutputChange: Exception occurred while updating {eventName} (id-{args.EventId}) {item.Key}: {ex.Message}"); + } + } + break; + } default: { Debug.LogDebug(this, $"Chassis_DMOutputChange: Unhandled DM Output Event {eventName} (id-{args.EventId}), ignoring."); @@ -749,6 +860,7 @@ void Chassis_DMInputChange(Switch device, DMInputEventArgs args) switch (args.EventId) { + case DMInputEventIds.SourceSyncEventId: case DMInputEventIds.VideoDetectedEventId: { Debug.LogDebug(this, $"Chassis_DMInputChange: received {eventName} (id-{args.EventId}) | Updating VideoInputSyncFeedbacks"); @@ -783,6 +895,19 @@ void Chassis_DMInputChange(Switch device, DMInputEventArgs args) } break; } + case DMInputEventIds.PriorityEventId: + { + Debug.LogDebug(this, $"Chassis_DMInputChange: received {eventName} (id-{args.EventId}) | Updating PriorityRouteFeedback"); + try + { + PriorityRouteFeedback?.FireUpdate(); + } + catch (Exception ex) + { + Debug.LogError(this, $"Chassis_DMInputChange: Exception occurred while updating {eventName} (id-{args.EventId}) PriorityRouteFeedback: {ex.Message}"); + } + break; + } default: { Debug.LogDebug(this, $"Chassis_DMInputChange: Unhandled DM Input Event {eventName} (id-{args.EventId}), ignoring."); From 84ae8b7bf67753d7bdf47db735997983e9c87f90 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Fri, 23 Jan 2026 15:57:32 -0600 Subject: [PATCH 31/50] fix: enhance error handling and logging in HdMdNxM4KzEController --- src/Chassis/HdMdNxM4KzEController.cs | 489 ++++++++++++--------------- 1 file changed, 210 insertions(+), 279 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index b069b1f..41f940d 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -11,6 +11,7 @@ using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Config; using Crestron.SimplSharpPro; +using PepperDash.Core.Logging; namespace PepperDash.Essentials.DM.Chassis @@ -85,35 +86,39 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, } } - try + DeviceNameFeedback = new StringFeedback("DeviceName", () => { - DeviceNameFeedback = new StringFeedback("DeviceName", () => Name); + try { return Name; } + catch { this.LogError("Error getting DeviceNameFeedback"); return ""; } + }); - VideoInputSyncFeedbacks = new FeedbackCollection(); - VideoOutputRouteFeedbacks = new FeedbackCollection(); - InputNameFeedbacks = new FeedbackCollection(); - OutputNameFeedbacks = new FeedbackCollection(); - OutputRouteNameFeedbacks = new FeedbackCollection(); - InputHdcpEnableFeedback = new FeedbackCollection(); + VideoInputSyncFeedbacks = new FeedbackCollection(); + VideoOutputRouteFeedbacks = new FeedbackCollection(); + InputNameFeedbacks = new FeedbackCollection(); + OutputNameFeedbacks = new FeedbackCollection(); + OutputRouteNameFeedbacks = new FeedbackCollection(); + InputHdcpEnableFeedback = new FeedbackCollection(); - InputPorts = new RoutingPortCollection(); - OutputPorts = new RoutingPortCollection(); + InputPorts = new RoutingPortCollection(); + OutputPorts = new RoutingPortCollection(); - if (_Chassis is HdMdNxM4kzE _chassis_M4kzE) - { - AutoRouteFeedback = new BoolFeedback("AutoRouteFeedback", () => _chassis_M4kzE.AutoRouteOnFeedback?.BoolValue ?? false); - } - if(_Chassis is HdMd4xX4kzE _chassis_X4kzE) + if (_Chassis is HdMdNxM4kzE _chassis_M4kzE) + { + AutoRouteFeedback = new BoolFeedback("AutoRouteFeedback", () => { - PriorityRouteFeedback = new BoolFeedback("PriorityRouteFeedback", () => _chassis_X4kzE.PriorityRouteOnFeedback?.BoolValue ?? false); - } + try { return _chassis_M4kzE.AutoRouteOnFeedback?.BoolValue ?? false; } + catch { this.LogError("Error getting AutoRouteFeedback"); return false; } + }); } - catch (Exception ex) + if (_Chassis is HdMd4xX4kzE _chassis_X4kzE) { - Debug.LogError(this, "Constructor Exception: {ex}", ex); + PriorityRouteFeedback = new BoolFeedback("PriorityRouteFeedback", () => + { + try { return _chassis_X4kzE.PriorityRouteOnFeedback?.BoolValue ?? false; } + catch { this.LogError("Error getting PriorityRouteFeedback"); return false; } + }); } - SetupInputs(); SetupOutputs(); @@ -128,59 +133,65 @@ private void SetupInputs() { if (InputNames == null) { - Debug.LogError(this, "SetupInputs: InputNames is null. Ensure 'inputs' is defined in the device configuration."); + this.LogError("SetupInputs: InputNames is null. Ensure 'inputs' is defined in the device configuration."); return; } - try - { - foreach (var kvp in InputNames) - { - var index = kvp.Key; - var inputName = kvp.Value; - var inputFbKeyPrefix = inputName.Replace(" ", "").Trim(); - if (index < 1 || index > _Chassis.NumberOfInputs) - { - Debug.LogWarning(this, "SetupInputs: Input index {index} is out of range (1-{max}). Skipping.", index, _Chassis.NumberOfInputs); - continue; - } + foreach (var kvp in InputNames) + { + var index = kvp.Key; + var inputName = kvp.Value; + var inputFbKeyPrefix = inputName.Replace(" ", "").Trim(); - var hdmiInput = _Chassis.HdmiInputs[index]; - if (hdmiInput == null) - { - Debug.LogError(this, "SetupInputs: HdmiInput at index {index} is null. Skipping.", index); - continue; - } + if (index < 1 || index > _Chassis.NumberOfInputs) + { + this.LogWarning("SetupInputs: Input index {index} is out of range (1-{max}). Skipping.", index, _Chassis.NumberOfInputs); + continue; + } - var chassisInput = _Chassis.Inputs[index]; - if (chassisInput == null) - { - Debug.LogError(this, "SetupInputs: Chassis Input at index {index} is null. Skipping.", index); - continue; - } + var hdmiInput = _Chassis.HdmiInputs[index]; + if (hdmiInput == null) + { + this.LogError("SetupInputs: HdmiInput at index {index} is null. Skipping.", index); + continue; + } - hdmiInput.Name.StringValue = inputName; + var chassisInput = _Chassis.Inputs[index]; + if (chassisInput == null) + { + this.LogError("SetupInputs: Chassis Input at index {index} is null. Skipping.", index); + continue; + } - InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, hdmiInput, this) - { - FeedbackMatchObject = hdmiInput - }); + hdmiInput.Name.StringValue = inputName; - VideoInputSyncFeedbacks.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}VideoInputSyncFeedback"), () => chassisInput?.VideoDetectedFeedback?.BoolValue ?? false)); - InputNameFeedbacks.Add(new StringFeedback(string.Format($"{inputFbKeyPrefix}InputNameFeedback"), () => InputNames[index])); + InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, hdmiInput, this) + { + FeedbackMatchObject = hdmiInput + }); - if (hdmiInput.HdmiInputPort == null) - { - Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "HdmiInputPort at index {index} is null. HDCP feedback will default to false.", this, index); - } + VideoInputSyncFeedbacks.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}VideoInputSyncFeedback"), () => + { + try { return chassisInput?.VideoDetectedFeedback?.BoolValue ?? false; } + catch { this.LogError($"Error getting VideoInputSyncFeedback for input {inputName}"); return false; } + })); + InputNameFeedbacks.Add(new StringFeedback(string.Format($"{inputFbKeyPrefix}InputNameFeedback"), () => + { + try { return InputNames[index]; } + catch { this.LogError($"Error getting InputNameFeedback for input {inputName}"); return ""; } + })); - InputHdcpEnableFeedback.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}HdcpEnableFeedback"), () => hdmiInput?.HdmiInputPort?.HdcpSupportOnFeedback?.BoolValue ?? false)); + if (hdmiInput.HdmiInputPort == null) + { + this.LogWarning("HdmiInputPort at index {index} is null. HDCP feedback will default to false.", index); } - } - catch (Exception ex) - { - Debug.LogError(this, "SetupInputs: Exception {ex}", ex); + + InputHdcpEnableFeedback.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}HdcpEnableFeedback"), () => + { + try { return hdmiInput?.HdmiInputPort?.HdcpSupportOnFeedback?.BoolValue ?? false; } + catch { this.LogError($"Error getting HdcpEnableFeedback for input {inputName}"); return false; } + })); } } @@ -188,52 +199,57 @@ private void SetupOutputs() { if (OutputNames == null) { - Debug.LogWarning(this, "SetupOutputs: OutputNames is null. Ensure 'outputs' is defined in the device configuration."); + this.LogWarning("SetupOutputs: OutputNames is null. Ensure 'outputs' is defined in the device configuration."); return; } - try + foreach (var kvp in OutputNames) { - foreach (var kvp in OutputNames) - { - var index = kvp.Key; - var outputName = kvp.Value; - var outputFbKeyPrefix = outputName.Replace(" ", "").Trim(); + var index = kvp.Key; + var outputName = kvp.Value; + var outputFbKeyPrefix = outputName.Replace(" ", "").Trim(); - if (index < 1 || index > _Chassis.NumberOfOutputs) - { - Debug.LogWarning(this, "SetupOutputs: Output index {index} is out of range (1-{max}). Skipping.", index, _Chassis.NumberOfOutputs); - continue; - } + if (index < 1 || index > _Chassis.NumberOfOutputs) + { + this.LogWarning("SetupOutputs: Output index {index} is out of range (1-{max}). Skipping.", index, _Chassis.NumberOfOutputs); + continue; + } - var hdmiOutput = _Chassis.HdmiOutputs[index]; - if (hdmiOutput == null) - { - Debug.LogWarning(this, "SetupOutputs: HdmiOutput at index {index} is null. Skipping.", index); - continue; - } + var hdmiOutput = _Chassis.HdmiOutputs[index]; + if (hdmiOutput == null) + { + this.LogWarning("SetupOutputs: HdmiOutput at index {index} is null. Skipping.", index); + continue; + } - var chassisOutput = _Chassis.Outputs[index]; - if (chassisOutput == null) - { - Debug.LogError(this, "SetupOutputs: Chassis Output at index {index} is null. Skipping.", index); - continue; - } + var chassisOutput = _Chassis.Outputs[index]; + if (chassisOutput == null) + { + this.LogError("SetupOutputs: Chassis Output at index {index} is null. Skipping.", index); + continue; + } - OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, hdmiOutput, this) - { - FeedbackMatchObject = hdmiOutput - }); + OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, hdmiOutput, this) + { + FeedbackMatchObject = hdmiOutput + }); - VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputFbKeyPrefix}VideoOutputRouteFeedback"), () => chassisOutput.VideoOutFeedback == null ? 0 : (int)chassisOutput.VideoOutFeedback.Number)); - OutputNameFeedbacks.Add(new StringFeedback(string.Format($"{outputFbKeyPrefix}OutputNameFeedback"), () => OutputNames[index])); - OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputFbKeyPrefix}OutputRouteNameFeedback"), () => chassisOutput.VideoOutFeedback == null ? NoRouteText : chassisOutput.VideoOutFeedback.NameFeedback.StringValue)); - } - } - catch (Exception ex) - { - Debug.LogError("SetupOutputs: Exception {ex}", ex); + VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputFbKeyPrefix}VideoOutputRouteFeedback"), () => + { + try { return chassisOutput.VideoOutFeedback == null ? 0 : (int)chassisOutput.VideoOutFeedback.Number; } + catch { this.LogError($"Error getting VideoOutputRouteFeedback for output {outputName}"); return 0; } + })); + OutputNameFeedbacks.Add(new StringFeedback(string.Format($"{outputFbKeyPrefix}OutputNameFeedback"), () => + { + try { return OutputNames[index]; } + catch { this.LogError($"Error getting OutputNameFeedback for output {outputName}"); return ""; } + })); + OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputFbKeyPrefix}OutputRouteNameFeedback"), () => + { + try { return chassisOutput.VideoOutFeedback == null ? NoRouteText : chassisOutput.VideoOutFeedback.NameFeedback.StringValue; } + catch { this.LogError($"Error getting OutputRouteNameFeedback for output {outputName}"); return NoRouteText; } + })); } } @@ -262,7 +278,7 @@ public void EnableHdcp(uint port) var hdmiInput = _Chassis.HdmiInputs[port]; if (hdmiInput?.HdmiInputPort == null) { - Debug.LogWarning(this, "EnableHdcp: HdmiInputPort at index {port} is null. Cannot enable HDCP.", port); + this.LogWarning("EnableHdcp: HdmiInputPort at index {port} is null. Cannot enable HDCP.", port); return; } @@ -276,14 +292,8 @@ public void EnableHdcp(uint port) { return; } - try - { - feedback.FireUpdate(); - } - catch (Exception ex) - { - Debug.LogError(this, $"EnableHdcp: Exception occurred while updating HDCP feedback for input {inputName}: {ex.Message}"); - } + + feedback.FireUpdate(); } } @@ -299,7 +309,7 @@ public void DisableHdcp(uint port) var hdmiInput = _Chassis.HdmiInputs[port]; if (hdmiInput?.HdmiInputPort == null) { - Debug.LogWarning(this, "DisableHdcp: HdmiInputPort at index {port} is null. Cannot disable HDCP.", port); + this.LogWarning("DisableHdcp: HdmiInputPort at index {port} is null. Cannot disable HDCP.", port); return; } @@ -313,14 +323,7 @@ public void DisableHdcp(uint port) { return; } - try - { - feedback.FireUpdate(); - } - catch (Exception ex) - { - Debug.LogError(this, $"DisableHdcp: Exception occurred while updating HDCP feedback for input {inputName}: {ex.Message}"); - } + feedback.FireUpdate(); } } @@ -336,7 +339,7 @@ public void EnableAutoRoute() return; } - Debug.LogVerbose(this, "EnableAutoRoute: AutoRoute is not supported on this chassis."); + this.LogVerbose("EnableAutoRoute: AutoRoute is not supported on this chassis."); } /// @@ -351,7 +354,7 @@ public void DisableAutoRoute() return; } - Debug.LogVerbose(this, "DisableAutoRoute: AutoRoute is not supported on this chassis."); + this.LogVerbose("DisableAutoRoute: AutoRoute is not supported on this chassis."); } /// @@ -365,7 +368,7 @@ public void EnablePriorityRoute() return; } - Debug.LogVerbose(this, "EnablePriorityRoute: Priority Route is not supported on this chassis."); + this.LogVerbose("EnablePriorityRoute: Priority Route is not supported on this chassis."); } /// @@ -379,7 +382,7 @@ public void DisablePriorityRoute() return; } - Debug.LogVerbose(this, "DisablePriorityRoute: Priority Route is not supported on this chassis."); + this.LogVerbose("DisablePriorityRoute: Priority Route is not supported on this chassis."); } @@ -391,9 +394,9 @@ public void DisablePriorityRoute() /// public void AddFeedbackCollections() { - if(IsOnline != null) + if (IsOnline != null) AddFeedbackToList(IsOnline); - + AddFeedbackToList(DeviceNameFeedback); if (AutoRouteFeedback != null) @@ -427,11 +430,11 @@ public void AddFeedbackCollections() } // TODO - Remove after testing - Debug.LogInformation(this, $"AddFeedbackCollections: Feedbacks contains {Feedbacks.Count} items"); + this.LogInformation("AddFeedbackCollections: Feedbacks contains {feedbacksCount} items", Feedbacks.Count); foreach (var fb in Feedbacks) { // TODO - Remove after testing - Debug.LogInformation(this, $"AddFeedbackCollections: Feedbacks = {fb.Key}"); + this.LogInformation("AddFeedbackCollections: Feedbacks = {feedbackKey}", fb.Key); } } @@ -445,7 +448,7 @@ public void AddFeedbackToList(Essentials.Core.Feedback newFb) if (Feedbacks.Any(f => f.Key == newFb.Key)) return; // TODO - Remove after testing - Debug.LogVerbose(this, $"AddFeedbackToList: adding {newFb.Key} to Feedbacks collection"); + this.LogVerbose("AddFeedbackToList: adding {feedbackKey} to Feedbacks collection", newFb.Key); Feedbacks.Add(newFb); } @@ -463,11 +466,11 @@ public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingS { var input = inputSelector as HdMdNxM4kzEHdmiInput; var output = outputSelector as HdMdNxM4kzEHdmiOutput; - Debug.LogVerbose(this, "ExecuteSwitch: input={0} output={1}", input, output); + this.LogVerbose("ExecuteSwitch: input={0} output={1}", input, output); if (output == null) { - Debug.LogInformation(this, "Unable to make switch. output selector is not HdMdNxM4kzEHdmiOutput"); + this.LogInformation("Unable to make switch. output selector is not HdMdNxM4kzEHdmiOutput"); return; } @@ -489,12 +492,12 @@ public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingS /// The type of signal to switch. public void ExecuteNumericSwitch(ushort inputSelector, ushort outputSelector, eRoutingSignalType signalType) { - Debug.LogInformation(this, $"ExecuteNumericSwitch: inputSelector={inputSelector} outputSelector={outputSelector}"); + this.LogInformation("ExecuteNumericSwitch: inputSelector={inputSelector} outputSelector={outputSelector}", inputSelector, outputSelector); var input = inputSelector == 0 ? null : _Chassis.HdmiInputs[inputSelector]; var output = _Chassis.HdmiOutputs[outputSelector]; - Debug.LogVerbose(this, $"ExecuteNumericSwitch: input={input} output={output}"); + this.LogVerbose("ExecuteNumericSwitch: input={input} output={output}", input, output); ExecuteSwitch(input, output, signalType); } @@ -527,26 +530,25 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join } else { - Debug.LogInformation(this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); + this.LogInformation("Please update config to use 'eiscapiadvanced' to get all join map features for this device."); } - if( IsOnline != null) IsOnline?.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); - + if (IsOnline != null) IsOnline?.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + DeviceNameFeedback?.LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); if (_Chassis is HdMdNxM4kzE _chassis_M4kzE) { - Debug.LogInformation(this, $"LinkToApi: _Chassis is HdMdNxM4kzE, setting up AutoRoute links"); + this.LogInformation("LinkToApi: _Chassis is HdMdNxM4kzE, setting up AutoRoute links"); trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis_M4kzE.AutoRouteOn()); trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _chassis_M4kzE.AutoRouteOff()); AutoRouteFeedback?.LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); } - if(_Chassis is HdMd4xX4kzE _chassis_X4kzE) + if (_Chassis is HdMd4xX4kzE _chassis_X4kzE) { - Debug.LogInformation(this, $"LinkToApi: _Chassis is HdMd4xX4kzE, setting up PriorityRoute links - not implemented"); - + this.LogInformation("LinkToApi: _Chassis is HdMd4xX4kzE, setting up PriorityRoute links - not implemented"); // trilist.SetSigTrueAction(joinMap.EnablePriorityRoute.JoinNumber, () => _chassis_X4kzE.PriorityRouteOn()); // trilist.SetSigFalseAction(joinMap.EnablePriorityRoute.JoinNumber, () => _chassis_X4kzE.PriorityRouteOff()); // PriorityRouteFeedback?.LinkInputSig(trilist.BooleanInput[joinMap.EnablePriorityRoute.JoinNumber]); @@ -561,7 +563,7 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join if (input < 1 || input > _Chassis.NumberOfInputs) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "LinkToApi: Input index {index} is out of range (1-{max}). Skipping.", this, input, _Chassis.NumberOfInputs); + this.LogWarning("LinkToApi: Input index {index} is out of range (1-{max}). Skipping.", input, _Chassis.NumberOfInputs); continue; } @@ -572,7 +574,8 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join var joinNumberDisableInputHdcp = joinMap.DisableInputHdcp.JoinNumber + joinIndex; var joinNumberInputName = joinMap.InputName.JoinNumber + joinIndex; - Debug.LogInformation(this, $"LinkToApi: Input {input} | joinIndex = {joinIndex}, InputSyncJoin = {joinNumberInputSync}, EnableHdcpJoin = {joinNumberEnableInputHdcp}, DisableHdcpJoin = {joinNumberDisableInputHdcp}, InputNameJoin = {joinNumberInputName}"); + this.LogInformation("LinkToApi: Input {input} | joinIndex = {joinIndex}, InputSyncJoin = {joinNumberInputSync}, EnableHdcpJoin = {joinNumberEnableInputHdcp}, DisableHdcpJoin = {joinNumberDisableInputHdcp}, InputNameJoin = {joinNumberInputName}", + input, joinIndex, joinNumberInputSync, joinNumberEnableInputHdcp, joinNumberDisableInputHdcp, joinNumberInputName); //Digital VideoInputSyncFeedbacks[inputName]?.LinkInputSig(trilist.BooleanInput[joinNumberInputSync]); @@ -588,7 +591,7 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join } else { - Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "LinkToApi: InputNames is null. Skipping input linking.", this); + this.LogWarning("LinkToApi: InputNames is null. Skipping input linking."); } if (OutputNames != null) @@ -600,7 +603,7 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join if (output < 1 || output > _Chassis.NumberOfOutputs) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "LinkToApi: Output index {index} is out of range (1-{max}). Skipping.", this, output, _Chassis.NumberOfOutputs); + this.LogWarning("LinkToApi: Output index {index} is out of range (1-{max}). Skipping.", output, _Chassis.NumberOfOutputs); continue; } @@ -610,7 +613,8 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join var joinNumberOutputName = joinMap.OutputName.JoinNumber + joinIndex; var joinNumberOutputRouteName = joinMap.OutputRoutedName.JoinNumber + joinIndex; - Debug.LogInformation(this, $"LinkToApi: Output {output} | joinIndex = {joinIndex}, OutputRouteJoin = {joinNumberOutputRoute}, OutputNameJoin = {joinNumberOutputName}, OutputRouteNameJoin = {joinNumberOutputRouteName}"); + this.LogInformation("LinkToApi: Output {output} | joinIndex = {joinIndex}, OutputRouteJoin = {joinNumberOutputRoute}, OutputNameJoin = {joinNumberOutputName}, OutputRouteNameJoin = {joinNumberOutputRouteName}", + output, joinIndex, joinNumberOutputRoute, joinNumberOutputName, joinNumberOutputRouteName); //Analog VideoOutputRouteFeedbacks[outputName]?.LinkInputSig(trilist.UShortInput[joinNumberOutputRoute]); @@ -624,7 +628,7 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join } else { - Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "LinkToApi: OutputNames is null. Skipping output linking.", this); + this.LogWarning("LinkToApi: OutputNames is null. Skipping output linking."); } _Chassis.OnlineStatusChange += Chassis_OnlineStatusChange; @@ -641,53 +645,46 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join /* private void UpdateFeedbacks() { - try - { - IsOnline.FireUpdate(); - DeviceNameFeedback.FireUpdate(); - AutoRouteFeedback.FireUpdate(); - - foreach (var item in VideoInputSyncFeedbacks) - { - item.FireUpdate(); - } - - foreach (var item in VideoOutputRouteFeedbacks) - { - item.FireUpdate(); - } + IsOnline?.FireUpdate(); + DeviceNameFeedback?.FireUpdate(); + AutoRouteFeedback?.FireUpdate(); - foreach (var item in InputHdcpEnableFeedback) - { - item.FireUpdate(); - } + foreach (var item in VideoInputSyncFeedbacks) + { + item.FireUpdate(); + } - foreach (var item in InputNameFeedbacks) - { - item.FireUpdate(); - } + foreach (var item in VideoOutputRouteFeedbacks) + { + item.FireUpdate(); + } - foreach (var item in OutputNameFeedbacks) - { - item.FireUpdate(); - } + foreach (var item in InputHdcpEnableFeedback) + { + item.FireUpdate(); + } - foreach (var item in OutputRouteNameFeedbacks) - { - item.FireUpdate(); - } + foreach (var item in InputNameFeedbacks) + { + item.FireUpdate(); + } - foreach (var item in Feedbacks) - { - // TODO - Remove after testing - Debug.LogInformation(this, $"UpdateFeedbacks: Firing feedback for {item.Key}"); - item.FireUpdate(); - } + foreach (var item in OutputNameFeedbacks) + { + item.FireUpdate(); } - catch (Exception ex) + + foreach (var item in OutputRouteNameFeedbacks) { - Debug.LogError(this, $"UpdateFeedbacks: Exception occurred while updating feedbacks: {ex.Message}"); + item.FireUpdate(); } + + foreach (var item in Feedbacks) + { + // TODO - Remove after testing + this.LogInformation("UpdateFeedbacks: Firing feedback for {itemKey}", item.Key); + item.FireUpdate(); + } } */ #endregion @@ -699,65 +696,37 @@ private void Chassis_BaseEvent(GenericBase device, BaseEventArgs args) var eventName = typeof(BaseEventArgs) .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static) .FirstOrDefault(f => f.IsLiteral && (int)f.GetValue(null) == args.EventId)?.Name ?? args.EventId.ToString(); - Debug.LogInformation(this, $"Chassis_BaseEvent: received {eventName} (id-{args.EventId}) received from device {device.GetType().Name}"); + this.LogInformation("Chassis_BaseEvent: received {eventName} (id-{eventId}) received from device {deviceName}", eventName, args.EventId, device.GetType().Name); } void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice, Crestron.SimplSharpPro.OnlineOfflineEventArgs args) { // TODO - Remove after testing - Debug.LogInformation(this, $"Chassis_OnlineStatusChange: DeviceOnline = {args.DeviceOnLine}"); + this.LogInformation("Chassis_OnlineStatusChange: DeviceOnline = {deviceOnline}", args.DeviceOnLine); - try - { - IsOnline.FireUpdate(); - } - catch (Exception ex) - { - Debug.LogError(this, $"Chassis_OnlineStatusChange: Exception occurred while updating IsOnline feedback: {ex.Message}"); - } + IsOnline?.FireUpdate(); if (!args.DeviceOnLine) return; // TODO - Remove after testing - Debug.LogInformation(this, $"Chassis_OnlineStatusChange: Feedbacks has {Feedbacks.Count} items in the collection"); + this.LogInformation("Chassis_OnlineStatusChange: Feedbacks has {feedbackCount} items in the collection", Feedbacks.Count); foreach (var feedback in Feedbacks) { - try - { - // TODO - Remove after testing - Debug.LogInformation(this, $"Chassis_OnlineStatusChange: Firing update for {feedback.Key}"); - feedback.FireUpdate(); - } - catch (Exception ex) - { - Debug.LogError(this, $"Chassis_OnlineStatusChange: Exception occurred while updating feedback {feedback.Key}: {ex.Message}"); - } + // TODO - Remove after testing + this.LogInformation("Chassis_OnlineStatusChange: Firing update for {feedbackKey}", feedback?.Key); + feedback?.FireUpdate(); } if (_Chassis is HdMd4xX4kzE) { - try - { - AutoRouteFeedback.FireUpdate(); - } - catch (Exception ex) - { - Debug.LogError(this, $"Chassis_OnlineStatusChange: Exception occurred while updating AutoRouteFeedback: {ex.Message}"); - } + AutoRouteFeedback?.FireUpdate(); } - if(_Chassis is HdMd4xX4kzE) + if (_Chassis is HdMd4xX4kzE) { - try - { - PriorityRouteFeedback.FireUpdate(); - } - catch (Exception ex) - { - Debug.LogError(this, $"Chassis_OnlineStatusChange: Exception occurred while updating PriorityRouteFeedback: {ex.Message}"); - } + PriorityRouteFeedback?.FireUpdate(); } } @@ -767,7 +736,7 @@ void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) var eventName = typeof(DMOutputEventIds) .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static) .FirstOrDefault(f => f.IsLiteral && (int)f.GetValue(null) == args.EventId)?.Name ?? args.EventId.ToString(); - Debug.LogInformation(this, $"Chassis_DMOutputChange: received {eventName} (id-{args.EventId}); Index = {args.Index}; Number = {args.Number}; Stream = {args.Stream} "); + this.LogInformation("Chassis_DMOutputChange: received {eventName} (id-{eventId}); Index = {index}; Number = {number}; Stream = {stream} ", eventName, args.EventId, args.Index, args.Number, args.Stream); switch (args.EventId) { @@ -791,62 +760,43 @@ void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) } catch (Exception ex) { - Debug.LogError(this, $"Chassis_DMOutputChange: Exception occurred while updating {eventName} (id-{args.EventId}) {feedback.Key}: {ex.Message}"); + this.LogError(ex, "Chassis_DMOutputChange: Exception occurred while updating {eventName} (id-{eventId}) {feedbackKey}", eventName, args.EventId, feedback.Key); } } else { - Debug.LogInformation(this, $"Chassis_DMOutputChange: {outputName} not found in VideoOutputRouteFeedbacks"); + this.LogInformation("Chassis_DMOutputChange: {outputName} not found in VideoOutputRouteFeedbacks", outputName); } break; } case DMOutputEventIds.AutoModeOffEventId: case DMOutputEventIds.AutoModeOnEventId: { - Debug.LogDebug(this, $"Chassis_DMOutputChange: received {eventName} (id-{args.EventId}) | Updating AutoRouteFeedback"); - try - { - AutoRouteFeedback?.FireUpdate(); - } - catch (Exception ex) - { - Debug.LogError(this, $"Chassis_DMOutputChange: Exception occurred while updating {eventName} (id-{args.EventId}) AutoRouteFeedback: {ex.Message}"); - } + this.LogInformation("Chassis_DMOutputChange: received {eventName} (id-{eventId}) | Updating AutoRouteFeedback", eventName, args.EventId); + AutoRouteFeedback?.FireUpdate(); + break; } case DMOutputEventIds.InputPrioritiesFeedbackEventId: { - Debug.LogDebug(this, $"Chassis_DMOutputChange: received {eventName} (id-{args.EventId}) | Updating PriorityRouteFeedback"); - try - { - PriorityRouteFeedback?.FireUpdate(); - } - catch (Exception ex) - { - Debug.LogError(this, $"Chassis_DMOutputChange: Exception occurred while updating {eventName} (id-{args.EventId}) PriorityRouteFeedback: {ex.Message}"); - } + this.LogInformation("Chassis_DMOutputChange: received {eventName} (id-{eventId}) | Updating PriorityRouteFeedback", eventName, args.EventId); + PriorityRouteFeedback?.FireUpdate(); + break; } case DMOutputEventIds.OutputNameEventId: case DMOutputEventIds.NameFeedbackEventId: { - Debug.LogDebug(this, $"Chassis_DMOutputChange: received {eventName} (id-{args.EventId}) | Output {args.Number} Name {_Chassis.HdmiOutputs[args.Number].NameFeedback.StringValue}, updating OutputNameFeedbacks and OutputRouteNameFeedbacks"); + this.LogInformation("Chassis_DMOutputChange: received {eventName} (id-{eventId}) | Output {number} Name {name}, updating OutputNameFeedbacks and OutputRouteNameFeedbacks", eventName, args.EventId, args.Number, _Chassis.HdmiOutputs[args.Number].NameFeedback.StringValue); foreach (var item in OutputNameFeedbacks) { - try - { - item.FireUpdate(); - } - catch (Exception ex) - { - Debug.LogError(this, $"Chassis_DMOutputChange: Exception occurred while updating {eventName} (id-{args.EventId}) {item.Key}: {ex.Message}"); - } + item.FireUpdate(); } break; } default: { - Debug.LogDebug(this, $"Chassis_DMOutputChange: Unhandled DM Output Event {eventName} (id-{args.EventId}), ignoring."); + this.LogInformation("Chassis_DMOutputChange: Unhandled DM Output Event {eventName} (id-{eventId}), ignoring.", eventName, args.EventId); break; } } @@ -863,17 +813,10 @@ void Chassis_DMInputChange(Switch device, DMInputEventArgs args) case DMInputEventIds.SourceSyncEventId: case DMInputEventIds.VideoDetectedEventId: { - Debug.LogDebug(this, $"Chassis_DMInputChange: received {eventName} (id-{args.EventId}) | Updating VideoInputSyncFeedbacks"); + this.LogInformation("Chassis_DMInputChange: received {eventName} (id-{eventId}) | Updating VideoInputSyncFeedbacks", eventName, args.EventId); foreach (var item in VideoInputSyncFeedbacks) { - try - { - item.FireUpdate(); - } - catch (Exception ex) - { - Debug.LogError(this, $"Chassis_DMInputChange: Exception occurred while updating {eventName} (id-{args.EventId}) {item.Key}: {ex.Message}"); - } + item.FireUpdate(); } break; } @@ -881,36 +824,24 @@ void Chassis_DMInputChange(Switch device, DMInputEventArgs args) case DMInputEventIds.InputNameEventId: case DMInputEventIds.NameFeedbackEventId: { - Debug.LogDebug(this, $"Chassis_DMInputChange: received {eventName} (id-{args.EventId}) | Input {args.Number} Name {_Chassis.HdmiInputs[args.Number].NameFeedback.StringValue}, updating InputNameFeedbacks"); + this.LogInformation("Chassis_DMInputChange: received {eventName} (id-{eventId}) | Input {number} Name {name}, updating InputNameFeedbacks", eventName, args.EventId, args.Number, _Chassis.HdmiInputs[args.Number].NameFeedback.StringValue); foreach (var item in InputNameFeedbacks) { - try - { - item.FireUpdate(); - } - catch (Exception ex) - { - Debug.LogError(this, $"Chassis_DMInputChange: Exception occurred while updating {eventName} (id-{args.EventId}) {item.Key}: {ex.Message}"); - } + item.FireUpdate(); } break; } case DMInputEventIds.PriorityEventId: { - Debug.LogDebug(this, $"Chassis_DMInputChange: received {eventName} (id-{args.EventId}) | Updating PriorityRouteFeedback"); - try - { - PriorityRouteFeedback?.FireUpdate(); - } - catch (Exception ex) - { - Debug.LogError(this, $"Chassis_DMInputChange: Exception occurred while updating {eventName} (id-{args.EventId}) PriorityRouteFeedback: {ex.Message}"); - } + this.LogInformation("Chassis_DMInputChange: received {eventName} (id-{eventId}) | Updating PriorityRouteFeedback", eventName, args.EventId); + + PriorityRouteFeedback?.FireUpdate(); + break; } default: { - Debug.LogDebug(this, $"Chassis_DMInputChange: Unhandled DM Input Event {eventName} (id-{args.EventId}), ignoring."); + this.LogInformation("Chassis_DMInputChange: Unhandled DM Input Event {eventName} (id-{eventId}), ignoring.", eventName, args.EventId); break; } } From 76ca77e1aaf90665b78c12ef8bdff2be095413aa Mon Sep 17 00:00:00 2001 From: jkdevito Date: Mon, 26 Jan 2026 12:04:17 +0100 Subject: [PATCH 32/50] refactor: reorganize feedback setup methods for input and output handling --- src/Chassis/HdMdNxM4KzEController.cs | 133 ++++++++++++++++++++++----- 1 file changed, 111 insertions(+), 22 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 41f940d..155f90c 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -12,6 +12,7 @@ using PepperDash.Essentials.Core.Config; using Crestron.SimplSharpPro; using PepperDash.Core.Logging; +using System.Runtime.CompilerServices; namespace PepperDash.Essentials.DM.Chassis @@ -119,8 +120,11 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, }); } - SetupInputs(); - SetupOutputs(); + SetupInputPortAndNameFeedbacks(); + SetupInputHdcpFeedbacks(); + SetupInputVideoSyncFeedbacks(); + SetupOutputPortsandNameFeedbacks(); + SetupVideoOutputRouteFeedbacks(); _Chassis.DMInputChange += Chassis_DMInputChange; _Chassis.DMOutputChange += Chassis_DMOutputChange; @@ -129,7 +133,7 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, AddPostActivationAction(AddFeedbackCollections); } - private void SetupInputs() + private void SetupInputPortAndNameFeedbacks() { if (InputNames == null) { @@ -171,31 +175,84 @@ private void SetupInputs() FeedbackMatchObject = hdmiInput }); - VideoInputSyncFeedbacks.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}VideoInputSyncFeedback"), () => - { - try { return chassisInput?.VideoDetectedFeedback?.BoolValue ?? false; } - catch { this.LogError($"Error getting VideoInputSyncFeedback for input {inputName}"); return false; } - })); InputNameFeedbacks.Add(new StringFeedback(string.Format($"{inputFbKeyPrefix}InputNameFeedback"), () => { try { return InputNames[index]; } catch { this.LogError($"Error getting InputNameFeedback for input {inputName}"); return ""; } })); + } + } + + private void SetupInputHdcpFeedbacks() + { + for (uint i = 1; i <= _Chassis.Inputs.Count; i++) + { + var inputIndex = i; + var chassisInput = _Chassis.Inputs[inputIndex - 1]; + var hdmiInput = _Chassis.HdmiInputs[inputIndex - 1]; + + var inputName = string.Format("Input{0}", inputIndex); + var inputFbKeyPrefix = inputName.Replace(" ", "").Trim(); + + InputHdcpEnableFeedback.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}HdcpEnableFeedback"), () => + { + try + { + if (hdmiInput?.HdmiInputPort != null) + { + this.LogWarning("SetupInputHdcpFeedbacks: HdmiInputPort at index {index} is null. Cannot get HdcpEnableFeedback.", inputIndex); + return false; + } + + return hdmiInput?.HdmiInputPort?.HdcpSupportOnFeedback?.BoolValue ?? false; + } + catch + { + this.LogError($"SetupInputHdcpFeedbacks: Error getting HdcpEnableFeedback for input {inputName}"); + return false; + } + })); + } + } - if (hdmiInput.HdmiInputPort == null) + private void SetupInputVideoSyncFeedbacks() + { + for (uint i = 1; i <= _Chassis.Inputs.Count; i++) + { + var inputIndex = i; + var chassisInput = _Chassis.Inputs[inputIndex - 1]; + + if (chassisInput == null) { - this.LogWarning("HdmiInputPort at index {index} is null. HDCP feedback will default to false.", index); + this.LogError("SetupInputVideoSyncFeedbacks: Chassis Input at index {index} is null. Skipping.", inputIndex); + continue; } - InputHdcpEnableFeedback.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}HdcpEnableFeedback"), () => + var inputName = string.Format("Input{0}", inputIndex); + var inputFbKeyPrefix = inputName.Replace(" ", "").Trim(); + + VideoInputSyncFeedbacks.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}VideoDetectedFeedback"), () => { - try { return hdmiInput?.HdmiInputPort?.HdcpSupportOnFeedback?.BoolValue ?? false; } - catch { this.LogError($"Error getting HdcpEnableFeedback for input {inputName}"); return false; } + try + { + if (chassisInput?.VideoDetectedFeedback == null) + { + this.LogError("SetupInputVideoSyncFeedbacks: Chassis Input at index {index} VideoDetectedFeedback is null. Cannot get VideoDetectedFeedback.", inputIndex); + return false; + } + + return chassisInput?.VideoDetectedFeedback?.BoolValue ?? false; + } + catch + { + this.LogError($"SetupInputVideoSyncFeedbacks: Error getting VideoDetectedFeedback for input {inputName}"); + return false; + } })); } } - private void SetupOutputs() + private void SetupOutputPortsandNameFeedbacks() { if (OutputNames == null) { @@ -235,11 +292,6 @@ private void SetupOutputs() FeedbackMatchObject = hdmiOutput }); - VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputFbKeyPrefix}VideoOutputRouteFeedback"), () => - { - try { return chassisOutput.VideoOutFeedback == null ? 0 : (int)chassisOutput.VideoOutFeedback.Number; } - catch { this.LogError($"Error getting VideoOutputRouteFeedback for output {outputName}"); return 0; } - })); OutputNameFeedbacks.Add(new StringFeedback(string.Format($"{outputFbKeyPrefix}OutputNameFeedback"), () => { try { return OutputNames[index]; } @@ -253,6 +305,43 @@ private void SetupOutputs() } } + private void SetupVideoOutputRouteFeedbacks() + { + for (uint i = 1; i <= _Chassis.Outputs.Count; i++) + { + var outputIndex = i; + var chassisOutput = _Chassis.Outputs[outputIndex - 1]; + + if (chassisOutput == null) + { + this.LogError("SetupVideoOutputRouteFeedbacks: Chassis Output at index {index} is null. Skipping.", outputIndex); + continue; + } + + var outputName = string.Format("Output{0}", outputIndex); + var outputFbKeyPrefix = outputName.Replace(" ", "").Trim(); + + VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputFbKeyPrefix}VideoOutputRouteFeedback"), () => + { + try + { + if (chassisOutput?.VideoOutFeedback == null) + { + this.LogError("SetupVideoOutputRouteFeedbacks: Chassis Output at index {index} VideoOutFeedback is null. Cannot get VideoOutputRouteFeedback.", outputIndex); + return 0; + } + + return (int)chassisOutput.VideoOutFeedback.Number; + } + catch + { + this.LogError($"SetupVideoOutputRouteFeedbacks: Error getting VideoOutputRouteFeedback for output {outputName}"); + return 0; + } + })); + } + } + #endregion #region Methods @@ -315,10 +404,9 @@ public void DisableHdcp(uint port) hdmiInput.HdmiInputPort.HdcpSupportOff(); - if (InputNames.ContainsKey(port)) + if (InputNames.TryGetValue(port, out var inputName)) { - var inputName = InputNames[port]; - var feedback = InputHdcpEnableFeedback.FirstOrDefault(f => f.Key == inputName); + var feedback = InputHdcpEnableFeedback.FirstOrDefault(f => f.Key == inputName); if (feedback == null) { return; @@ -730,6 +818,7 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice } } + void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) { // TODO - Remove after testing From 57123101cf8d28955e2b5ae0095c0ae07f00bec0 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Mon, 26 Jan 2026 12:19:49 +0100 Subject: [PATCH 33/50] ci(force-patch): increment patch by force From 155966880925ace744bbbdceff4d527f9b6f41db Mon Sep 17 00:00:00 2001 From: jkdevito Date: Mon, 26 Jan 2026 12:30:32 +0100 Subject: [PATCH 34/50] fix: correct input and output indexing in HdMdNxM4KzEController --- src/Chassis/HdMdNxM4KzEController.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 155f90c..5762cc3 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -188,8 +188,8 @@ private void SetupInputHdcpFeedbacks() for (uint i = 1; i <= _Chassis.Inputs.Count; i++) { var inputIndex = i; - var chassisInput = _Chassis.Inputs[inputIndex - 1]; - var hdmiInput = _Chassis.HdmiInputs[inputIndex - 1]; + var chassisInput = _Chassis.Inputs[inputIndex]; + var hdmiInput = _Chassis.HdmiInputs[inputIndex]; var inputName = string.Format("Input{0}", inputIndex); var inputFbKeyPrefix = inputName.Replace(" ", "").Trim(); @@ -220,7 +220,7 @@ private void SetupInputVideoSyncFeedbacks() for (uint i = 1; i <= _Chassis.Inputs.Count; i++) { var inputIndex = i; - var chassisInput = _Chassis.Inputs[inputIndex - 1]; + var chassisInput = _Chassis.Inputs[inputIndex]; if (chassisInput == null) { @@ -310,7 +310,7 @@ private void SetupVideoOutputRouteFeedbacks() for (uint i = 1; i <= _Chassis.Outputs.Count; i++) { var outputIndex = i; - var chassisOutput = _Chassis.Outputs[outputIndex - 1]; + var chassisOutput = _Chassis.Outputs[outputIndex]; if (chassisOutput == null) { From 4e432a10fddab56f6d79b20c4118f01e704af0e8 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Mon, 26 Jan 2026 13:05:51 +0100 Subject: [PATCH 35/50] fix: update HDMI input feedback methods for consistency and accuracy --- src/Chassis/HdMdNxM4KzEController.cs | 32 +++++++++++----------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 5762cc3..1b38d9a 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -121,7 +121,7 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, } SetupInputPortAndNameFeedbacks(); - SetupInputHdcpFeedbacks(); + SetupHdmiInputHdcpFeedbacks(); SetupInputVideoSyncFeedbacks(); SetupOutputPortsandNameFeedbacks(); SetupVideoOutputRouteFeedbacks(); @@ -160,13 +160,6 @@ private void SetupInputPortAndNameFeedbacks() continue; } - var chassisInput = _Chassis.Inputs[index]; - if (chassisInput == null) - { - this.LogError("SetupInputs: Chassis Input at index {index} is null. Skipping.", index); - continue; - } - hdmiInput.Name.StringValue = inputName; InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, @@ -183,12 +176,11 @@ private void SetupInputPortAndNameFeedbacks() } } - private void SetupInputHdcpFeedbacks() + private void SetupHdmiInputHdcpFeedbacks() { - for (uint i = 1; i <= _Chassis.Inputs.Count; i++) + for (uint i = 1; i <= _Chassis.HdmiInputs.Count; i++) { var inputIndex = i; - var chassisInput = _Chassis.Inputs[inputIndex]; var hdmiInput = _Chassis.HdmiInputs[inputIndex]; var inputName = string.Format("Input{0}", inputIndex); @@ -198,7 +190,7 @@ private void SetupInputHdcpFeedbacks() { try { - if (hdmiInput?.HdmiInputPort != null) + if (hdmiInput?.HdmiInputPort == null) { this.LogWarning("SetupInputHdcpFeedbacks: HdmiInputPort at index {index} is null. Cannot get HdcpEnableFeedback.", inputIndex); return false; @@ -217,14 +209,14 @@ private void SetupInputHdcpFeedbacks() private void SetupInputVideoSyncFeedbacks() { - for (uint i = 1; i <= _Chassis.Inputs.Count; i++) + for (uint i = 1; i <= _Chassis.HdmiInputs.Count; i++) { var inputIndex = i; - var chassisInput = _Chassis.Inputs[inputIndex]; + var chassisHdmiInput = _Chassis.HdmiInputs[inputIndex]; - if (chassisInput == null) + if (chassisHdmiInput == null) { - this.LogError("SetupInputVideoSyncFeedbacks: Chassis Input at index {index} is null. Skipping.", inputIndex); + this.LogError("SetupInputVideoSyncFeedbacks: Chassis HDMI input {index} is null. Skipping.", inputIndex); continue; } @@ -235,17 +227,17 @@ private void SetupInputVideoSyncFeedbacks() { try { - if (chassisInput?.VideoDetectedFeedback == null) + if (chassisHdmiInput?.VideoDetectedFeedback == null) { - this.LogError("SetupInputVideoSyncFeedbacks: Chassis Input at index {index} VideoDetectedFeedback is null. Cannot get VideoDetectedFeedback.", inputIndex); + this.LogError("SetupInputVideoSyncFeedbacks: Chassis HDMI input {index} VideoDetectedFeedback is null. Cannot get VideoDetectedFeedback.", inputIndex); return false; } - return chassisInput?.VideoDetectedFeedback?.BoolValue ?? false; + return chassisHdmiInput?.VideoDetectedFeedback?.BoolValue ?? false; } catch { - this.LogError($"SetupInputVideoSyncFeedbacks: Error getting VideoDetectedFeedback for input {inputName}"); + this.LogError($"SetupInputVideoSyncFeedbacks: Error getting VideoDetectedFeedback for {inputName}"); return false; } })); From 520c56ce6aad01b118fede5b0dc837f71b06bfb4 Mon Sep 17 00:00:00 2001 From: Jason DeVito Date: Mon, 26 Jan 2026 11:29:05 -0600 Subject: [PATCH 36/50] fix: update i/o keys referenced for FBs --- src/Chassis/HdMdNxM4KzEController.cs | 29 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 1b38d9a..2ff6460 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -144,8 +144,8 @@ private void SetupInputPortAndNameFeedbacks() foreach (var kvp in InputNames) { var index = kvp.Key; + var inputKey = string.Format("input{0}", index); var inputName = kvp.Value; - var inputFbKeyPrefix = inputName.Replace(" ", "").Trim(); if (index < 1 || index > _Chassis.NumberOfInputs) { @@ -162,16 +162,16 @@ private void SetupInputPortAndNameFeedbacks() hdmiInput.Name.StringValue = inputName; - InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, + InputPorts.Add(new RoutingInputPort(inputKey, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, hdmiInput, this) { FeedbackMatchObject = hdmiInput }); - InputNameFeedbacks.Add(new StringFeedback(string.Format($"{inputFbKeyPrefix}InputNameFeedback"), () => + InputNameFeedbacks.Add(new StringFeedback(string.Format($"{inputKey}Name"), () => { try { return InputNames[index]; } - catch { this.LogError($"Error getting InputNameFeedback for input {inputName}"); return ""; } + catch { this.LogError($"Error getting InputNameFeedback for input {inputKey}"); return ""; } })); } } @@ -183,10 +183,9 @@ private void SetupHdmiInputHdcpFeedbacks() var inputIndex = i; var hdmiInput = _Chassis.HdmiInputs[inputIndex]; - var inputName = string.Format("Input{0}", inputIndex); - var inputFbKeyPrefix = inputName.Replace(" ", "").Trim(); - - InputHdcpEnableFeedback.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}HdcpEnableFeedback"), () => + var inputName = string.Format("input{0}", inputIndex); + + InputHdcpEnableFeedback.Add(new BoolFeedback(string.Format($"{inputName}HdcpEnable"), () => { try { @@ -220,10 +219,10 @@ private void SetupInputVideoSyncFeedbacks() continue; } - var inputName = string.Format("Input{0}", inputIndex); + var inputName = string.Format("input{0}", inputIndex); var inputFbKeyPrefix = inputName.Replace(" ", "").Trim(); - VideoInputSyncFeedbacks.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}VideoDetectedFeedback"), () => + VideoInputSyncFeedbacks.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}VideoDetected"), () => { try { @@ -255,8 +254,8 @@ private void SetupOutputPortsandNameFeedbacks() foreach (var kvp in OutputNames) { var index = kvp.Key; + var outputKey = string.Format("output{0}", index); var outputName = kvp.Value; - var outputFbKeyPrefix = outputName.Replace(" ", "").Trim(); if (index < 1 || index > _Chassis.NumberOfOutputs) { @@ -284,12 +283,12 @@ private void SetupOutputPortsandNameFeedbacks() FeedbackMatchObject = hdmiOutput }); - OutputNameFeedbacks.Add(new StringFeedback(string.Format($"{outputFbKeyPrefix}OutputNameFeedback"), () => + OutputNameFeedbacks.Add(new StringFeedback(string.Format($"{outputKey}Name"), () => { try { return OutputNames[index]; } catch { this.LogError($"Error getting OutputNameFeedback for output {outputName}"); return ""; } })); - OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputFbKeyPrefix}OutputRouteNameFeedback"), () => + OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputKey}RouteName"), () => { try { return chassisOutput.VideoOutFeedback == null ? NoRouteText : chassisOutput.VideoOutFeedback.NameFeedback.StringValue; } catch { this.LogError($"Error getting OutputRouteNameFeedback for output {outputName}"); return NoRouteText; } @@ -310,10 +309,10 @@ private void SetupVideoOutputRouteFeedbacks() continue; } - var outputName = string.Format("Output{0}", outputIndex); + var outputName = string.Format("output{0}", outputIndex); var outputFbKeyPrefix = outputName.Replace(" ", "").Trim(); - VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputFbKeyPrefix}VideoOutputRouteFeedback"), () => + VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputName}Route"), () => { try { From bfaf1a9f24e03657879b5a619642318ec4f4167d Mon Sep 17 00:00:00 2001 From: jkdevito Date: Sat, 31 Jan 2026 12:08:36 +0100 Subject: [PATCH 37/50] fix: improve error handling and logging in SetupInputVideoSyncFeedbacks method --- src/Chassis/HdMdNxM4KzEController.cs | 166 +++++++++++++++++---------- 1 file changed, 105 insertions(+), 61 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 1b38d9a..67abbd3 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -209,39 +209,81 @@ private void SetupHdmiInputHdcpFeedbacks() private void SetupInputVideoSyncFeedbacks() { - for (uint i = 1; i <= _Chassis.HdmiInputs.Count; i++) + if(VideoInputSyncFeedbacks == null) { - var inputIndex = i; - var chassisHdmiInput = _Chassis.HdmiInputs[inputIndex]; + this.LogError("SetupInputVideoSyncFeedbacks: VideoInputSyncFeedbacks collection is null initializing."); + VideoInputSyncFeedbacks = new FeedbackCollection(); + } + + if(_Chassis == null || _Chassis.HdmiInputs == null) + { + this.LogError("SetupInputVideoSyncFeedbacks: Chassis or Chassis HDMI inputs is null. Cannot setup VideoSync feedbacks."); + return; + } - if (chassisHdmiInput == null) + foreach(var input in _Chassis.HdmiInputs) + { + if(input == null) { - this.LogError("SetupInputVideoSyncFeedbacks: Chassis HDMI input {index} is null. Skipping.", inputIndex); + this.LogError("SetupInputVideoSyncFeedbacks: Chassis HDMI {input} is null. Skipping.",input.ToString()); continue; } - var inputName = string.Format("Input{0}", inputIndex); - var inputFbKeyPrefix = inputName.Replace(" ", "").Trim(); + var inputSync = input.VideoDetectedFeedback; + if(inputSync == null) + { + this.LogError("SetupInputVideoSyncFeedbacks: Chassis HDMI input VideoDetectedFeedback is null. Skipping."); + continue; + } - VideoInputSyncFeedbacks.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}VideoDetectedFeedback"), () => + VideoInputSyncFeedbacks.Add(new BoolFeedback(string.Format($"{inputSync}VideoDetectedFeedback"), () => { try { - if (chassisHdmiInput?.VideoDetectedFeedback == null) - { - this.LogError("SetupInputVideoSyncFeedbacks: Chassis HDMI input {index} VideoDetectedFeedback is null. Cannot get VideoDetectedFeedback.", inputIndex); - return false; - } - - return chassisHdmiInput?.VideoDetectedFeedback?.BoolValue ?? false; + this.LogInformation("SetupInputVideoSyncFeedbacks: Getting VideoDetectedFeedback for HDMI {inputSync}", inputSync.ToString()); + return inputSync.BoolValue; } catch { - this.LogError($"SetupInputVideoSyncFeedbacks: Error getting VideoDetectedFeedback for {inputName}"); + this.LogError($"SetupInputVideoSyncFeedbacks: Error getting VideoDetectedFeedback for HDMI {inputSync}"); return false; } })); } + + // for (uint i = 1; i <= _Chassis.HdmiInputs.Count; i++) + // { + // var inputIndex = i; + // var chassisHdmiInput = _Chassis.HdmiInputs[inputIndex]; + + // if (chassisHdmiInput == null) + // { + // this.LogError("SetupInputVideoSyncFeedbacks: Chassis HDMI input {index} is null. Skipping.", inputIndex); + // continue; + // } + + // var inputName = string.Format("Input{0}", inputIndex); + // var inputFbKeyPrefix = inputName.Replace(" ", "").Trim(); + + // VideoInputSyncFeedbacks.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}VideoDetectedFeedback"), () => + // { + // try + // { + // if (chassisHdmiInput?.VideoDetectedFeedback == null) + // { + // this.LogError("SetupInputVideoSyncFeedbacks: Chassis HDMI input {index} VideoDetectedFeedback is null. Cannot get VideoDetectedFeedback.", inputIndex); + // return false; + // } + + // return chassisHdmiInput?.VideoDetectedFeedback?.BoolValue ?? false; + // } + // catch + // { + // this.LogError($"SetupInputVideoSyncFeedbacks: Error getting VideoDetectedFeedback for {inputName}"); + // return false; + // } + // })); + // } } private void SetupOutputPortsandNameFeedbacks() @@ -486,6 +528,7 @@ public void AddFeedbackCollections() foreach (var fb in VideoInputSyncFeedbacks) { + this.LogInformation("AddFeedbackCollections: Adding VideoInputSyncFeedback {feedbackKey} to Feedbacks collection", fb.Key); AddFeedbackToList(fb); } foreach (var fb in InputHdcpEnableFeedback) @@ -810,6 +853,52 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice } } + void Chassis_DMInputChange(Switch device, DMInputEventArgs args) + { + var eventName = typeof(DMInputEventIds) + .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static) + .FirstOrDefault(f => f.IsLiteral && (int)f.GetValue(null) == args.EventId)?.Name ?? args.EventId.ToString(); + + switch (args.EventId) + { + case DMInputEventIds.SourceSyncEventId: + case DMInputEventIds.VideoDetectedEventId: + { + this.LogInformation("Chassis_DMInputChange: received {eventName} (id-{eventId}) | Updating VideoInputSyncFeedbacks", eventName, args.EventId); + foreach (var item in VideoInputSyncFeedbacks) + { + this.LogInformation("Chassis_DMInputChange: Updating VideoInputSyncFeedbacks for HDMI {itemKey} to {itemValue}", item.Key, item.BoolValue); + item.FireUpdate(); + } + break; + } + case DMInputEventIds.InputNameFeedbackEventId: + case DMInputEventIds.InputNameEventId: + case DMInputEventIds.NameFeedbackEventId: + { + this.LogInformation("Chassis_DMInputChange: received {eventName} (id-{eventId}) | Input {number} Name {name}, updating InputNameFeedbacks", eventName, args.EventId, args.Number, _Chassis.HdmiInputs[args.Number].NameFeedback.StringValue); + foreach (var item in InputNameFeedbacks) + { + item.FireUpdate(); + } + break; + } + case DMInputEventIds.PriorityEventId: + { + this.LogInformation("Chassis_DMInputChange: received {eventName} (id-{eventId}) | Updating PriorityRouteFeedback", eventName, args.EventId); + + PriorityRouteFeedback?.FireUpdate(); + + break; + } + default: + { + this.LogInformation("Chassis_DMInputChange: Unhandled DM Input Event {eventName} (id-{eventId}), ignoring.", eventName, args.EventId); + break; + } + } + } + void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) { @@ -883,51 +972,6 @@ void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) } } - void Chassis_DMInputChange(Switch device, DMInputEventArgs args) - { - var eventName = typeof(DMInputEventIds) - .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static) - .FirstOrDefault(f => f.IsLiteral && (int)f.GetValue(null) == args.EventId)?.Name ?? args.EventId.ToString(); - - switch (args.EventId) - { - case DMInputEventIds.SourceSyncEventId: - case DMInputEventIds.VideoDetectedEventId: - { - this.LogInformation("Chassis_DMInputChange: received {eventName} (id-{eventId}) | Updating VideoInputSyncFeedbacks", eventName, args.EventId); - foreach (var item in VideoInputSyncFeedbacks) - { - item.FireUpdate(); - } - break; - } - case DMInputEventIds.InputNameFeedbackEventId: - case DMInputEventIds.InputNameEventId: - case DMInputEventIds.NameFeedbackEventId: - { - this.LogInformation("Chassis_DMInputChange: received {eventName} (id-{eventId}) | Input {number} Name {name}, updating InputNameFeedbacks", eventName, args.EventId, args.Number, _Chassis.HdmiInputs[args.Number].NameFeedback.StringValue); - foreach (var item in InputNameFeedbacks) - { - item.FireUpdate(); - } - break; - } - case DMInputEventIds.PriorityEventId: - { - this.LogInformation("Chassis_DMInputChange: received {eventName} (id-{eventId}) | Updating PriorityRouteFeedback", eventName, args.EventId); - - PriorityRouteFeedback?.FireUpdate(); - - break; - } - default: - { - this.LogInformation("Chassis_DMInputChange: Unhandled DM Input Event {eventName} (id-{eventId}), ignoring.", eventName, args.EventId); - break; - } - } - } - #endregion #region Factory From dfc48ca817e86bb58da29c43fe1346b3d124751d Mon Sep 17 00:00:00 2001 From: jkdevito Date: Sat, 31 Jan 2026 13:54:23 +0100 Subject: [PATCH 38/50] refactor: streamline input and output setup methods for improved clarity and error handling --- src/Chassis/HdMdNxM4KzEController.cs | 282 ++++++++++----------------- 1 file changed, 98 insertions(+), 184 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 95c4dad..344dae1 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -120,11 +120,8 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, }); } - SetupInputPortAndNameFeedbacks(); - SetupHdmiInputHdcpFeedbacks(); - SetupInputVideoSyncFeedbacks(); - SetupOutputPortsandNameFeedbacks(); - SetupVideoOutputRouteFeedbacks(); + SetupInputs(); + SetupOutputs(); _Chassis.DMInputChange += Chassis_DMInputChange; _Chassis.DMOutputChange += Chassis_DMOutputChange; @@ -133,254 +130,171 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, AddPostActivationAction(AddFeedbackCollections); } - private void SetupInputPortAndNameFeedbacks() + private void SetupInputs() { - if (InputNames == null) + if (_Chassis == null) { - this.LogError("SetupInputs: InputNames is null. Ensure 'inputs' is defined in the device configuration."); + this.LogError("SetupInputs: Chassis is null. Cannot setup VideoSync feedbacks."); return; } - foreach (var kvp in InputNames) + //var inputCount = _Chassis.Inputs.Count; + var inputCount = _Chassis.NumberOfInputs; + this.LogError("SetupInputs: Chassis has {index} inputs", inputCount); + for (uint index = 1; index < inputCount; index++) { - var index = kvp.Key; - var inputKey = string.Format("input{0}", index); - var inputName = kvp.Value; + // for reference only + //InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].NameFeedback.StringValue)); + //InputNameFeedbacks.Add(new StringFeedback(inputName, () => InputNames[index])); - if (index < 1 || index > _Chassis.NumberOfInputs) - { - this.LogWarning("SetupInputs: Input index {index} is out of range (1-{max}). Skipping.", index, _Chassis.NumberOfInputs); - continue; - } + var input = _Chassis.Inputs[index]; + var inputName = string.Format("input{0}", index); + this.LogError("SetupInputs: _Chassis.Inputs[{index}] is {input}", index, input == null ? "NULL" : "NOT NULL"); var hdmiInput = _Chassis.HdmiInputs[index]; - if (hdmiInput == null) + this.LogError("SetupInputs: _Chassis.HdmiInputs[{index}] is {hdmiInput}", index, hdmiInput == null ? "NULL" : "NOT NULL"); + + // Name Feedback + this.LogError("SetupInputs: _Chassis.Inputs[{index}].NameFeedback is {inputNameFb}", index, input.NameFeedback == null ? "NULL" : "NOT NULL"); + InputNameFeedbacks.Add(new StringFeedback(string.Format($"{inputName}Name"), () => { - this.LogError("SetupInputs: HdmiInput at index {index} is null. Skipping.", index); - continue; - } + try + { + return input.NameFeedback.StringValue; + } + catch + { + this.LogError("SetupInputs: Error getting _Chassis.Inputs[{index}].NameFeedback", index); + return ""; + } + })); - hdmiInput.Name.StringValue = inputName; + // Set Input Name from config + var inputFriendlyName = InputNames[index] ?? inputName; + input.Name.StringValue = inputFriendlyName; + // hdmiInput.Name.StringValue = inputFriendlyName; - InputPorts.Add(new RoutingInputPort(inputKey, eRoutingSignalType.AudioVideo, + // Routing Input Port + InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, hdmiInput, this) { FeedbackMatchObject = hdmiInput }); - InputNameFeedbacks.Add(new StringFeedback(string.Format($"{inputKey}Name"), () => - { - try { return InputNames[index]; } - catch { this.LogError($"Error getting InputNameFeedback for input {inputKey}"); return ""; } - })); - } - } - - private void SetupHdmiInputHdcpFeedbacks() - { - for (uint i = 1; i <= _Chassis.HdmiInputs.Count; i++) - { - var inputIndex = i; - var hdmiInput = _Chassis.HdmiInputs[inputIndex]; - - var inputName = string.Format("input{0}", inputIndex); - - InputHdcpEnableFeedback.Add(new BoolFeedback(string.Format($"{inputName}HdcpEnable"), () => + // Video Detected Feedback + this.LogError("SetupInputs: _Chassis.Inputs[{index}].VideoDetectedFeedback is {inputSyncFb}", index, input.VideoDetectedFeedback == null ? "NULL" : "NOT NULL"); + var inputSyncFbKey = string.Format("{0}VideoDetected", inputName); + VideoInputSyncFeedbacks.Add(new BoolFeedback(inputSyncFbKey, () => { try { - if (hdmiInput?.HdmiInputPort == null) - { - this.LogWarning("SetupInputHdcpFeedbacks: HdmiInputPort at index {index} is null. Cannot get HdcpEnableFeedback.", inputIndex); - return false; - } - - return hdmiInput?.HdmiInputPort?.HdcpSupportOnFeedback?.BoolValue ?? false; + return input.VideoDetectedFeedback.BoolValue; } catch { - this.LogError($"SetupInputHdcpFeedbacks: Error getting HdcpEnableFeedback for input {inputName}"); + this.LogError("SetupInputs: Error getting _Chassis.Inputs[{index}].VideoDetectedFeedback", index); return false; } })); - } - } - - private void SetupInputVideoSyncFeedbacks() - { - if(VideoInputSyncFeedbacks == null) - { - this.LogError("SetupInputVideoSyncFeedbacks: VideoInputSyncFeedbacks collection is null initializing."); - VideoInputSyncFeedbacks = new FeedbackCollection(); - } - - if(_Chassis == null || _Chassis.HdmiInputs == null) - { - this.LogError("SetupInputVideoSyncFeedbacks: Chassis or Chassis HDMI inputs is null. Cannot setup VideoSync feedbacks."); - return; - } - - for (var i = 0; i < _Chassis.HdmiInputs.Count; i++) - { - var inputIndex = i + 1; - var input = _Chassis.HdmiInputs[(uint)inputIndex]; - - if(input == null) - { - this.LogError("SetupInputVideoSyncFeedbacks: Chassis HDMI {input} is null. Skipping.", inputIndex); - continue; - } - - var inputSync = input.VideoDetectedFeedback; - if(inputSync == null) - { - this.LogError("SetupInputVideoSyncFeedbacks: Chassis HDMI input VideoDetectedFeedback is null. Skipping."); - continue; - } - - var inputName = string.Format("Input{0}", inputIndex); - var inputFbKeyPrefix = inputName.Replace(" ", "").Trim(); - VideoInputSyncFeedbacks.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}VideoDetected"), () => + // HDCP Enable Feedback + this.LogError("SetupInputs: _Chassis.HdmiInputs[{index}].HdmiInputPort.HdcpSupportOnFeedback is {inputHdcpFb}", index, hdmiInput.HdmiInputPort.HdcpSupportOnFeedback == null ? "NULL" : "NOT NULL"); + var inputHdcpFbKey = string.Format("{0}HdcpSupportOn", inputName); + InputHdcpEnableFeedback.Add(new BoolFeedback(inputHdcpFbKey, () => { try { - this.LogInformation("SetupInputVideoSyncFeedbacks: Getting VideoDetectedFeedback for HDMI {inputSync}", inputSync.ToString()); - return inputSync.BoolValue; + return hdmiInput.HdmiInputPort.HdcpSupportOnFeedback.BoolValue; } catch { - this.LogError($"SetupInputVideoSyncFeedbacks: Error getting VideoDetectedFeedback for HDMI {inputSync}"); + this.LogError("SetupInputs: Error getting _Chassis.HdmiInputs[{index}].HdmiInputPort.HdcpSupportOnFeedback", index); return false; } })); } - - // for (uint i = 1; i <= _Chassis.HdmiInputs.Count; i++) - // { - // var inputIndex = i; - // var chassisHdmiInput = _Chassis.HdmiInputs[inputIndex]; - - // if (chassisHdmiInput == null) - // { - // this.LogError("SetupInputVideoSyncFeedbacks: Chassis HDMI input {index} is null. Skipping.", inputIndex); - // continue; - // } - - // var inputName = string.Format("Input{0}", inputIndex); - // var inputFbKeyPrefix = inputName.Replace(" ", "").Trim(); - - // VideoInputSyncFeedbacks.Add(new BoolFeedback(string.Format($"{inputFbKeyPrefix}VideoDetectedFeedback"), () => - // { - // try - // { - // if (chassisHdmiInput?.VideoDetectedFeedback == null) - // { - // this.LogError("SetupInputVideoSyncFeedbacks: Chassis HDMI input {index} VideoDetectedFeedback is null. Cannot get VideoDetectedFeedback.", inputIndex); - // return false; - // } - - // return chassisHdmiInput?.VideoDetectedFeedback?.BoolValue ?? false; - // } - // catch - // { - // this.LogError($"SetupInputVideoSyncFeedbacks: Error getting VideoDetectedFeedback for {inputName}"); - // return false; - // } - // })); - // } } - private void SetupOutputPortsandNameFeedbacks() + private void SetupOutputs() { - if (OutputNames == null) + if (_Chassis == null) { - this.LogWarning("SetupOutputs: OutputNames is null. Ensure 'outputs' is defined in the device configuration."); + this.LogError("SetupOutputs: Chassis is null. Cannot setup VideoSync feedbacks."); return; } - foreach (var kvp in OutputNames) + //var outputCount = _Chassis.Outputs.Count; + var outputCount = _Chassis.NumberOfOutputs; + this.LogError("SetupOutputs: Chassis has {index} outputs", outputCount); + for (uint index = 1; index < outputCount; index++) { - var index = kvp.Key; - var outputKey = string.Format("output{0}", index); - var outputName = kvp.Value; - - if (index < 1 || index > _Chassis.NumberOfOutputs) - { - this.LogWarning("SetupOutputs: Output index {index} is out of range (1-{max}). Skipping.", index, _Chassis.NumberOfOutputs); - continue; - } + var output = _Chassis.Outputs[index]; + var outputName = string.Format("output{0}", index); + this.LogError("SetupOutputs: _Chassis.Outputs[{index}] is {output}", index, output == null ? "NULL" : "NOT NULL"); var hdmiOutput = _Chassis.HdmiOutputs[index]; - if (hdmiOutput == null) - { - this.LogWarning("SetupOutputs: HdmiOutput at index {index} is null. Skipping.", index); - continue; - } + this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{index}] is {hdmiOutput}", index, hdmiOutput == null ? "NULL" : "NOT NULL"); - var chassisOutput = _Chassis.Outputs[index]; - if (chassisOutput == null) + // Name Feedback + this.LogError("SetupOutputs: _Chassis.Outputs[{index}].NameFeedback is {outputNameFb}", index, output.NameFeedback == null ? "NULL" : "NOT NULL"); + OutputNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}Name"), () => { - this.LogError("SetupOutputs: Chassis Output at index {index} is null. Skipping.", index); - continue; - } + try + { + return output.NameFeedback.StringValue; + } + catch + { + this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].NameFeedback", index); + return ""; + } + })); + + // Set Output Name from config + var outputFriendlyName = OutputNames[index] ?? outputName; + output.Name.StringValue = outputFriendlyName; + // hdmiOutput.Name.StringValue = outputFriendlyName; + // Routing Output Port OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, hdmiOutput, this) { FeedbackMatchObject = hdmiOutput }); - OutputNameFeedbacks.Add(new StringFeedback(string.Format($"{outputKey}Name"), () => - { - try { return OutputNames[index]; } - catch { this.LogError($"Error getting OutputNameFeedback for output {outputName}"); return ""; } - })); - OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputKey}RouteName"), () => + // Video Output Route Feedback + this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutputFeedback is {outputVideoFb}", index, output.VideoOutFeedback == null ? "NULL" : "NOT NULL"); + VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputName}Route"), () => { - try { return chassisOutput.VideoOutFeedback == null ? NoRouteText : chassisOutput.VideoOutFeedback.NameFeedback.StringValue; } - catch { this.LogError($"Error getting OutputRouteNameFeedback for output {outputName}"); return NoRouteText; } + try + { + return (int)output.VideoOutFeedback.Number; + } + catch + { + this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].VideoOutFeedback", index); + return 0; + } })); - } - } - private void SetupVideoOutputRouteFeedbacks() - { - for (uint i = 1; i <= _Chassis.Outputs.Count; i++) - { - var outputIndex = i; - var chassisOutput = _Chassis.Outputs[outputIndex]; - - if (chassisOutput == null) - { - this.LogError("SetupVideoOutputRouteFeedbacks: Chassis Output at index {index} is null. Skipping.", outputIndex); - continue; - } - - var outputName = string.Format("output{0}", outputIndex); - var outputFbKeyPrefix = outputName.Replace(" ", "").Trim(); - - VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputName}Route"), () => + // Output Route Name Feedback + this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback is {outputVideoNameFb}", index, output.VideoOutFeedback.NameFeedback == null ? "NULL" : "NOT NULL"); + OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}RoutedName"), () => { try { - if (chassisOutput?.VideoOutFeedback == null) - { - this.LogError("SetupVideoOutputRouteFeedbacks: Chassis Output at index {index} VideoOutFeedback is null. Cannot get VideoOutputRouteFeedback.", outputIndex); - return 0; - } - - return (int)chassisOutput.VideoOutFeedback.Number; + return output.VideoOutFeedback.NameFeedback.StringValue; } catch { - this.LogError($"SetupVideoOutputRouteFeedbacks: Error getting VideoOutputRouteFeedback for output {outputName}"); - return 0; + this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback", index); + return ""; } })); } } + #endregion #region Methods @@ -445,7 +359,7 @@ public void DisableHdcp(uint port) if (InputNames.TryGetValue(port, out var inputName)) { - var feedback = InputHdcpEnableFeedback.FirstOrDefault(f => f.Key == inputName); + var feedback = InputHdcpEnableFeedback.FirstOrDefault(f => f.Key == inputName); if (feedback == null) { return; From 79ff7f6d70e204a6d6515087dc3bf3856556a55a Mon Sep 17 00:00:00 2001 From: jkdevito Date: Sat, 31 Jan 2026 13:54:34 +0100 Subject: [PATCH 39/50] ci(force-patch): increment patch by force From dee27145d4f7842ba19b24bc3f65890565bcbfc7 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Sat, 31 Jan 2026 14:02:01 +0100 Subject: [PATCH 40/50] fix: correct loop boundaries and enhance error logging in SetupInputs and SetupOutputs methods --- src/Chassis/HdMdNxM4KzEController.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 344dae1..fbbc4a7 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -140,9 +140,14 @@ private void SetupInputs() //var inputCount = _Chassis.Inputs.Count; var inputCount = _Chassis.NumberOfInputs; - this.LogError("SetupInputs: Chassis has {index} inputs", inputCount); - for (uint index = 1; index < inputCount; index++) + this.LogError("SetupInputs: Chassis has {inputCount} inputs", inputCount); + for (uint index = 1; index <= inputCount; index++) { + if(index > inputCount) + { + this.LogError("SetupInputs: index {index} is greater than _Chassis.NumberOfInputs {inputCount}, breaking loop", index, inputCount); + break; + } // for reference only //InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].NameFeedback.StringValue)); //InputNameFeedbacks.Add(new StringFeedback(inputName, () => InputNames[index])); @@ -225,9 +230,15 @@ private void SetupOutputs() //var outputCount = _Chassis.Outputs.Count; var outputCount = _Chassis.NumberOfOutputs; - this.LogError("SetupOutputs: Chassis has {index} outputs", outputCount); - for (uint index = 1; index < outputCount; index++) + this.LogError("SetupOutputs: Chassis has {outputCount} outputs", outputCount); + for (uint index = 1; index <= outputCount; index++) { + if(index > outputCount) + { + this.LogError("SetupOutputs: index {index} is greater than _Chassis.NumberOfOutputs {outputCount}, breaking loop", index, outputCount); + break; + } + var output = _Chassis.Outputs[index]; var outputName = string.Format("output{0}", index); this.LogError("SetupOutputs: _Chassis.Outputs[{index}] is {output}", index, output == null ? "NULL" : "NOT NULL"); From fbc09a6ba5153c2e8fffa5b5519bf56e28b7b061 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Sat, 31 Jan 2026 14:17:17 +0100 Subject: [PATCH 41/50] fix: enhance input/output setup methods with improved error logging and index handling --- src/Chassis/HdMdNxM4KzEController.cs | 40 +++++++++++++++++----------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index fbbc4a7..93cdb2f 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -141,8 +141,9 @@ private void SetupInputs() //var inputCount = _Chassis.Inputs.Count; var inputCount = _Chassis.NumberOfInputs; this.LogError("SetupInputs: Chassis has {inputCount} inputs", inputCount); - for (uint index = 1; index <= inputCount; index++) + for (uint i = 1; i <= inputCount; i++) { + var index = i; if(index > inputCount) { this.LogError("SetupInputs: index {index} is greater than _Chassis.NumberOfInputs {inputCount}, breaking loop", index, inputCount); @@ -152,8 +153,10 @@ private void SetupInputs() //InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].NameFeedback.StringValue)); //InputNameFeedbacks.Add(new StringFeedback(inputName, () => InputNames[index])); - var input = _Chassis.Inputs[index]; var inputName = string.Format("input{0}", index); + var inputFriendlyName = InputNames[index] ?? inputName; + + var input = _Chassis.Inputs[index]; this.LogError("SetupInputs: _Chassis.Inputs[{index}] is {input}", index, input == null ? "NULL" : "NOT NULL"); var hdmiInput = _Chassis.HdmiInputs[index]; @@ -175,7 +178,6 @@ private void SetupInputs() })); // Set Input Name from config - var inputFriendlyName = InputNames[index] ?? inputName; input.Name.StringValue = inputFriendlyName; // hdmiInput.Name.StringValue = inputFriendlyName; @@ -231,16 +233,19 @@ private void SetupOutputs() //var outputCount = _Chassis.Outputs.Count; var outputCount = _Chassis.NumberOfOutputs; this.LogError("SetupOutputs: Chassis has {outputCount} outputs", outputCount); - for (uint index = 1; index <= outputCount; index++) + for (uint i = 1; i <= outputCount; i++) { + var index = i; if(index > outputCount) { this.LogError("SetupOutputs: index {index} is greater than _Chassis.NumberOfOutputs {outputCount}, breaking loop", index, outputCount); break; } + + var outputName = string.Format("output{0}", index); + var outputFriendlyName = OutputNames[index] ?? outputName; var output = _Chassis.Outputs[index]; - var outputName = string.Format("output{0}", index); this.LogError("SetupOutputs: _Chassis.Outputs[{index}] is {output}", index, output == null ? "NULL" : "NOT NULL"); var hdmiOutput = _Chassis.HdmiOutputs[index]; @@ -262,7 +267,6 @@ private void SetupOutputs() })); // Set Output Name from config - var outputFriendlyName = OutputNames[index] ?? outputName; output.Name.StringValue = outputFriendlyName; // hdmiOutput.Name.StringValue = outputFriendlyName; @@ -274,31 +278,37 @@ private void SetupOutputs() }); // Video Output Route Feedback - this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutputFeedback is {outputVideoFb}", index, output.VideoOutFeedback == null ? "NULL" : "NOT NULL"); + //this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutputFeedback is {outputVideoFb}", index, output.VideoOutFeedback == null ? "NULL" : "NOT NULL"); + this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{index}].VideoOutputFeedback is {outputVideoFb}", index, hdmiOutput.VideoOutFeedback == null ? "NULL" : "NOT NULL"); VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputName}Route"), () => { try { - return (int)output.VideoOutFeedback.Number; + //return (int)output.VideoOutFeedback.Number; + return (int)hdmiOutput.VideoOutFeedback.Number; } catch { - this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].VideoOutFeedback", index); + //this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].VideoOutFeedback", index); + this.LogError("SetupOutputs: Error getting _Chassis.HdmiOutputs[{index}].VideoOutFeedback", index); return 0; } })); // Output Route Name Feedback - this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback is {outputVideoNameFb}", index, output.VideoOutFeedback.NameFeedback == null ? "NULL" : "NOT NULL"); + //this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback is {outputVideoNameFb}", index, output.VideoOutFeedback.NameFeedback == null ? "NULL" : "NOT NULL"); + this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{index}].VideoOutFeedback.NameFeedback is {outputVideoNameFb}", index, hdmiOutput.VideoOutFeedback.NameFeedback == null ? "NULL" : "NOT NULL"); OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}RoutedName"), () => { try { - return output.VideoOutFeedback.NameFeedback.StringValue; + //return output.VideoOutFeedback.NameFeedback.StringValue; + return hdmiOutput.VideoOutFeedback.NameFeedback.StringValue; } catch { - this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback", index); + //this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback", index); + this.LogError("SetupOutputs: Error getting _Chassis.HdmiOutputs[{index}].VideoOutFeedback.NameFeedback", index); return ""; } })); @@ -325,8 +335,7 @@ private void OnSwitchChange(RoutingNumericEventArgs e) /// The input port number to enable HDCP on. public void EnableHdcp(uint port) { - if (port > _Chassis.NumberOfInputs) return; - if (port <= 0) return; + if (port <= 0 || port > _Chassis.NumberOfInputs) return; var hdmiInput = _Chassis.HdmiInputs[port]; if (hdmiInput?.HdmiInputPort == null) @@ -356,8 +365,7 @@ public void EnableHdcp(uint port) /// The input port number to disable HDCP on. public void DisableHdcp(uint port) { - if (port > _Chassis.NumberOfInputs) return; - if (port <= 0) return; + if (port <= 0 || port > _Chassis.NumberOfInputs) return; var hdmiInput = _Chassis.HdmiInputs[port]; if (hdmiInput?.HdmiInputPort == null) From ee064b0d19e3ed0691e6044ae1910944ae311e83 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Sat, 31 Jan 2026 14:32:31 +0100 Subject: [PATCH 42/50] fix: improve error logging and variable naming in SetupInputs and SetupOutputs methods --- src/Chassis/HdMdNxM4KzEController.cs | 165 ++++++++++++++++++--------- 1 file changed, 110 insertions(+), 55 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 93cdb2f..54fd4fa 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -144,7 +144,7 @@ private void SetupInputs() for (uint i = 1; i <= inputCount; i++) { var index = i; - if(index > inputCount) + if (index > inputCount) { this.LogError("SetupInputs: index {index} is greater than _Chassis.NumberOfInputs {inputCount}, breaking loop", index, inputCount); break; @@ -156,19 +156,19 @@ private void SetupInputs() var inputName = string.Format("input{0}", index); var inputFriendlyName = InputNames[index] ?? inputName; - var input = _Chassis.Inputs[index]; - this.LogError("SetupInputs: _Chassis.Inputs[{index}] is {input}", index, input == null ? "NULL" : "NOT NULL"); + var chassisInput = _Chassis.Inputs[index]; + this.LogError("SetupInputs: _Chassis.Inputs[{index}] is {input}", index, chassisInput == null ? "NULL" : "NOT NULL"); - var hdmiInput = _Chassis.HdmiInputs[index]; - this.LogError("SetupInputs: _Chassis.HdmiInputs[{index}] is {hdmiInput}", index, hdmiInput == null ? "NULL" : "NOT NULL"); + var chassisHdmiInput = _Chassis.HdmiInputs[index]; + this.LogError("SetupInputs: _Chassis.HdmiInputs[{index}] is {hdmiInput}", index, chassisHdmiInput == null ? "NULL" : "NOT NULL"); // Name Feedback - this.LogError("SetupInputs: _Chassis.Inputs[{index}].NameFeedback is {inputNameFb}", index, input.NameFeedback == null ? "NULL" : "NOT NULL"); + this.LogError("SetupInputs: _Chassis.Inputs[{index}].NameFeedback is {inputNameFb}", index, chassisInput.NameFeedback == null ? "NULL" : "NOT NULL"); InputNameFeedbacks.Add(new StringFeedback(string.Format($"{inputName}Name"), () => { try { - return input.NameFeedback.StringValue; + return chassisInput.NameFeedback.StringValue; } catch { @@ -178,24 +178,24 @@ private void SetupInputs() })); // Set Input Name from config - input.Name.StringValue = inputFriendlyName; + chassisInput.Name.StringValue = inputFriendlyName; // hdmiInput.Name.StringValue = inputFriendlyName; // Routing Input Port InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, hdmiInput, this) + eRoutingPortConnectionType.Hdmi, chassisHdmiInput, this) { - FeedbackMatchObject = hdmiInput + FeedbackMatchObject = chassisHdmiInput }); // Video Detected Feedback - this.LogError("SetupInputs: _Chassis.Inputs[{index}].VideoDetectedFeedback is {inputSyncFb}", index, input.VideoDetectedFeedback == null ? "NULL" : "NOT NULL"); + this.LogError("SetupInputs: _Chassis.Inputs[{index}].VideoDetectedFeedback is {inputSyncFb}", index, chassisInput.VideoDetectedFeedback == null ? "NULL" : "NOT NULL"); var inputSyncFbKey = string.Format("{0}VideoDetected", inputName); VideoInputSyncFeedbacks.Add(new BoolFeedback(inputSyncFbKey, () => { try { - return input.VideoDetectedFeedback.BoolValue; + return chassisInput.VideoDetectedFeedback.BoolValue; } catch { @@ -205,13 +205,13 @@ private void SetupInputs() })); // HDCP Enable Feedback - this.LogError("SetupInputs: _Chassis.HdmiInputs[{index}].HdmiInputPort.HdcpSupportOnFeedback is {inputHdcpFb}", index, hdmiInput.HdmiInputPort.HdcpSupportOnFeedback == null ? "NULL" : "NOT NULL"); + this.LogError("SetupInputs: _Chassis.HdmiInputs[{index}].HdmiInputPort.HdcpSupportOnFeedback is {inputHdcpFb}", index, chassisHdmiInput.HdmiInputPort.HdcpSupportOnFeedback == null ? "NULL" : "NOT NULL"); var inputHdcpFbKey = string.Format("{0}HdcpSupportOn", inputName); InputHdcpEnableFeedback.Add(new BoolFeedback(inputHdcpFbKey, () => { try { - return hdmiInput.HdmiInputPort.HdcpSupportOnFeedback.BoolValue; + return chassisHdmiInput.HdmiInputPort.HdcpSupportOnFeedback.BoolValue; } catch { @@ -236,7 +236,7 @@ private void SetupOutputs() for (uint i = 1; i <= outputCount; i++) { var index = i; - if(index > outputCount) + if (index > outputCount) { this.LogError("SetupOutputs: index {index} is greater than _Chassis.NumberOfOutputs {outputCount}, breaking loop", index, outputCount); break; @@ -244,20 +244,20 @@ private void SetupOutputs() var outputName = string.Format("output{0}", index); var outputFriendlyName = OutputNames[index] ?? outputName; - - var output = _Chassis.Outputs[index]; - this.LogError("SetupOutputs: _Chassis.Outputs[{index}] is {output}", index, output == null ? "NULL" : "NOT NULL"); - var hdmiOutput = _Chassis.HdmiOutputs[index]; - this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{index}] is {hdmiOutput}", index, hdmiOutput == null ? "NULL" : "NOT NULL"); + var chassisOutput = _Chassis.Outputs[index]; + this.LogError("SetupOutputs: _Chassis.Outputs[{index}] is {output}", index, chassisOutput == null ? "NULL" : "NOT NULL"); + + var chassisHdmiOutput = _Chassis.HdmiOutputs[index]; + this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{index}] is {hdmiOutput}", index, chassisHdmiOutput == null ? "NULL" : "NOT NULL"); // Name Feedback - this.LogError("SetupOutputs: _Chassis.Outputs[{index}].NameFeedback is {outputNameFb}", index, output.NameFeedback == null ? "NULL" : "NOT NULL"); + this.LogError("SetupOutputs: _Chassis.Outputs[{index}].NameFeedback is {outputNameFb}", index, chassisOutput.NameFeedback == null ? "NULL" : "NOT NULL"); OutputNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}Name"), () => { try { - return output.NameFeedback.StringValue; + return chassisOutput.NameFeedback.StringValue; } catch { @@ -267,55 +267,111 @@ private void SetupOutputs() })); // Set Output Name from config - output.Name.StringValue = outputFriendlyName; + chassisOutput.Name.StringValue = outputFriendlyName; // hdmiOutput.Name.StringValue = outputFriendlyName; // Routing Output Port OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, hdmiOutput, this) + eRoutingPortConnectionType.Hdmi, chassisHdmiOutput, this) { - FeedbackMatchObject = hdmiOutput + FeedbackMatchObject = chassisHdmiOutput }); - // Video Output Route Feedback - //this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutputFeedback is {outputVideoFb}", index, output.VideoOutFeedback == null ? "NULL" : "NOT NULL"); - this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{index}].VideoOutputFeedback is {outputVideoFb}", index, hdmiOutput.VideoOutFeedback == null ? "NULL" : "NOT NULL"); - VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputName}Route"), () => + // Chassis.Outputs[index].VideoOutFeedback + try { - try + this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutputFeedback is {outputVideoFb}", index, chassisOutput.VideoOutFeedback == null ? "NULL" : "NOT NULL"); + VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputName}Route"), () => { - //return (int)output.VideoOutFeedback.Number; - return (int)hdmiOutput.VideoOutFeedback.Number; - } - catch + try + { + return (int)chassisOutput.VideoOutFeedback.Number; + } + catch + { + this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].VideoOutFeedback", index); + return 0; + } + })); + + } + catch (Exception ex) + { + this.LogError("SetupOutputs: Exception occurred while setting _Chassis.Outputs[{index}].VideoOutFeedback: {exceptionMessage}", index, ex.Message); + this.LogError(ex, "SetupOutputs: Stack trace for exception"); + } + + // Chassis.HdmiOutputs[index].VideoOutFeedback + try + { + this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{index}].VideoOutputFeedback is {outputVideoFb}", index, chassisHdmiOutput.VideoOutFeedback == null ? "NULL" : "NOT NULL"); + VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputName}Route"), () => { - //this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].VideoOutFeedback", index); - this.LogError("SetupOutputs: Error getting _Chassis.HdmiOutputs[{index}].VideoOutFeedback", index); - return 0; - } - })); + try + { + return (int)chassisHdmiOutput.VideoOutFeedback.Number; + } + catch + { + this.LogError("SetupOutputs: Error getting _Chassis.HdmiOutputs[{index}].VideoOutFeedback", index); + return 0; + } + })); + } + catch (Exception ex) + { + this.LogError("SetupOutputs: Exception occurred while preparing to set _Chassis.HdmiOutputs[{index}].VideoOutFeedback: {exceptionMessage}", index, ex.Message); + this.LogError(ex, "SetupOutputs: Stack trace for exception"); + } - // Output Route Name Feedback - //this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback is {outputVideoNameFb}", index, output.VideoOutFeedback.NameFeedback == null ? "NULL" : "NOT NULL"); - this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{index}].VideoOutFeedback.NameFeedback is {outputVideoNameFb}", index, hdmiOutput.VideoOutFeedback.NameFeedback == null ? "NULL" : "NOT NULL"); - OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}RoutedName"), () => + // _Chassis.Outputs[index].VideoOutFeedback.NameFeedback + try { - try + this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback is {outputVideoNameFb}", index, output.VideoOutFeedback.NameFeedback == null ? "NULL" : "NOT NULL"); + OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}RoutedName"), () => { - //return output.VideoOutFeedback.NameFeedback.StringValue; - return hdmiOutput.VideoOutFeedback.NameFeedback.StringValue; - } - catch + try + { + return chassisOutput.VideoOutFeedback.NameFeedback.StringValue; + } + catch + { + this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback", index); + return ""; + } + })); + } + catch (Exception ex) + { + this.LogError("SetupOutputs: Exception occurred while preparing to set _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback", index); + this.LogError(ex, "SetupOutputs: Stack trace for exception"); + } + + // _Chassis.HdmiOutputs[index].VideoOutFeedback.NameFeedback + try + { + this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{index}].VideoOutFeedback.NameFeedback is {outputVideoNameFb}", index, chassisHdmiOutput.VideoOutFeedback.NameFeedback == null ? "NULL" : "NOT NULL"); + OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}RoutedName"), () => { - //this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback", index); - this.LogError("SetupOutputs: Error getting _Chassis.HdmiOutputs[{index}].VideoOutFeedback.NameFeedback", index); - return ""; - } - })); + try + { + return chassisHdmiOutput.VideoOutFeedback.NameFeedback.StringValue; + } + catch + { + this.LogError("SetupOutputs: Error getting _Chassis.HdmiOutputs[{index}].VideoOutFeedback.NameFeedback", index); + return ""; + } + })); + } + catch (Exception ex) + { + this.LogError("SetupOutputs: Exception occurred while preparing to set _Chassis.HdmiOutputs[{index}].VideoOutFeedback.NameFeedback", index); + this.LogError(ex, "SetupOutputs: Stack trace for exception"); + } } } - #endregion #region Methods @@ -466,7 +522,6 @@ public void AddFeedbackCollections() foreach (var fb in VideoInputSyncFeedbacks) { - this.LogInformation("AddFeedbackCollections: Adding VideoInputSyncFeedback {feedbackKey} to Feedbacks collection", fb.Key); AddFeedbackToList(fb); } foreach (var fb in InputHdcpEnableFeedback) From c37f73c9a331f460f8c89d714e855e1e23d9a98b Mon Sep 17 00:00:00 2001 From: jkdevito Date: Sat, 31 Jan 2026 14:35:21 +0100 Subject: [PATCH 43/50] fix: correct variable reference in error logging for SetupOutputs method --- src/Chassis/HdMdNxM4KzEController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 54fd4fa..54a42b0 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -327,7 +327,7 @@ private void SetupOutputs() // _Chassis.Outputs[index].VideoOutFeedback.NameFeedback try { - this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback is {outputVideoNameFb}", index, output.VideoOutFeedback.NameFeedback == null ? "NULL" : "NOT NULL"); + this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback is {outputVideoNameFb}", index, chassisOutput.VideoOutFeedback.NameFeedback == null ? "NULL" : "NOT NULL"); OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}RoutedName"), () => { try From 869c1ea10247ac5594813ebb71a37ea83bff93c3 Mon Sep 17 00:00:00 2001 From: jdevito Date: Sat, 31 Jan 2026 18:03:06 +0100 Subject: [PATCH 44/50] fix: resolve videooutfeedback reference --- src/Chassis/HdMdNxM4KzEController.cs | 66 ++++++++++++---------------- 1 file changed, 27 insertions(+), 39 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 54a42b0..cdf6819 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -1,18 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json; -using Crestron.SimplSharp; +using Crestron.SimplSharpPro; using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DM; +using Newtonsoft.Json; using PepperDash.Core; +using PepperDash.Core.Logging; using PepperDash.Essentials.Core; -using PepperDash.Essentials.DM.Config; using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Config; -using Crestron.SimplSharpPro; -using PepperDash.Core.Logging; -using System.Runtime.CompilerServices; +using PepperDash.Essentials.DM.Config; +using System; +using System.Collections.Generic; +using System.Linq; namespace PepperDash.Essentials.DM.Chassis @@ -70,38 +68,28 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, NoRouteText = props.NoRouteText ?? "None"; - if (props.Inputs != null) - { - InputNames = props.Inputs; - foreach (var kvp in InputNames) - { - Debug.LogDebug(this, "InputNames: {0}-{1}", kvp.Key, kvp.Value); - } - } - if (props.Outputs != null) - { - OutputNames = props.Outputs; - foreach (var kvp in OutputNames) - { - Debug.LogDebug(this, "OutputNamess: {0}-{1}", kvp.Key, kvp.Value); - } - } - - DeviceNameFeedback = new StringFeedback("DeviceName", () => + InputNames = new Dictionary(); + InputNames = props.Inputs ?? new Dictionary(); + + OutputNames = new Dictionary(); + OutputNames = props.Outputs ?? new Dictionary(); + + InputPorts = new RoutingPortCollection(); + OutputPorts = new RoutingPortCollection(); + + DeviceNameFeedback = new StringFeedback("DeviceName", () => { try { return Name; } catch { this.LogError("Error getting DeviceNameFeedback"); return ""; } - }); - - VideoInputSyncFeedbacks = new FeedbackCollection(); - VideoOutputRouteFeedbacks = new FeedbackCollection(); - InputNameFeedbacks = new FeedbackCollection(); - OutputNameFeedbacks = new FeedbackCollection(); + }); + + InputNameFeedbacks = new FeedbackCollection(); + OutputNameFeedbacks = new FeedbackCollection(); + + VideoInputSyncFeedbacks = new FeedbackCollection(); + InputHdcpEnableFeedback = new FeedbackCollection(); + VideoOutputRouteFeedbacks = new FeedbackCollection(); OutputRouteNameFeedbacks = new FeedbackCollection(); - InputHdcpEnableFeedback = new FeedbackCollection(); - - InputPorts = new RoutingPortCollection(); - OutputPorts = new RoutingPortCollection(); if (_Chassis is HdMdNxM4kzE _chassis_M4kzE) { @@ -234,8 +222,8 @@ private void SetupOutputs() var outputCount = _Chassis.NumberOfOutputs; this.LogError("SetupOutputs: Chassis has {outputCount} outputs", outputCount); for (uint i = 1; i <= outputCount; i++) - { - var index = i; + { + var index = i - 1; if (index > outputCount) { this.LogError("SetupOutputs: index {index} is greater than _Chassis.NumberOfOutputs {outputCount}, breaking loop", index, outputCount); From ff6c82e066eec8a5240955d88eb6715b62715722 Mon Sep 17 00:00:00 2001 From: jdevito Date: Sat, 31 Jan 2026 18:13:19 +0100 Subject: [PATCH 45/50] chore: remove comments --- src/Chassis/HdMdNxM4KzEController.cs | 170 ++++++++++----------------- 1 file changed, 61 insertions(+), 109 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index cdf6819..453275d 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -216,14 +216,14 @@ private void SetupOutputs() { this.LogError("SetupOutputs: Chassis is null. Cannot setup VideoSync feedbacks."); return; - } - - //var outputCount = _Chassis.Outputs.Count; - var outputCount = _Chassis.NumberOfOutputs; + } + + //var outputCount = _Chassis.Outputs.Count; + var outputCount = _Chassis.NumberOfOutputs; this.LogError("SetupOutputs: Chassis has {outputCount} outputs", outputCount); for (uint i = 1; i <= outputCount; i++) { - var index = i - 1; + var index = i; if (index > outputCount) { this.LogError("SetupOutputs: index {index} is greater than _Chassis.NumberOfOutputs {outputCount}, breaking loop", index, outputCount); @@ -237,9 +237,9 @@ private void SetupOutputs() this.LogError("SetupOutputs: _Chassis.Outputs[{index}] is {output}", index, chassisOutput == null ? "NULL" : "NOT NULL"); var chassisHdmiOutput = _Chassis.HdmiOutputs[index]; - this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{index}] is {hdmiOutput}", index, chassisHdmiOutput == null ? "NULL" : "NOT NULL"); - - // Name Feedback + this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{index}] is {hdmiOutput}", index, chassisHdmiOutput == null ? "NULL" : "NOT NULL"); + + // Name Feedback this.LogError("SetupOutputs: _Chassis.Outputs[{index}].NameFeedback is {outputNameFb}", index, chassisOutput.NameFeedback == null ? "NULL" : "NOT NULL"); OutputNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}Name"), () => { @@ -252,111 +252,63 @@ private void SetupOutputs() this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].NameFeedback", index); return ""; } - })); - - // Set Output Name from config - chassisOutput.Name.StringValue = outputFriendlyName; - // hdmiOutput.Name.StringValue = outputFriendlyName; - - // Routing Output Port + })); + + // Set Output Name from config + chassisOutput.Name.StringValue = outputFriendlyName; + // hdmiOutput.Name.StringValue = outputFriendlyName; + + // Routing Output Port OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, chassisHdmiOutput, this) { FeedbackMatchObject = chassisHdmiOutput - }); - - // Chassis.Outputs[index].VideoOutFeedback - try - { - this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutputFeedback is {outputVideoFb}", index, chassisOutput.VideoOutFeedback == null ? "NULL" : "NOT NULL"); - VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputName}Route"), () => - { - try - { - return (int)chassisOutput.VideoOutFeedback.Number; - } - catch - { - this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].VideoOutFeedback", index); - return 0; - } - })); - - } - catch (Exception ex) - { - this.LogError("SetupOutputs: Exception occurred while setting _Chassis.Outputs[{index}].VideoOutFeedback: {exceptionMessage}", index, ex.Message); - this.LogError(ex, "SetupOutputs: Stack trace for exception"); - } - - // Chassis.HdmiOutputs[index].VideoOutFeedback - try - { - this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{index}].VideoOutputFeedback is {outputVideoFb}", index, chassisHdmiOutput.VideoOutFeedback == null ? "NULL" : "NOT NULL"); - VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputName}Route"), () => - { - try - { - return (int)chassisHdmiOutput.VideoOutFeedback.Number; - } - catch - { - this.LogError("SetupOutputs: Error getting _Chassis.HdmiOutputs[{index}].VideoOutFeedback", index); - return 0; - } - })); - } - catch (Exception ex) - { - this.LogError("SetupOutputs: Exception occurred while preparing to set _Chassis.HdmiOutputs[{index}].VideoOutFeedback: {exceptionMessage}", index, ex.Message); - this.LogError(ex, "SetupOutputs: Stack trace for exception"); - } - - // _Chassis.Outputs[index].VideoOutFeedback.NameFeedback - try - { - this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback is {outputVideoNameFb}", index, chassisOutput.VideoOutFeedback.NameFeedback == null ? "NULL" : "NOT NULL"); - OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}RoutedName"), () => - { - try - { - return chassisOutput.VideoOutFeedback.NameFeedback.StringValue; - } - catch - { - this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback", index); - return ""; - } - })); - } - catch (Exception ex) - { - this.LogError("SetupOutputs: Exception occurred while preparing to set _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback", index); - this.LogError(ex, "SetupOutputs: Stack trace for exception"); - } - - // _Chassis.HdmiOutputs[index].VideoOutFeedback.NameFeedback - try - { - this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{index}].VideoOutFeedback.NameFeedback is {outputVideoNameFb}", index, chassisHdmiOutput.VideoOutFeedback.NameFeedback == null ? "NULL" : "NOT NULL"); - OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}RoutedName"), () => - { - try - { - return chassisHdmiOutput.VideoOutFeedback.NameFeedback.StringValue; - } - catch - { - this.LogError("SetupOutputs: Error getting _Chassis.HdmiOutputs[{index}].VideoOutFeedback.NameFeedback", index); - return ""; - } - })); - } - catch (Exception ex) - { - this.LogError("SetupOutputs: Exception occurred while preparing to set _Chassis.HdmiOutputs[{index}].VideoOutFeedback.NameFeedback", index); - this.LogError(ex, "SetupOutputs: Stack trace for exception"); - } + }); + + // Chassis.Outputs[index].VideoOutFeedback + this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutputFeedback is {outputVideoFb}", index, chassisOutput.VideoOutFeedback == null ? "NULL" : "NOT NULL"); + VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputName}Route"), () => + { + try + { + return (int)(chassisOutput.VideoOutFeedback == null ? 0 : chassisOutput.VideoOutFeedback.Number); + } + catch + { + this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].VideoOutFeedback", index); + return 0; + } + })); + + // _Chassis.Outputs[index].VideoOutFeedback.NameFeedback + this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback is {outputVideoNameFb}", index, chassisOutput.VideoOutFeedback.NameFeedback == null ? "NULL" : "NOT NULL"); + OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}RoutedName"), () => + { + try + { + return chassisOutput.VideoOutFeedback.NameFeedback.StringValue; + } + catch + { + this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback", index); + return ""; + } + })); + + // _Chassis.HdmiOutputs[index].VideoOutFeedback.NameFeedback + this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{index}].VideoOutFeedback.NameFeedback is {outputVideoNameFb}", index, chassisHdmiOutput.VideoOutFeedback.NameFeedback == null ? "NULL" : "NOT NULL"); + OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}RoutedName"), () => + { + try + { + return chassisHdmiOutput.VideoOutFeedback.NameFeedback.StringValue; + } + catch + { + this.LogError("SetupOutputs: Error getting _Chassis.HdmiOutputs[{index}].VideoOutFeedback.NameFeedback", index); + return ""; + } + })); } } From 3a1a7228c6436819a8f887a9f6c4981db8b4f7ad Mon Sep 17 00:00:00 2001 From: jkdevito Date: Sat, 31 Jan 2026 18:45:58 +0100 Subject: [PATCH 46/50] fix: refactor input/output setup methods for improved clarity and logging --- src/Chassis/HdMdNxM4KzEController.cs | 245 ++++++++------------------- 1 file changed, 70 insertions(+), 175 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 453275d..fb457f9 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -68,26 +68,26 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, NoRouteText = props.NoRouteText ?? "None"; - InputNames = new Dictionary(); - InputNames = props.Inputs ?? new Dictionary(); - - OutputNames = new Dictionary(); - OutputNames = props.Outputs ?? new Dictionary(); - - InputPorts = new RoutingPortCollection(); - OutputPorts = new RoutingPortCollection(); - + InputNames = new Dictionary(); + InputNames = props.Inputs ?? new Dictionary(); + + OutputNames = new Dictionary(); + OutputNames = props.Outputs ?? new Dictionary(); + + InputPorts = new RoutingPortCollection(); + OutputPorts = new RoutingPortCollection(); + DeviceNameFeedback = new StringFeedback("DeviceName", () => { try { return Name; } catch { this.LogError("Error getting DeviceNameFeedback"); return ""; } - }); - - InputNameFeedbacks = new FeedbackCollection(); - OutputNameFeedbacks = new FeedbackCollection(); - + }); + + InputNameFeedbacks = new FeedbackCollection(); + OutputNameFeedbacks = new FeedbackCollection(); + VideoInputSyncFeedbacks = new FeedbackCollection(); - InputHdcpEnableFeedback = new FeedbackCollection(); + InputHdcpEnableFeedback = new FeedbackCollection(); VideoOutputRouteFeedbacks = new FeedbackCollection(); OutputRouteNameFeedbacks = new FeedbackCollection(); @@ -111,9 +111,9 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, SetupInputs(); SetupOutputs(); + _Chassis.BaseEvent += Chassis_BaseEvent; _Chassis.DMInputChange += Chassis_DMInputChange; _Chassis.DMOutputChange += Chassis_DMOutputChange; - _Chassis.BaseEvent += Chassis_BaseEvent; AddPostActivationAction(AddFeedbackCollections); } @@ -126,87 +126,39 @@ private void SetupInputs() return; } - //var inputCount = _Chassis.Inputs.Count; - var inputCount = _Chassis.NumberOfInputs; - this.LogError("SetupInputs: Chassis has {inputCount} inputs", inputCount); - for (uint i = 1; i <= inputCount; i++) + foreach(var input in _Chassis.Inputs) { - var index = i; - if (index > inputCount) - { - this.LogError("SetupInputs: index {index} is greater than _Chassis.NumberOfInputs {inputCount}, breaking loop", index, inputCount); - break; - } - // for reference only - //InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].NameFeedback.StringValue)); - //InputNameFeedbacks.Add(new StringFeedback(inputName, () => InputNames[index])); + var inputNumber = input.Number; + this.LogError("SetupInputs: _Chassis.Inputs[{inputNumber}]", inputNumber); - var inputName = string.Format("input{0}", index); - var inputFriendlyName = InputNames[index] ?? inputName; + var inputName = string.Format("input{0}", inputNumber); + var inputFriendlyName = InputNames[inputNumber] ?? inputName; - var chassisInput = _Chassis.Inputs[index]; - this.LogError("SetupInputs: _Chassis.Inputs[{index}] is {input}", index, chassisInput == null ? "NULL" : "NOT NULL"); + // Set Input Name from config + input.Name.StringValue = inputFriendlyName; - var chassisHdmiInput = _Chassis.HdmiInputs[index]; - this.LogError("SetupInputs: _Chassis.HdmiInputs[{index}] is {hdmiInput}", index, chassisHdmiInput == null ? "NULL" : "NOT NULL"); + // Feedbacks + InputNameFeedbacks.Add(new StringFeedback(string.Format($"{inputName}Name"), () => input.NameFeedback.StringValue)); + VideoInputSyncFeedbacks.Add(new BoolFeedback(string.Format($"{inputName}VideoDetected"), () => input.VideoDetectedFeedback.BoolValue)); + } - // Name Feedback - this.LogError("SetupInputs: _Chassis.Inputs[{index}].NameFeedback is {inputNameFb}", index, chassisInput.NameFeedback == null ? "NULL" : "NOT NULL"); - InputNameFeedbacks.Add(new StringFeedback(string.Format($"{inputName}Name"), () => - { - try - { - return chassisInput.NameFeedback.StringValue; - } - catch - { - this.LogError("SetupInputs: Error getting _Chassis.Inputs[{index}].NameFeedback", index); - return ""; - } - })); + foreach(var hdmiInput in _Chassis.HdmiInputs) + { + var hdmiInputNumber = hdmiInput.Number; + this.LogError("SetupInputs: _Chassis.HdmiInputs[{hdmiInputNumber}]", hdmiInputNumber); - // Set Input Name from config - chassisInput.Name.StringValue = inputFriendlyName; - // hdmiInput.Name.StringValue = inputFriendlyName; + var hdmiInputName = string.Format("hdmiInput{0}", hdmiInputNumber); + var hdmiInputFriendlyName = InputNames[hdmiInputNumber] ?? hdmiInputName; // Routing Input Port - InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, chassisHdmiInput, this) + InputPorts.Add(new RoutingInputPort(hdmiInputName, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, hdmiInput, this) { - FeedbackMatchObject = chassisHdmiInput + FeedbackMatchObject = hdmiInput }); - // Video Detected Feedback - this.LogError("SetupInputs: _Chassis.Inputs[{index}].VideoDetectedFeedback is {inputSyncFb}", index, chassisInput.VideoDetectedFeedback == null ? "NULL" : "NOT NULL"); - var inputSyncFbKey = string.Format("{0}VideoDetected", inputName); - VideoInputSyncFeedbacks.Add(new BoolFeedback(inputSyncFbKey, () => - { - try - { - return chassisInput.VideoDetectedFeedback.BoolValue; - } - catch - { - this.LogError("SetupInputs: Error getting _Chassis.Inputs[{index}].VideoDetectedFeedback", index); - return false; - } - })); - - // HDCP Enable Feedback - this.LogError("SetupInputs: _Chassis.HdmiInputs[{index}].HdmiInputPort.HdcpSupportOnFeedback is {inputHdcpFb}", index, chassisHdmiInput.HdmiInputPort.HdcpSupportOnFeedback == null ? "NULL" : "NOT NULL"); - var inputHdcpFbKey = string.Format("{0}HdcpSupportOn", inputName); - InputHdcpEnableFeedback.Add(new BoolFeedback(inputHdcpFbKey, () => - { - try - { - return chassisHdmiInput.HdmiInputPort.HdcpSupportOnFeedback.BoolValue; - } - catch - { - this.LogError("SetupInputs: Error getting _Chassis.HdmiInputs[{index}].HdmiInputPort.HdcpSupportOnFeedback", index); - return false; - } - })); + // Feedbacks + InputHdcpEnableFeedback.Add(new BoolFeedback(hdmiInputName, () => hdmiInput.HdmiInputPort.HdcpSupportOnFeedback.BoolValue)); } } @@ -216,99 +168,42 @@ private void SetupOutputs() { this.LogError("SetupOutputs: Chassis is null. Cannot setup VideoSync feedbacks."); return; - } - - //var outputCount = _Chassis.Outputs.Count; - var outputCount = _Chassis.NumberOfOutputs; - this.LogError("SetupOutputs: Chassis has {outputCount} outputs", outputCount); - for (uint i = 1; i <= outputCount; i++) - { - var index = i; - if (index > outputCount) - { - this.LogError("SetupOutputs: index {index} is greater than _Chassis.NumberOfOutputs {outputCount}, breaking loop", index, outputCount); - break; - } + } + + foreach(var output in _Chassis.Outputs) + { + var outputNumber = output.Number; + this.LogError("SetupOutputs: _Chassis.Outputs[{outputNumber}]", outputNumber); - var outputName = string.Format("output{0}", index); - var outputFriendlyName = OutputNames[index] ?? outputName; + var outputName = string.Format("output{0}", outputNumber); + var outputFriendlyName = OutputNames[outputNumber] ?? outputName; - var chassisOutput = _Chassis.Outputs[index]; - this.LogError("SetupOutputs: _Chassis.Outputs[{index}] is {output}", index, chassisOutput == null ? "NULL" : "NOT NULL"); + // Set Output Name from config + output.Name.StringValue = outputFriendlyName; - var chassisHdmiOutput = _Chassis.HdmiOutputs[index]; - this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{index}] is {hdmiOutput}", index, chassisHdmiOutput == null ? "NULL" : "NOT NULL"); - - // Name Feedback - this.LogError("SetupOutputs: _Chassis.Outputs[{index}].NameFeedback is {outputNameFb}", index, chassisOutput.NameFeedback == null ? "NULL" : "NOT NULL"); - OutputNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}Name"), () => - { - try - { - return chassisOutput.NameFeedback.StringValue; - } - catch - { - this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].NameFeedback", index); - return ""; - } - })); - - // Set Output Name from config - chassisOutput.Name.StringValue = outputFriendlyName; - // hdmiOutput.Name.StringValue = outputFriendlyName; - - // Routing Output Port - OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, chassisHdmiOutput, this) + // Feedbacks + OutputNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}Name"), () => output.NameFeedback.StringValue)); + VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputName}Route"), () => (int)(output.VideoOutFeedback == null ? 0 : output.VideoOutFeedback.Number))); + OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}RoutedName"), () => output.VideoOutFeedback?.NameFeedback.StringValue ?? NoRouteText)); + } + + foreach(var hdmiOutput in _Chassis.HdmiOutputs) + { + var hdmiOutputNumber = hdmiOutput.Number; + this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{hdmiOutputNumber}]", hdmiOutputNumber); + + var hdmiOutputName = string.Format("hdmiOutput{0}", hdmiOutputNumber); + var hdmiOutputFriendlyName = OutputNames[hdmiOutputNumber] ?? hdmiOutputName; + + // Set Output Name from config + hdmiOutput.Name.StringValue = hdmiOutputFriendlyName; + + // Routing Output Port + OutputPorts.Add(new RoutingOutputPort(hdmiOutputName, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, hdmiOutput, this) { - FeedbackMatchObject = chassisHdmiOutput - }); - - // Chassis.Outputs[index].VideoOutFeedback - this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutputFeedback is {outputVideoFb}", index, chassisOutput.VideoOutFeedback == null ? "NULL" : "NOT NULL"); - VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputName}Route"), () => - { - try - { - return (int)(chassisOutput.VideoOutFeedback == null ? 0 : chassisOutput.VideoOutFeedback.Number); - } - catch - { - this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].VideoOutFeedback", index); - return 0; - } - })); - - // _Chassis.Outputs[index].VideoOutFeedback.NameFeedback - this.LogError("SetupOutputs: _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback is {outputVideoNameFb}", index, chassisOutput.VideoOutFeedback.NameFeedback == null ? "NULL" : "NOT NULL"); - OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}RoutedName"), () => - { - try - { - return chassisOutput.VideoOutFeedback.NameFeedback.StringValue; - } - catch - { - this.LogError("SetupOutputs: Error getting _Chassis.Outputs[{index}].VideoOutFeedback.NameFeedback", index); - return ""; - } - })); - - // _Chassis.HdmiOutputs[index].VideoOutFeedback.NameFeedback - this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{index}].VideoOutFeedback.NameFeedback is {outputVideoNameFb}", index, chassisHdmiOutput.VideoOutFeedback.NameFeedback == null ? "NULL" : "NOT NULL"); - OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}RoutedName"), () => - { - try - { - return chassisHdmiOutput.VideoOutFeedback.NameFeedback.StringValue; - } - catch - { - this.LogError("SetupOutputs: Error getting _Chassis.HdmiOutputs[{index}].VideoOutFeedback.NameFeedback", index); - return ""; - } - })); + FeedbackMatchObject = hdmiOutput + }); } } From bd4290230fb14ff2bc74c3d1273675b7ccc18805 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Sat, 31 Jan 2026 19:42:54 +0100 Subject: [PATCH 47/50] fix: enhance input/output name handling and feedback linking for improved clarity --- src/Chassis/HdMdNxM4KzEController.cs | 199 +++++++++------------------ 1 file changed, 63 insertions(+), 136 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index fb457f9..936daeb 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -69,10 +69,16 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, NoRouteText = props.NoRouteText ?? "None"; InputNames = new Dictionary(); - InputNames = props.Inputs ?? new Dictionary(); + foreach(var inputNames in props.Inputs) + { + InputNames.Add(inputNames.Key, inputNames.Value ?? string.Format("Input {0}", inputNames.Key)); + } OutputNames = new Dictionary(); - OutputNames = props.Outputs ?? new Dictionary(); + foreach(var outputName in props.Outputs) + { + OutputNames.Add(outputName.Key, outputName.Value ?? string.Format("Output {0}", outputName.Key)); + } InputPorts = new RoutingPortCollection(); OutputPorts = new RoutingPortCollection(); @@ -93,7 +99,7 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, if (_Chassis is HdMdNxM4kzE _chassis_M4kzE) { - AutoRouteFeedback = new BoolFeedback("AutoRouteFeedback", () => + AutoRouteFeedback = new BoolFeedback("AutoRoute", () => { try { return _chassis_M4kzE.AutoRouteOnFeedback?.BoolValue ?? false; } catch { this.LogError("Error getting AutoRouteFeedback"); return false; } @@ -101,7 +107,7 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, } if (_Chassis is HdMd4xX4kzE _chassis_X4kzE) { - PriorityRouteFeedback = new BoolFeedback("PriorityRouteFeedback", () => + PriorityRouteFeedback = new BoolFeedback("PriorityRoute", () => { try { return _chassis_X4kzE.PriorityRouteOnFeedback?.BoolValue ?? false; } catch { this.LogError("Error getting PriorityRouteFeedback"); return false; } @@ -131,15 +137,14 @@ private void SetupInputs() var inputNumber = input.Number; this.LogError("SetupInputs: _Chassis.Inputs[{inputNumber}]", inputNumber); - var inputName = string.Format("input{0}", inputNumber); - var inputFriendlyName = InputNames[inputNumber] ?? inputName; + var inputFriendlyName = InputNames[inputNumber]; // Set Input Name from config input.Name.StringValue = inputFriendlyName; // Feedbacks - InputNameFeedbacks.Add(new StringFeedback(string.Format($"{inputName}Name"), () => input.NameFeedback.StringValue)); - VideoInputSyncFeedbacks.Add(new BoolFeedback(string.Format($"{inputName}VideoDetected"), () => input.VideoDetectedFeedback.BoolValue)); + InputNameFeedbacks.Add(new StringFeedback(inputFriendlyName, () => input.NameFeedback.StringValue)); + VideoInputSyncFeedbacks.Add(new BoolFeedback(inputFriendlyName, () => input.VideoDetectedFeedback.BoolValue)); } foreach(var hdmiInput in _Chassis.HdmiInputs) @@ -158,7 +163,7 @@ private void SetupInputs() }); // Feedbacks - InputHdcpEnableFeedback.Add(new BoolFeedback(hdmiInputName, () => hdmiInput.HdmiInputPort.HdcpSupportOnFeedback.BoolValue)); + InputHdcpEnableFeedback.Add(new BoolFeedback(hdmiInputFriendlyName, () => hdmiInput.HdmiInputPort.HdcpSupportOnFeedback.BoolValue)); } } @@ -175,16 +180,15 @@ private void SetupOutputs() var outputNumber = output.Number; this.LogError("SetupOutputs: _Chassis.Outputs[{outputNumber}]", outputNumber); - var outputName = string.Format("output{0}", outputNumber); - var outputFriendlyName = OutputNames[outputNumber] ?? outputName; + var outputFriendlyName = OutputNames[outputNumber]; // Set Output Name from config output.Name.StringValue = outputFriendlyName; // Feedbacks - OutputNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}Name"), () => output.NameFeedback.StringValue)); - VideoOutputRouteFeedbacks.Add(new IntFeedback(string.Format($"{outputName}Route"), () => (int)(output.VideoOutFeedback == null ? 0 : output.VideoOutFeedback.Number))); - OutputRouteNameFeedbacks.Add(new StringFeedback(string.Format($"{outputName}RoutedName"), () => output.VideoOutFeedback?.NameFeedback.StringValue ?? NoRouteText)); + OutputNameFeedbacks.Add(new StringFeedback(outputFriendlyName, () => output.NameFeedback.StringValue)); + VideoOutputRouteFeedbacks.Add(new IntFeedback(outputFriendlyName, () => (int)(output.VideoOutFeedback == null ? 0 : output.VideoOutFeedback.Number))); + OutputRouteNameFeedbacks.Add(new StringFeedback(outputFriendlyName, () => output.VideoOutFeedback?.NameFeedback.StringValue ?? NoRouteText)); } foreach(var hdmiOutput in _Chassis.HdmiOutputs) @@ -193,7 +197,7 @@ private void SetupOutputs() this.LogError("SetupOutputs: _Chassis.HdmiOutputs[{hdmiOutputNumber}]", hdmiOutputNumber); var hdmiOutputName = string.Format("hdmiOutput{0}", hdmiOutputNumber); - var hdmiOutputFriendlyName = OutputNames[hdmiOutputNumber] ?? hdmiOutputName; + var hdmiOutputFriendlyName = OutputNames[hdmiOutputNumber]; // Set Output Name from config hdmiOutput.Name.StringValue = hdmiOutputFriendlyName; @@ -228,26 +232,8 @@ public void EnableHdcp(uint port) { if (port <= 0 || port > _Chassis.NumberOfInputs) return; - var hdmiInput = _Chassis.HdmiInputs[port]; - if (hdmiInput?.HdmiInputPort == null) - { - this.LogWarning("EnableHdcp: HdmiInputPort at index {port} is null. Cannot enable HDCP.", port); - return; - } - - hdmiInput.HdmiInputPort.HdcpSupportOn(); - - if (InputNames.ContainsKey(port)) - { - var inputName = InputNames[port]; - var feedback = InputHdcpEnableFeedback.FirstOrDefault(f => f.Key == inputName); - if (feedback == null) - { - return; - } - - feedback.FireUpdate(); - } + _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOn(); + InputHdcpEnableFeedback[InputNames[port]]?.FireUpdate(); } /// @@ -258,24 +244,8 @@ public void DisableHdcp(uint port) { if (port <= 0 || port > _Chassis.NumberOfInputs) return; - var hdmiInput = _Chassis.HdmiInputs[port]; - if (hdmiInput?.HdmiInputPort == null) - { - this.LogWarning("DisableHdcp: HdmiInputPort at index {port} is null. Cannot disable HDCP.", port); - return; - } - - hdmiInput.HdmiInputPort.HdcpSupportOff(); - - if (InputNames.TryGetValue(port, out var inputName)) - { - var feedback = InputHdcpEnableFeedback.FirstOrDefault(f => f.Key == inputName); - if (feedback == null) - { - return; - } - feedback.FireUpdate(); - } + _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOff(); + InputHdcpEnableFeedback[InputNames[port]]?.FireUpdate(); } /// @@ -284,13 +254,13 @@ public void DisableHdcp(uint port) public void EnableAutoRoute() { if (_Chassis.NumberOfOutputs > 1) return; - if (_Chassis is HdMdNxM4kzE _chassis_M4kzE) + if (!(_Chassis is HdMdNxM4kzE _chassis)) { - _chassis_M4kzE.AutoRouteOn(); + this.LogVerbose("EnableAutoRoute: AutoRoute is not supported on this chassis."); return; } - - this.LogVerbose("EnableAutoRoute: AutoRoute is not supported on this chassis."); + + _chassis.AutoRouteOn(); } /// @@ -299,13 +269,13 @@ public void EnableAutoRoute() public void DisableAutoRoute() { if (_Chassis.NumberOfOutputs > 1) return; - if (_Chassis is HdMdNxM4kzE _chassis_M4kzE) + if (!(_Chassis is HdMdNxM4kzE _chassis)) { - _chassis_M4kzE.AutoRouteOff(); + this.LogVerbose("DisableAutoRoute: AutoRoute is not supported on this chassis."); return; } - - this.LogVerbose("DisableAutoRoute: AutoRoute is not supported on this chassis."); + + _chassis.AutoRouteOff(); } /// @@ -313,13 +283,14 @@ public void DisableAutoRoute() /// public void EnablePriorityRoute() { - if (_Chassis is HdMd4xX4kzE _chassis_X4kzE) + //if (!(_Chassis is HdMd4xX4kzE _chassis)) + if(!(_Chassis is HdMd4xX4kzE _chassis)) { - _chassis_X4kzE.PriorityRouteOn(); + this.LogVerbose("EnablePriorityRoute: Priority Route is not supported on {key}.", Key); return; } - - this.LogVerbose("EnablePriorityRoute: Priority Route is not supported on this chassis."); + + _chassis.PriorityRouteOn(); } /// @@ -392,11 +363,11 @@ public void AddFeedbackCollections() /// /// Adds a feedback to the Feedbacks collection if it does not already exist. /// - public void AddFeedbackToList(Essentials.Core.Feedback newFb) + public void AddFeedbackToList(Core.Feedback newFb) { if (newFb == null) return; - if (Feedbacks.Any(f => f.Key == newFb.Key)) return; + //if (Feedbacks.Any(f => f.Key == newFb.Key)) return; // TODO - Remove after testing this.LogVerbose("AddFeedbackToList: adding {feedbackKey} to Feedbacks collection", newFb.Key); @@ -417,7 +388,7 @@ public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingS { var input = inputSelector as HdMdNxM4kzEHdmiInput; var output = outputSelector as HdMdNxM4kzEHdmiOutput; - this.LogVerbose("ExecuteSwitch: input={0} output={1}", input, output); + this.LogVerbose("ExecuteSwitch: input={input} output={output}", input, output); if (output == null) { @@ -443,8 +414,6 @@ public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingS /// The type of signal to switch. public void ExecuteNumericSwitch(ushort inputSelector, ushort outputSelector, eRoutingSignalType signalType) { - this.LogInformation("ExecuteNumericSwitch: inputSelector={inputSelector} outputSelector={outputSelector}", inputSelector, outputSelector); - var input = inputSelector == 0 ? null : _Chassis.HdmiInputs[inputSelector]; var output = _Chassis.HdmiOutputs[outputSelector]; @@ -484,10 +453,9 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join this.LogInformation("Please update config to use 'eiscapiadvanced' to get all join map features for this device."); } - if (IsOnline != null) IsOnline?.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); - DeviceNameFeedback?.LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); - + IsOnline?.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + if (_Chassis is HdMdNxM4kzE _chassis_M4kzE) { this.LogInformation("LinkToApi: _Chassis is HdMdNxM4kzE, setting up AutoRoute links"); @@ -505,81 +473,37 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join // PriorityRouteFeedback?.LinkInputSig(trilist.BooleanInput[joinMap.EnablePriorityRoute.JoinNumber]); } - if (InputNames != null) + foreach(var input in _Chassis.Inputs) { - foreach (var kvp in InputNames) - { - var input = kvp.Key; - var inputName = kvp.Value; + uint inputNumber = input.Number; + var joinOffset = inputNumber - 1; - if (input < 1 || input > _Chassis.NumberOfInputs) - { - this.LogWarning("LinkToApi: Input index {index} is out of range (1-{max}). Skipping.", input, _Chassis.NumberOfInputs); - continue; - } - - var joinIndex = input - 1; - - var joinNumberInputSync = joinMap.InputSync.JoinNumber + joinIndex; - var joinNumberEnableInputHdcp = joinMap.EnableInputHdcp.JoinNumber + joinIndex; - var joinNumberDisableInputHdcp = joinMap.DisableInputHdcp.JoinNumber + joinIndex; - var joinNumberInputName = joinMap.InputName.JoinNumber + joinIndex; + this.LogInformation("LinkToApi: _Chassis.Inputs[{inputNumber}].Name = {inputName}", input.Number, input.Name.StringValue); - this.LogInformation("LinkToApi: Input {input} | joinIndex = {joinIndex}, InputSyncJoin = {joinNumberInputSync}, EnableHdcpJoin = {joinNumberEnableInputHdcp}, DisableHdcpJoin = {joinNumberDisableInputHdcp}, InputNameJoin = {joinNumberInputName}", - input, joinIndex, joinNumberInputSync, joinNumberEnableInputHdcp, joinNumberDisableInputHdcp, joinNumberInputName); + trilist.SetSigTrueAction(joinMap.EnableInputHdcp.JoinNumber + joinOffset, () => EnableHdcp(inputNumber)); + trilist.SetSigTrueAction(joinMap.DisableInputHdcp.JoinNumber + joinOffset, () => DisableHdcp(inputNumber)); - //Digital - VideoInputSyncFeedbacks[inputName]?.LinkInputSig(trilist.BooleanInput[joinNumberInputSync]); - InputHdcpEnableFeedback[inputName]?.LinkInputSig(trilist.BooleanInput[joinNumberEnableInputHdcp]); - InputHdcpEnableFeedback[inputName]?.LinkComplementInputSig(trilist.BooleanInput[joinNumberDisableInputHdcp]); + InputNameFeedbacks[InputNames[inputNumber]]?.LinkInputSig(trilist.StringInput[joinMap.InputName.JoinNumber + joinOffset]); - trilist.SetSigTrueAction(joinNumberEnableInputHdcp, () => EnableHdcp(input)); - trilist.SetSigTrueAction(joinNumberDisableInputHdcp, () => DisableHdcp(input)); + InputHdcpEnableFeedback[InputNames[inputNumber]]?.LinkInputSig(trilist.BooleanInput[joinMap.EnableInputHdcp.JoinNumber + joinOffset]); + InputHdcpEnableFeedback[InputNames[inputNumber]]?.LinkComplementInputSig(trilist.BooleanInput[joinMap.DisableInputHdcp.JoinNumber + joinOffset]); - //Serial - InputNameFeedbacks[inputName]?.LinkInputSig(trilist.StringInput[joinNumberInputName]); - } - } - else - { - this.LogWarning("LinkToApi: InputNames is null. Skipping input linking."); + VideoInputSyncFeedbacks[InputNames[inputNumber]]?.LinkInputSig(trilist.BooleanInput[joinMap.InputSync.JoinNumber + joinOffset]); } - if (OutputNames != null) + foreach (var output in _Chassis.Outputs) { - foreach (var kvp in OutputNames) - { - var output = kvp.Key; - var outputName = kvp.Value; - - if (output < 1 || output > _Chassis.NumberOfOutputs) - { - this.LogWarning("LinkToApi: Output index {index} is out of range (1-{max}). Skipping.", output, _Chassis.NumberOfOutputs); - continue; - } - - var joinIndex = output - 1; - - var joinNumberOutputRoute = joinMap.OutputRoute.JoinNumber + joinIndex; - var joinNumberOutputName = joinMap.OutputName.JoinNumber + joinIndex; - var joinNumberOutputRouteName = joinMap.OutputRoutedName.JoinNumber + joinIndex; + uint outputNumber = output.Number; + var joinOffset = outputNumber - 1; - this.LogInformation("LinkToApi: Output {output} | joinIndex = {joinIndex}, OutputRouteJoin = {joinNumberOutputRoute}, OutputNameJoin = {joinNumberOutputName}, OutputRouteNameJoin = {joinNumberOutputRouteName}", - output, joinIndex, joinNumberOutputRoute, joinNumberOutputName, joinNumberOutputRouteName); + this.LogInformation("LinkToApi: _Chassis.Outputs[{outputNumber}].Name = {outputName}", output.Number, output.Name.StringValue); - //Analog - VideoOutputRouteFeedbacks[outputName]?.LinkInputSig(trilist.UShortInput[joinNumberOutputRoute]); + trilist.SetUShortSigAction(joinMap.OutputRoute.JoinNumber + joinOffset, (a) => ExecuteNumericSwitch(a, (ushort)outputNumber, eRoutingSignalType.AudioVideo)); - trilist.SetUShortSigAction(joinNumberOutputRoute, (a) => ExecuteNumericSwitch(a, (ushort)output, eRoutingSignalType.AudioVideo)); + OutputNameFeedbacks[OutputNames[outputNumber]]?.LinkInputSig(trilist.StringInput[joinMap.OutputName.JoinNumber + joinOffset]); + OutputRouteNameFeedbacks[OutputNames[outputNumber]]?.LinkInputSig(trilist.StringInput[joinMap.OutputRoutedName.JoinNumber + joinOffset]); - //Serial - OutputNameFeedbacks[outputName]?.LinkInputSig(trilist.StringInput[joinNumberOutputName]); - OutputRouteNameFeedbacks[outputName]?.LinkInputSig(trilist.StringInput[joinNumberOutputRouteName]); - } - } - else - { - this.LogWarning("LinkToApi: OutputNames is null. Skipping output linking."); + VideoOutputRouteFeedbacks[OutputNames[outputNumber]]?.LinkInputSig(trilist.UShortInput[joinMap.OutputRoute.JoinNumber + joinOffset]); } _Chassis.OnlineStatusChange += Chassis_OnlineStatusChange; @@ -588,6 +512,8 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join { if (!args.DeviceOnLine) return; + DeviceNameFeedback?.FireUpdate(); + // feedback updates was moved to the Chassis_OnlineStatusChange // due to the amount of time it takes for the device to come online }; @@ -647,6 +573,7 @@ private void Chassis_BaseEvent(GenericBase device, BaseEventArgs args) var eventName = typeof(BaseEventArgs) .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static) .FirstOrDefault(f => f.IsLiteral && (int)f.GetValue(null) == args.EventId)?.Name ?? args.EventId.ToString(); + this.LogInformation("Chassis_BaseEvent: received {eventName} (id-{eventId}) received from device {deviceName}", eventName, args.EventId, device.GetType().Name); } @@ -670,7 +597,7 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice feedback?.FireUpdate(); } - if (_Chassis is HdMd4xX4kzE) + if (_Chassis is HdMdNxM4kzE) { AutoRouteFeedback?.FireUpdate(); } From c8c8b338168fe59c1a284de0aefa1e4b9f04d132 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Sat, 31 Jan 2026 21:00:30 +0100 Subject: [PATCH 48/50] fix: improve logging for ExecuteSwitch and DMOutputChange methods for better error tracking --- src/Chassis/HdMdNxM4KzEController.cs | 46 ++++++++++++---------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 936daeb..379c649 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -388,14 +388,15 @@ public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingS { var input = inputSelector as HdMdNxM4kzEHdmiInput; var output = outputSelector as HdMdNxM4kzEHdmiOutput; - this.LogVerbose("ExecuteSwitch: input={input} output={output}", input, output); - + if (output == null) { - this.LogInformation("Unable to make switch. output selector is not HdMdNxM4kzEHdmiOutput"); + this.LogError("ExecuteSwitch: Unable to make switch. output selector is not HdMdNxM4kzEHdmiOutput"); return; } + this.LogVerbose("ExecuteSwitch: input={input} output={output}", input, output); + // Try to make switch only when necessary. The unit appears to toggle when already selected. var current = output.VideoOut; if (current != input) @@ -661,37 +662,30 @@ void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) var eventName = typeof(DMOutputEventIds) .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static) .FirstOrDefault(f => f.IsLiteral && (int)f.GetValue(null) == args.EventId)?.Name ?? args.EventId.ToString(); - this.LogInformation("Chassis_DMOutputChange: received {eventName} (id-{eventId}); Index = {index}; Number = {number}; Stream = {stream} ", eventName, args.EventId, args.Index, args.Number, args.Stream); - + switch (args.EventId) { case DMOutputEventIds.VideoOutEventId: { - var output = args.Number; - var outputName = OutputNames[output]; + var outputNumber = args.Number; + var outputName = OutputNames[outputNumber]; + var inputNumber = _Chassis.HdmiOutputs[outputNumber].VideoOutFeedback?.Number ?? 0; - var inputNumber = _Chassis.HdmiOutputs[output].VideoOutFeedback?.Number ?? 0; + this.LogInformation("Chassis_DMOutputChange: received {eventName} (id-{eventId}) | Input {inputNumber} routed to Output {outputNumber}", eventName, args.EventId, inputNumber, outputNumber); - var feedback = VideoOutputRouteFeedbacks.FirstOrDefault(f => f.Key == outputName); - if (feedback != null) + var feedback = VideoOutputRouteFeedbacks[outputName]; + if(feedback == null) { - var inPort = InputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output].VideoOutFeedback); - var outPort = OutputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output]); - - try - { - feedback.FireUpdate(); - OnSwitchChange(new RoutingNumericEventArgs(output, inputNumber, outPort, inPort, eRoutingSignalType.AudioVideo)); - } - catch (Exception ex) - { - this.LogError(ex, "Chassis_DMOutputChange: Exception occurred while updating {eventName} (id-{eventId}) {feedbackKey}", eventName, args.EventId, feedback.Key); - } - } - else - { - this.LogInformation("Chassis_DMOutputChange: {outputName} not found in VideoOutputRouteFeedbacks", outputName); + this.LogInformation("Chassis_DMOutputChange: VideoOutputRouteFeedbacks does not contain key {outputName}", outputName); + break; } + + var inPort = InputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[outputNumber].VideoOutFeedback); + var outPort = OutputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[outputNumber]); + + feedback.FireUpdate(); + OnSwitchChange(new RoutingNumericEventArgs(outputNumber, inputNumber, outPort, inPort, eRoutingSignalType.AudioVideo)); + break; } case DMOutputEventIds.AutoModeOffEventId: From 0e7322457d7c4da137c83f8d1f4c0834997293e0 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Sat, 31 Jan 2026 23:36:52 +0100 Subject: [PATCH 49/50] fix: streamline AutoRoute and PriorityRoute feedback handling for improved clarity --- src/Chassis/HdMdNxM4KzEController.cs | 70 ++++------------------------ 1 file changed, 10 insertions(+), 60 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index 379c649..be97c2e 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -97,19 +97,17 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, VideoOutputRouteFeedbacks = new FeedbackCollection(); OutputRouteNameFeedbacks = new FeedbackCollection(); - if (_Chassis is HdMdNxM4kzE _chassis_M4kzE) + AutoRouteFeedback = new BoolFeedback("AutoRoute", () => { - AutoRouteFeedback = new BoolFeedback("AutoRoute", () => - { - try { return _chassis_M4kzE.AutoRouteOnFeedback?.BoolValue ?? false; } - catch { this.LogError("Error getting AutoRouteFeedback"); return false; } - }); - } - if (_Chassis is HdMd4xX4kzE _chassis_X4kzE) + try { return _chassis_M4kzE.AutoRouteOnFeedback?.BoolValue ?? false; } + catch { this.LogError("Error getting AutoRouteFeedback"); return false; } + }); + + if (_Chassis is HdMd4xX4kzE _chassis) { PriorityRouteFeedback = new BoolFeedback("PriorityRoute", () => { - try { return _chassis_X4kzE.PriorityRouteOnFeedback?.BoolValue ?? false; } + try { return _chassis.PriorityRouteOnFeedback?.BoolValue ?? false; } catch { this.LogError("Error getting PriorityRouteFeedback"); return false; } }); } @@ -519,52 +517,7 @@ public override void LinkToApi(BasicTriList trilist, uint joinStart, string join // due to the amount of time it takes for the device to come online }; } - - /* - private void UpdateFeedbacks() - { - IsOnline?.FireUpdate(); - DeviceNameFeedback?.FireUpdate(); - AutoRouteFeedback?.FireUpdate(); - - foreach (var item in VideoInputSyncFeedbacks) - { - item.FireUpdate(); - } - - foreach (var item in VideoOutputRouteFeedbacks) - { - item.FireUpdate(); - } - - foreach (var item in InputHdcpEnableFeedback) - { - item.FireUpdate(); - } - - foreach (var item in InputNameFeedbacks) - { - item.FireUpdate(); - } - - foreach (var item in OutputNameFeedbacks) - { - item.FireUpdate(); - } - - foreach (var item in OutputRouteNameFeedbacks) - { - item.FireUpdate(); - } - - foreach (var item in Feedbacks) - { - // TODO - Remove after testing - this.LogInformation("UpdateFeedbacks: Firing feedback for {itemKey}", item.Key); - item.FireUpdate(); - } - } - */ + #endregion #region Events @@ -598,11 +551,8 @@ void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice feedback?.FireUpdate(); } - if (_Chassis is HdMdNxM4kzE) - { - AutoRouteFeedback?.FireUpdate(); - } - + AutoRouteFeedback?.FireUpdate(); + if (_Chassis is HdMd4xX4kzE) { PriorityRouteFeedback?.FireUpdate(); From a70fa1d72ac9b195ae44c8cec6e303acd3fe36ad Mon Sep 17 00:00:00 2001 From: jkdevito Date: Sat, 31 Jan 2026 23:42:30 +0100 Subject: [PATCH 50/50] fix: simplify improper reference for autoroute feedback --- src/Chassis/HdMdNxM4KzEController.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Chassis/HdMdNxM4KzEController.cs b/src/Chassis/HdMdNxM4KzEController.cs index be97c2e..9d9dd68 100644 --- a/src/Chassis/HdMdNxM4KzEController.cs +++ b/src/Chassis/HdMdNxM4KzEController.cs @@ -97,11 +97,7 @@ public HdMdNxM4kZEController(string key, string name, HdMdNxM4kzE chassis, VideoOutputRouteFeedbacks = new FeedbackCollection(); OutputRouteNameFeedbacks = new FeedbackCollection(); - AutoRouteFeedback = new BoolFeedback("AutoRoute", () => - { - try { return _chassis_M4kzE.AutoRouteOnFeedback?.BoolValue ?? false; } - catch { this.LogError("Error getting AutoRouteFeedback"); return false; } - }); + AutoRouteFeedback = new BoolFeedback("AutoRoute", () =>_Chassis.AutoRouteOnFeedback?.BoolValue ?? false); if (_Chassis is HdMd4xX4kzE _chassis) {