From 2048e3f65d02c8baf257e58eee35c85f1fe5bfca Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Wed, 3 Dec 2025 14:07:50 -0600 Subject: [PATCH 1/8] fix: GenericSink implements ICurrentSources --- .../Generic/GenericSink.cs | 62 ++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs b/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs index 8f2fc1e24..a01daa2e9 100644 --- a/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs +++ b/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs @@ -1,8 +1,10 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using PepperDash.Core; using PepperDash.Core.Logging; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Routing; using Serilog.Events; namespace PepperDash.Essentials.Devices.Common.Generic @@ -10,8 +12,17 @@ namespace PepperDash.Essentials.Devices.Common.Generic /// /// Represents a GenericSink /// - public class GenericSink : EssentialsDevice, IRoutingSinkWithSwitchingWithInputPort + public class GenericSink : EssentialsDevice, IRoutingSinkWithSwitchingWithInputPort, ICurrentSources { + /// + public Dictionary CurrentSources { get; private set; } + + /// + public Dictionary CurrentSourceKeys { get; private set; } + + /// + public event EventHandler CurrentSourcesChanged; + /// /// Initializes a new instance of the GenericSink class /// @@ -26,6 +37,53 @@ public GenericSink(string key, string name) : base(key, name) InputPorts.Add(inputPort); } + /// + public void SetCurrentSource(eRoutingSignalType signalType, string sourceListKey, SourceListItem sourceListItem) + { + foreach (eRoutingSignalType type in Enum.GetValues(typeof(eRoutingSignalType))) + { + var flagValue = Convert.ToInt32(type); + // Skip if flagValue is 0 or not a power of two (i.e., not a single-bit flag). + // (flagValue & (flagValue - 1)) != 0 checks if more than one bit is set. + if (flagValue == 0 || (flagValue & (flagValue - 1)) != 0) + { + this.LogDebug("Skipping {type}", type); + continue; + } + + this.LogDebug("setting {type}", type); + + if (signalType.HasFlag(type)) + { + UpdateCurrentSources(type, sourceListKey, sourceListItem); + } + } + // Raise the CurrentSourcesChanged event + CurrentSourcesChanged?.Invoke(this, EventArgs.Empty); + } + + private void UpdateCurrentSources(eRoutingSignalType signalType, string sourceListKey, SourceListItem sourceListItem) + { + if (CurrentSources.ContainsKey(signalType)) + { + CurrentSources[signalType] = sourceListItem; + } + else + { + CurrentSources.Add(signalType, sourceListItem); + } + + // Update the current source key for the specified signal type + if (CurrentSourceKeys.ContainsKey(signalType)) + { + CurrentSourceKeys[signalType] = sourceListKey; + } + else + { + CurrentSourceKeys.Add(signalType, sourceListKey); + } + } + /// /// Gets or sets the InputPorts /// From 6a33e7c99d705d939bd5df4fe5aa444e6268fc05 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Wed, 3 Dec 2025 14:19:26 -0600 Subject: [PATCH 2/8] fix: initialize current sources dictionaries --- .../Routing/Extensions.cs | 35 ++++++++++--------- .../Generic/GenericSink.cs | 12 +++++++ 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/PepperDash.Essentials.Core/Routing/Extensions.cs b/src/PepperDash.Essentials.Core/Routing/Extensions.cs index 17f1e50d9..4346f3a93 100644 --- a/src/PepperDash.Essentials.Core/Routing/Extensions.cs +++ b/src/PepperDash.Essentials.Core/Routing/Extensions.cs @@ -1,11 +1,11 @@ -using Crestron.SimplSharpPro.Keypads; -using PepperDash.Essentials.Core.Queues; -using PepperDash.Essentials.Core.Routing; -using Serilog.Events; -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using Crestron.SimplSharpPro.Keypads; +using PepperDash.Essentials.Core.Queues; +using PepperDash.Essentials.Core.Routing; +using Serilog.Events; using Debug = PepperDash.Core.Debug; @@ -115,7 +115,7 @@ public static void RemoveRouteRequestForDestination(string destinationKey) public static (RouteDescriptor, RouteDescriptor) GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort, RoutingOutputPort sourcePort) { // if it's a single signal type, find the route - if (!signalType.HasFlag(eRoutingSignalType.AudioVideo) && + if (!signalType.HasFlag(eRoutingSignalType.AudioVideo) && !(signalType.HasFlag(eRoutingSignalType.Video) && signalType.HasFlag(eRoutingSignalType.SecondaryAudio))) { var singleTypeRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, signalType); @@ -134,14 +134,15 @@ public static (RouteDescriptor, RouteDescriptor) GetRouteToSource(this IRoutingI } // otherwise, audioVideo needs to be handled as two steps. - Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {sourceKey} of type {type}", destination, source.Key); + Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {sourceKey} of type {type}", destination, source.Key, signalType); RouteDescriptor audioRouteDescriptor; if (signalType.HasFlag(eRoutingSignalType.SecondaryAudio)) { audioRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, eRoutingSignalType.SecondaryAudio); - } else + } + else { audioRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, eRoutingSignalType.Audio); } @@ -199,13 +200,13 @@ private static void ReleaseAndMakeRoute(IRoutingInputs destination, IRoutingOutp Source = source, SourcePort = sourcePort, SignalType = signalType - }; + }; var coolingDevice = destination as IWarmingCooling; //We already have a route request for this device, and it's a cooling device and is cooling if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true) - { + { coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown; coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown; @@ -219,7 +220,7 @@ private static void ReleaseAndMakeRoute(IRoutingInputs destination, IRoutingOutp //New Request if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true) - { + { coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown; RouteRequests.Add(destination.Key, routeRequest); @@ -239,9 +240,9 @@ private static void ReleaseAndMakeRoute(IRoutingInputs destination, IRoutingOutp Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is NOT cooling down. Removing stored route request and routing to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key); } - routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination,destinationPort?.Key ?? string.Empty, false)); + routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, destinationPort?.Key ?? string.Empty, false)); - routeRequestQueue.Enqueue(new RouteRequestQueueItem(RunRouteRequest, routeRequest)); + routeRequestQueue.Enqueue(new RouteRequestQueueItem(RunRouteRequest, routeRequest)); } /// @@ -272,7 +273,8 @@ private static void RunRouteRequest(RouteRequest request) audioOrSingleRoute.ExecuteRoutes(); videoRoute?.ExecuteRoutes(); - } catch(Exception ex) + } + catch (Exception ex) { Debug.LogMessage(ex, "Exception Running Route Request {request}", null, request); } @@ -305,9 +307,10 @@ private static void ReleaseRouteInternal(IRoutingInputs destination, string inpu Debug.LogMessage(LogEventLevel.Information, "Releasing current route: {0}", destination, current.Source.Key); current.ReleaseRoutes(clearRoute); } - } catch (Exception ex) + } + catch (Exception ex) { - Debug.LogMessage(ex, "Exception releasing route for '{destination}':'{inputPortKey}'",null, destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey); + Debug.LogMessage(ex, "Exception releasing route for '{destination}':'{inputPortKey}'", null, destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey); } } diff --git a/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs b/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs index a01daa2e9..7e933b519 100644 --- a/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs +++ b/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs @@ -35,6 +35,18 @@ public GenericSink(string key, string name) : base(key, name) var inputPort = new RoutingInputPort(RoutingPortNames.AnyVideoIn, eRoutingSignalType.AudioVideo | eRoutingSignalType.SecondaryAudio, eRoutingPortConnectionType.Hdmi, null, this); InputPorts.Add(inputPort); + + CurrentSources = new Dictionary + { + { eRoutingSignalType.Audio, null }, + { eRoutingSignalType.Video, null }, + }; + + CurrentSourceKeys = new Dictionary + { + { eRoutingSignalType.Audio, string.Empty }, + { eRoutingSignalType.Video, string.Empty }, + }; } /// From bce1e3610ea0c880e18f67e6dddcb686e18d4cd7 Mon Sep 17 00:00:00 2001 From: Nick Genovese Date: Wed, 10 Dec 2025 16:58:41 -0600 Subject: [PATCH 3/8] fix: copy dictionaries - fixed multiple enumeration exception --- .../Messengers/CurrentSourcesMessenger.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CurrentSourcesMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CurrentSourcesMessenger.cs index 9643a6075..a7c4614b3 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CurrentSourcesMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CurrentSourcesMessenger.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; @@ -39,10 +40,14 @@ protected override void RegisterActions() sourceDevice.CurrentSourcesChanged += (sender, e) => { + // need to copy the dictionaries to avoid enumeration issues + var currentSourceKeys = sourceDevice.CurrentSourceKeys.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + var currentSources = sourceDevice.CurrentSources.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + PostStatusMessage(JToken.FromObject(new { - currentSourceKeys = sourceDevice.CurrentSourceKeys, - currentSources = sourceDevice.CurrentSources + currentSourceKeys, + currentSources, })); }; } From 210b398a13f04e9cfb1dbcdc5f210e9e40307c0f Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 16 Dec 2025 10:04:01 -0600 Subject: [PATCH 4/8] fix: implement PR Review suggestions --- .../Routing/Extensions.cs | 2 +- .../Generic/GenericSink.cs | 20 ++----------------- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/PepperDash.Essentials.Core/Routing/Extensions.cs b/src/PepperDash.Essentials.Core/Routing/Extensions.cs index 4346f3a93..5d09176f8 100644 --- a/src/PepperDash.Essentials.Core/Routing/Extensions.cs +++ b/src/PepperDash.Essentials.Core/Routing/Extensions.cs @@ -134,7 +134,7 @@ public static (RouteDescriptor, RouteDescriptor) GetRouteToSource(this IRoutingI } // otherwise, audioVideo needs to be handled as two steps. - Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {sourceKey} of type {type}", destination, source.Key, signalType); + Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {destinationKey} to {sourceKey} of type {type}", destination, source.Key, signalType); RouteDescriptor audioRouteDescriptor; diff --git a/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs b/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs index 7e933b519..64617770f 100644 --- a/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs +++ b/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs @@ -76,24 +76,8 @@ public void SetCurrentSource(eRoutingSignalType signalType, string sourceListKey private void UpdateCurrentSources(eRoutingSignalType signalType, string sourceListKey, SourceListItem sourceListItem) { - if (CurrentSources.ContainsKey(signalType)) - { - CurrentSources[signalType] = sourceListItem; - } - else - { - CurrentSources.Add(signalType, sourceListItem); - } - - // Update the current source key for the specified signal type - if (CurrentSourceKeys.ContainsKey(signalType)) - { - CurrentSourceKeys[signalType] = sourceListKey; - } - else - { - CurrentSourceKeys.Add(signalType, sourceListKey); - } + CurrentSources[signalType] = sourceListItem; + CurrentSourceKeys[signalType] = sourceListKey; } /// From c5b0872a4c870370c9ca66d021e06ce1e1a0e2bf Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 16 Dec 2025 15:41:33 -0600 Subject: [PATCH 5/8] fix: DeviceVolumeMessenger only sends rawValue when device implements it --- .../Messengers/DeviceVolumeMessenger.cs | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceVolumeMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceVolumeMessenger.cs index fba7c6432..800423527 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceVolumeMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/DeviceVolumeMessenger.cs @@ -130,34 +130,33 @@ protected override void RegisterActions() feedback.MuteFeedback.OutputChange += (sender, args) => { - PostStatusMessage(JToken.FromObject( - new - { - volume = new - { - muted = args.BoolValue - } - }) - ); + var message = new VolumeStateMessage + { + Volume = new Volume + { + Muted = args.BoolValue + } + }; + + PostStatusMessage(JToken.FromObject(message)); }; feedback.VolumeLevelFeedback.OutputChange += (sender, args) => { - var rawValue = ""; - if (feedback is IBasicVolumeWithFeedbackAdvanced volumeAdvanced) + var message = new VolumeStateMessage { - rawValue = volumeAdvanced.RawVolumeLevel.ToString(); - } - - var message = new - { - volume = new + Volume = new Volume { - level = args.IntValue, - rawValue + Level = args.IntValue, } }; + if (device is IBasicVolumeWithFeedbackAdvanced volumeAdvanced) + { + message.Volume.RawValue = volumeAdvanced.RawVolumeLevel.ToString(); + message.Volume.Units = volumeAdvanced.Units; + } + PostStatusMessage(JToken.FromObject(message)); }; } From 092896bb25deb825017bf9622d6f067cfe27b21a Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 23 Dec 2025 09:57:49 -0600 Subject: [PATCH 6/8] fix: support for legacy UDP EISC --- .../Bridges/BridgeBase.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs b/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs index 92ac2b9c0..d8f19bc8f 100644 --- a/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs +++ b/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs @@ -427,9 +427,12 @@ public class ApiRoomPropertiesConfig /// public class EiscApiAdvancedFactory : EssentialsDeviceFactory { + /// + /// Constructor + /// public EiscApiAdvancedFactory() { - TypeNames = new List { "eiscapiadv", "eiscapiadvanced", "eiscapiadvancedserver", "eiscapiadvancedclient", "vceiscapiadv", "vceiscapiadvanced" }; + TypeNames = new List { "eiscapiadv", "eiscapiadvanced", "eiscapiadvancedserver", "eiscapiadvancedclient", "vceiscapiadv", "vceiscapiadvanced", "eiscapiadvudp", "eiscapiadvancedudp" }; } /// @@ -438,7 +441,7 @@ public EiscApiAdvancedFactory() /// public override EssentialsDevice BuildDevice(DeviceConfig dc) { - Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new EiscApiAdvanced Device"); + Debug.LogDebug("Attempting to create new EiscApiAdvanced Device"); var controlProperties = CommFactory.GetControlPropertiesConfig(dc); @@ -446,6 +449,13 @@ public override EssentialsDevice BuildDevice(DeviceConfig dc) switch (dc.Type.ToLower()) { + case "eiscapiadvudp": + case "eiscapiadvancedudp": + { + eisc = new EthernetIntersystemCommunications(controlProperties.IpIdInt, + controlProperties.TcpSshProperties.Address, Global.ControlSystem); + break; + } case "eiscapiadv": case "eiscapiadvanced": { From 076e5dfa9d94fdad988b10f5543c7db76dae7d03 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 23 Dec 2025 10:25:53 -0600 Subject: [PATCH 7/8] chore: update comments, debug methods, and mark some things obsolete --- .../Bridges/BridgeBase.cs | 164 +++++++++--------- 1 file changed, 85 insertions(+), 79 deletions(-) diff --git a/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs b/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs index d8f19bc8f..1b8e826ec 100644 --- a/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs +++ b/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs @@ -2,25 +2,29 @@ using System; using System.Collections.Generic; -using System.Reflection; using Crestron.SimplSharp; using Crestron.SimplSharpPro; using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.EthernetCommunication; using Newtonsoft.Json; using PepperDash.Core; +using PepperDash.Core.Logging; using PepperDash.Essentials.Core.Config; using Serilog.Events; -//using PepperDash.Essentials.Devices.Common.Cameras; namespace PepperDash.Essentials.Core.Bridges { /// /// Base class for bridge API variants /// + [Obsolete("Will be removed in v3.0.0")] public abstract class BridgeApi : EssentialsDevice { + /// + /// Constructor + /// + /// Device key protected BridgeApi(string key) : base(key) { @@ -29,23 +33,36 @@ protected BridgeApi(string key) : } /// - /// Represents a EiscApiAdvanced + /// Class to link devices and rooms to an EISC Instance /// public class EiscApiAdvanced : BridgeApi, ICommunicationMonitor { + /// + /// Gets the PropertiesConfig + /// public EiscApiPropertiesConfig PropertiesConfig { get; private set; } + /// + /// Gets the JoinMaps dictionary + /// public Dictionary JoinMaps { get; private set; } + /// + /// Gets the EISC instance + /// public BasicTriList Eisc { get; private set; } + /// + /// Constructor + /// + /// Device configuration + /// EISC instance public EiscApiAdvanced(DeviceConfig dc, BasicTriList eisc) : base(dc.Key) { JoinMaps = new Dictionary(); PropertiesConfig = dc.Properties.ToObject(); - //PropertiesConfig = JsonConvert.DeserializeObject(dc.Properties.ToString()); Eisc = eisc; @@ -60,8 +77,7 @@ public EiscApiAdvanced(DeviceConfig dc, BasicTriList eisc) : /// /// CustomActivate method - /// - /// + /// public override bool CustomActivate() { CommunicationMonitor.Start(); @@ -83,7 +99,7 @@ private void LinkDevices() if (PropertiesConfig.Devices == null) { - Debug.LogMessage(LogEventLevel.Debug, this, "No devices linked to this bridge"); + this.LogDebug("No devices linked to this bridge"); return; } @@ -104,9 +120,7 @@ private void LinkDevices() continue; } - Debug.LogMessage(LogEventLevel.Information, this, - "{0} is not compatible with this bridge type. Please use 'eiscapi' instead, or updae the device.", - device.Key); + this.LogWarning("{deviceKey} is not compatible with this bridge type. Please update the device.", device.Key); } } @@ -121,34 +135,31 @@ private void RegisterEisc() if (registerResult != eDeviceRegistrationUnRegistrationResponse.Success) { - Debug.LogMessage(LogEventLevel.Verbose, this, "Registration result: {0}", registerResult); + this.LogVerbose("Registration result: {registerResult}", registerResult); return; } - Debug.LogMessage(LogEventLevel.Debug, this, "EISC registration successful"); + this.LogDebug("EISC registration successful"); } /// - /// LinkRooms method + /// Link rooms to this EISC. Rooms MUST implement IBridgeAdvanced /// public void LinkRooms() { - Debug.LogMessage(LogEventLevel.Debug, this, "Linking Rooms..."); + this.LogDebug("Linking Rooms..."); if (PropertiesConfig.Rooms == null) { - Debug.LogMessage(LogEventLevel.Debug, this, "No rooms linked to this bridge."); + this.LogDebug("No rooms linked to this bridge."); return; } foreach (var room in PropertiesConfig.Rooms) { - var rm = DeviceManager.GetDeviceForKey(room.RoomKey) as IBridgeAdvanced; - - if (rm == null) + if (!(DeviceManager.GetDeviceForKey(room.RoomKey) is IBridgeAdvanced rm)) { - Debug.LogMessage(LogEventLevel.Debug, this, - "Room {0} does not implement IBridgeAdvanced. Skipping...", room.RoomKey); + this.LogDebug("Room {roomKey} does not implement IBridgeAdvanced. Skipping...", room.RoomKey); continue; } @@ -159,11 +170,8 @@ public void LinkRooms() /// /// Adds a join map /// - /// - /// - /// - /// AddJoinMap method - /// + /// The key of the device to add the join map for + /// The join map to add public void AddJoinMap(string deviceKey, JoinMapBaseAdvanced joinMap) { if (!JoinMaps.ContainsKey(deviceKey)) @@ -172,14 +180,13 @@ public void AddJoinMap(string deviceKey, JoinMapBaseAdvanced joinMap) } else { - Debug.LogMessage(LogEventLevel.Verbose, this, "Unable to add join map with key '{0}'. Key already exists in JoinMaps dictionary", deviceKey); + this.LogWarning("Unable to add join map with key '{deviceKey}'. Key already exists in JoinMaps dictionary", deviceKey); } } /// /// PrintJoinMaps method - /// - /// + /// public virtual void PrintJoinMaps() { CrestronConsole.ConsoleCommandResponse("Join Maps for EISC IPID: {0}\r\n", Eisc.ID.ToString("X")); @@ -190,17 +197,17 @@ public virtual void PrintJoinMaps() joinMap.Value.PrintJoinMapInfo(); } } + /// /// MarkdownForBridge method - /// - /// + /// public virtual void MarkdownForBridge(string bridgeKey) { - Debug.LogMessage(LogEventLevel.Information, this, "Writing Joinmaps to files for EISC IPID: {0}", Eisc.ID.ToString("X")); + this.LogInformation("Writing Joinmaps to files for EISC IPID: {eiscId}", Eisc.ID.ToString("X")); foreach (var joinMap in JoinMaps) { - Debug.LogMessage(LogEventLevel.Information, "Generating markdown for device '{0}':", joinMap.Key); + this.LogInformation("Generating markdown for device '{deviceKey}':", joinMap.Key); joinMap.Value.MarkdownJoinMapInfo(joinMap.Key, bridgeKey); } } @@ -208,53 +215,45 @@ public virtual void MarkdownForBridge(string bridgeKey) /// /// Prints the join map for a device by key /// - /// - /// - /// PrintJoinMapForDevice method - /// + /// The key of the device to print the join map for public void PrintJoinMapForDevice(string deviceKey) { var joinMap = JoinMaps[deviceKey]; if (joinMap == null) { - Debug.LogMessage(LogEventLevel.Information, this, "Unable to find joinMap for device with key: '{0}'", deviceKey); + this.LogInformation("Unable to find joinMap for device with key: '{deviceKey}'", deviceKey); return; } - Debug.LogMessage(LogEventLevel.Information, "Join map for device '{0}' on EISC '{1}':", deviceKey, Key); + this.LogInformation("Join map for device '{deviceKey}' on EISC '{eiscKey}':", deviceKey, Key); joinMap.PrintJoinMapInfo(); } /// - /// Prints the join map for a device by key - /// - /// - /// - /// MarkdownJoinMapForDevice method + /// Prints the join map for a device by key in Markdown format /// + /// The key of the device to print the join map for + /// The key of the bridge to use for the Markdown output public void MarkdownJoinMapForDevice(string deviceKey, string bridgeKey) { var joinMap = JoinMaps[deviceKey]; if (joinMap == null) { - Debug.LogMessage(LogEventLevel.Information, this, "Unable to find joinMap for device with key: '{0}'", deviceKey); + this.LogInformation("Unable to find joinMap for device with key: '{deviceKey}'", deviceKey); return; } - Debug.LogMessage(LogEventLevel.Information, "Join map for device '{0}' on EISC '{1}':", deviceKey, Key); + this.LogInformation("Join map for device '{deviceKey}' on EISC '{eiscKey}':", deviceKey, Key); joinMap.MarkdownJoinMapInfo(deviceKey, bridgeKey); } /// /// Used for debugging to trigger an action based on a join number and type /// - /// - /// - /// - /// - /// ExecuteJoinAction method - /// + /// The join number to execute the action for + /// The type of join (digital, analog, serial) + /// The state to pass to the action public void ExecuteJoinAction(uint join, string type, object state) { try @@ -263,68 +262,66 @@ public void ExecuteJoinAction(uint join, string type, object state) { case "digital": { - var uo = Eisc.BooleanOutput[join].UserObject as Action; - if (uo != null) + if (Eisc.BooleanOutput[join].UserObject is Action userObject) { - Debug.LogMessage(LogEventLevel.Verbose, this, "Executing Action: {0}", uo.ToString()); - uo(Convert.ToBoolean(state)); + this.LogVerbose("Executing Boolean Action"); + userObject(Convert.ToBoolean(state)); } else - Debug.LogMessage(LogEventLevel.Verbose, this, "User Action is null. Nothing to Execute"); + this.LogVerbose("User Object is null. Nothing to Execute"); break; } case "analog": { - var uo = Eisc.BooleanOutput[join].UserObject as Action; - if (uo != null) + if (Eisc.BooleanOutput[join].UserObject is Action userObject) { - Debug.LogMessage(LogEventLevel.Verbose, this, "Executing Action: {0}", uo.ToString()); - uo(Convert.ToUInt16(state)); + this.LogVerbose("Executing Analog Action"); + userObject(Convert.ToUInt16(state)); } else - Debug.LogMessage(LogEventLevel.Verbose, this, "User Action is null. Nothing to Execute"); break; + this.LogVerbose("User Object is null. Nothing to Execute"); break; } case "serial": { - var uo = Eisc.BooleanOutput[join].UserObject as Action; - if (uo != null) + if (Eisc.BooleanOutput[join].UserObject is Action userObject) { - Debug.LogMessage(LogEventLevel.Verbose, this, "Executing Action: {0}", uo.ToString()); - uo(Convert.ToString(state)); + this.LogVerbose("Executing Serial Action"); + userObject(Convert.ToString(state)); } else - Debug.LogMessage(LogEventLevel.Verbose, this, "User Action is null. Nothing to Execute"); + this.LogVerbose("User Object is null. Nothing to Execute"); break; } default: { - Debug.LogMessage(LogEventLevel.Verbose, "Unknown join type. Use digital/serial/analog"); + this.LogVerbose("Unknown join type. Use digital/serial/analog"); break; } } } catch (Exception e) { - Debug.LogMessage(LogEventLevel.Debug, this, "Error: {0}", e); + this.LogError("ExecuteJoinAction error: {message}", e.Message); + this.LogDebug(e, "Stack Trace: "); } } /// - /// Handles incoming sig changes + /// Handle incoming sig changes /// - /// - /// + /// BasicTriList device that triggered the event + /// Event arguments containing the signal information protected void Eisc_SigChange(object currentDevice, SigEventArgs args) { try { - Debug.LogMessage(LogEventLevel.Verbose, this, "EiscApiAdvanced change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue); + this.LogVerbose("EiscApiAdvanced change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue); var uo = args.Sig.UserObject; if (uo == null) return; - Debug.LogMessage(LogEventLevel.Debug, this, "Executing Action: {0}", uo.ToString()); + this.LogDebug("Executing Action: {0}", uo.ToString()); if (uo is Action) (uo as Action)(args.Sig.BoolValue); else if (uo is Action) @@ -334,7 +331,8 @@ protected void Eisc_SigChange(object currentDevice, SigEventArgs args) } catch (Exception e) { - Debug.LogMessage(LogEventLevel.Verbose, this, "Error in Eisc_SigChange handler: {0}", e); + this.LogError("Eisc_SigChange handler error: {message}", e.Message); + this.LogDebug(e, "Stack Trace: "); } } @@ -423,8 +421,19 @@ public class ApiRoomPropertiesConfig } /// - /// Represents a EiscApiAdvancedFactory + /// Factory class for EiscApiAdvanced devices /// + /// + /// Supported types: + /// eiscapiadv - Create a standard EISC client over TCP/IP + /// eiscapiadvanced - Create a standard EISC client over TCP/IP + /// eiscapiadvancedserver - Create an EISC server + /// eiscapiadvancedclient - Create an EISC client + /// vceiscapiadv - Create a VC-4 EISC client + /// vceiscapiadvanced - Create a VC-4 EISC client + /// eiscapiadvudp - Create a standard EISC client over UDP + /// eiscapiadvancedudp - Create a standard EISC client over UDP + /// public class EiscApiAdvancedFactory : EssentialsDeviceFactory { /// @@ -435,9 +444,6 @@ public EiscApiAdvancedFactory() TypeNames = new List { "eiscapiadv", "eiscapiadvanced", "eiscapiadvancedserver", "eiscapiadvancedclient", "vceiscapiadv", "vceiscapiadvanced", "eiscapiadvudp", "eiscapiadvancedudp" }; } - /// - /// BuildDevice method - /// /// public override EssentialsDevice BuildDevice(DeviceConfig dc) { @@ -478,7 +484,7 @@ public override EssentialsDevice BuildDevice(DeviceConfig dc) { if (string.IsNullOrEmpty(controlProperties.RoomId)) { - Debug.LogMessage(LogEventLevel.Information, "Unable to build VC-4 EISC Client for device {0}. Room ID is missing or empty", dc.Key); + Debug.LogInformation("Unable to build VC-4 EISC Client for device {0}. Room ID is missing or empty", dc.Key); eisc = null; break; } From 2cc37a4e40550704a5dabe0142247adbe8628373 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 23 Dec 2025 11:06:52 -0600 Subject: [PATCH 8/8] fix: ExecuteJoinAction now uses the correct Sig collections --- .../Bridges/BridgeBase.cs | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs b/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs index 1b8e826ec..070f17435 100644 --- a/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs +++ b/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs @@ -273,17 +273,18 @@ public void ExecuteJoinAction(uint join, string type, object state) } case "analog": { - if (Eisc.BooleanOutput[join].UserObject is Action userObject) + if (Eisc.UShortOutput[join].UserObject is Action userObject) { this.LogVerbose("Executing Analog Action"); userObject(Convert.ToUInt16(state)); } else - this.LogVerbose("User Object is null. Nothing to Execute"); break; + this.LogVerbose("User Object is null. Nothing to Execute"); + break; } case "serial": { - if (Eisc.BooleanOutput[join].UserObject is Action userObject) + if (Eisc.StringOutput[join].UserObject is Action userObject) { this.LogVerbose("Executing Serial Action"); userObject(Convert.ToString(state)); @@ -316,18 +317,27 @@ protected void Eisc_SigChange(object currentDevice, SigEventArgs args) { try { - this.LogVerbose("EiscApiAdvanced change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue); - var uo = args.Sig.UserObject; - - if (uo == null) return; - - this.LogDebug("Executing Action: {0}", uo.ToString()); - if (uo is Action) - (uo as Action)(args.Sig.BoolValue); - else if (uo is Action) - (uo as Action)(args.Sig.UShortValue); - else if (uo is Action) - (uo as Action)(args.Sig.StringValue); + this.LogVerbose("EiscApiAdvanced change: {type} {number}={value}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue); + var userObject = args.Sig.UserObject; + + if (userObject == null) return; + + + if (userObject is Action) + { + this.LogDebug("Executing Boolean Action"); + (userObject as Action)(args.Sig.BoolValue); + } + else if (userObject is Action) + { + this.LogDebug("Executing Analog Action"); + (userObject as Action)(args.Sig.UShortValue); + } + else if (userObject is Action) + { + this.LogDebug("Executing Serial Action"); + (userObject as Action)(args.Sig.StringValue); + } } catch (Exception e) { @@ -484,7 +494,7 @@ public override EssentialsDevice BuildDevice(DeviceConfig dc) { if (string.IsNullOrEmpty(controlProperties.RoomId)) { - Debug.LogInformation("Unable to build VC-4 EISC Client for device {0}. Room ID is missing or empty", dc.Key); + Debug.LogInformation("Unable to build VC-4 EISC Client for device {deviceKey}. Room ID is missing or empty", dc.Key); eisc = null; break; }