diff --git a/src/PepperDash.Essentials.Devices.Common/Routing/MockRoutingMidpoint.cs b/src/PepperDash.Essentials.Devices.Common/Routing/MockRoutingMidpoint.cs
index 74853223..8a6ef2f6 100644
--- a/src/PepperDash.Essentials.Devices.Common/Routing/MockRoutingMidpoint.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Routing/MockRoutingMidpoint.cs
@@ -11,14 +11,15 @@ using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common.Routing;
///
-/// A mock midpoint routing device (e.g. a matrix switcher) that implements
+/// A mock midpoint routing device (e.g. a matrix switcher) that implements
/// without any real hardware communication. Its input and output ports are configured via
/// , each with a name, signal type, and physical port
-/// (connection) type - so it can stand in for a real switching device (such as a StreamSync matrix) for
-/// development and testing of routing logic.
+/// (connection) type - so it can stand in for a real switching device (such as a DM chassis or a StreamSync
+/// matrix) for development and testing of routing logic, including named-routing-slot UI that a bare
+/// device cannot support.
///
[Description("A mock routing midpoint (e.g. matrix switcher) device for testing routing logic without real hardware")]
-public class MockRoutingMidpoint : EssentialsDevice, IRoutingMidpointWithFeedback
+public class MockRoutingMidpoint : EssentialsDevice, IHasNamedRoutingSlots
{
///
/// The configuration properties for this device.
@@ -37,6 +38,14 @@ public class MockRoutingMidpoint : EssentialsDevice, IRoutingMidpointWithFeedbac
///
public event RouteChangedEventHandler RouteChanged;
+ private readonly Dictionary _outputSlotsByKey = new Dictionary();
+
+ ///
+ public IReadOnlyDictionary InputSlots { get; private set; }
+
+ ///
+ public IReadOnlyDictionary OutputSlots { get; private set; }
+
///
/// Initializes a new instance of the class from a .
///
@@ -56,14 +65,20 @@ public class MockRoutingMidpoint : EssentialsDevice, IRoutingMidpointWithFeedbac
}
///
- /// Builds the input and output ports from . Each port's Key and Selector
- /// are both set to the configured port name, so selectors passed to and
- /// can simply be looked up by matching against the port's Selector.
+ /// Builds the input and output ports (and their slot info) from
+ /// . Each port's Key and Selector are both set to the configured port
+ /// name, so selectors passed to and can simply be
+ /// looked up by matching against the port's Selector. Slot number is the 1-based position of the port
+ /// within its input/output list.
///
private void BuildPorts()
{
try
{
+ var inputSlots = new Dictionary();
+ var outputSlots = new Dictionary();
+ var slotNumber = 0;
+
foreach (var portConfig in PropertiesConfig.InputPorts)
{
if (string.IsNullOrEmpty(portConfig.Name))
@@ -74,8 +89,13 @@ public class MockRoutingMidpoint : EssentialsDevice, IRoutingMidpointWithFeedbac
var port = new RoutingInputPort(portConfig.Name, portConfig.SignalType, portConfig.PortType, portConfig.Name, this);
InputPorts.Add(port);
+
+ slotNumber++;
+ inputSlots[portConfig.Name] = new MockRoutingSlotInfo(
+ portConfig.Name, portConfig.Label ?? portConfig.Name, slotNumber, portConfig.SignalType);
}
+ slotNumber = 0;
foreach (var portConfig in PropertiesConfig.OutputPorts)
{
if (string.IsNullOrEmpty(portConfig.Name))
@@ -86,8 +106,17 @@ public class MockRoutingMidpoint : EssentialsDevice, IRoutingMidpointWithFeedbac
var port = new RoutingOutputPort(portConfig.Name, portConfig.SignalType, portConfig.PortType, portConfig.Name, this);
OutputPorts.Add(port);
+
+ slotNumber++;
+ var outputSlot = new MockRoutingOutputSlotInfo(
+ portConfig.Name, portConfig.Label ?? portConfig.Name, slotNumber, portConfig.SignalType);
+ _outputSlotsByKey[portConfig.Name] = outputSlot;
+ outputSlots[portConfig.Name] = outputSlot;
}
+ InputSlots = inputSlots;
+ OutputSlots = outputSlots;
+
this.LogInformation("Built {inputCount} input port(s) and {outputCount} output port(s) for mock midpoint {key}",
InputPorts.Count, OutputPorts.Count, Key);
}
@@ -121,6 +150,11 @@ public class MockRoutingMidpoint : EssentialsDevice, IRoutingMidpointWithFeedbac
{
this.LogInformation("Clearing route to output {output} on {key}", outputPort.Key, Key);
+ if (_outputSlotsByKey.TryGetValue(outputPort.Key, out var clearedSlot))
+ {
+ clearedSlot.ClearRoute(signalType);
+ }
+
var clearedDescriptor = new RouteSwitchDescriptor(outputPort, null);
RouteChanged?.Invoke(this, clearedDescriptor);
return;
@@ -137,6 +171,11 @@ public class MockRoutingMidpoint : EssentialsDevice, IRoutingMidpointWithFeedbac
var descriptor = new RouteSwitchDescriptor(outputPort, inputPort);
CurrentRoutes.Add(descriptor);
+ if (_outputSlotsByKey.TryGetValue(outputPort.Key, out var routedSlot))
+ {
+ routedSlot.SetRoute(signalType, inputPort.Key);
+ }
+
this.LogInformation("Executed switch: {input} -> {output} ({signalType}) on {key}",
inputPort.Key, outputPort.Key, signalType, Key);
@@ -155,6 +194,79 @@ public class MockRoutingMidpoint : EssentialsDevice, IRoutingMidpointWithFeedbac
}
}
+///
+/// Named routing slot info for a input or output port.
+///
+class MockRoutingSlotInfo : IRoutingSlotInfo
+{
+ ///
+ public string Key { get; }
+
+ ///
+ public string Name { get; }
+
+ ///
+ public int SlotNumber { get; }
+
+ ///
+ public eRoutingSignalType SupportedSignalTypes { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MockRoutingSlotInfo(string key, string name, int slotNumber, eRoutingSignalType supportedSignalTypes)
+ {
+ Key = key;
+ Name = name;
+ SlotNumber = slotNumber;
+ SupportedSignalTypes = supportedSignalTypes;
+ }
+}
+
+///
+/// Named output routing slot info for a output port, tracking the
+/// currently routed input key per signal type since the mock's flat
+/// list does not carry signal type.
+///
+class MockRoutingOutputSlotInfo : MockRoutingSlotInfo, IRoutingOutputSlotInfo
+{
+ private readonly Dictionary _currentRouteInputKeys = new Dictionary();
+
+ ///
+ public IReadOnlyDictionary CurrentRouteInputKeys => _currentRouteInputKeys;
+
+ ///
+ public event EventHandler OutputSlotChanged;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MockRoutingOutputSlotInfo(string key, string name, int slotNumber, eRoutingSignalType supportedSignalTypes)
+ : base(key, name, slotNumber, supportedSignalTypes)
+ {
+ }
+
+ ///
+ /// Records the input key routed to this output for the given signal type and raises .
+ ///
+ public void SetRoute(eRoutingSignalType signalType, string inputKey)
+ {
+ _currentRouteInputKeys[signalType] = inputKey;
+ OutputSlotChanged?.Invoke(this, EventArgs.Empty);
+ }
+
+ ///
+ /// Clears the routed input key for the given signal type and raises if it changed.
+ ///
+ public void ClearRoute(eRoutingSignalType signalType)
+ {
+ if (_currentRouteInputKeys.Remove(signalType))
+ {
+ OutputSlotChanged?.Invoke(this, EventArgs.Empty);
+ }
+ }
+}
+
///
/// Factory for building devices.
///
diff --git a/src/PepperDash.Essentials.Devices.Common/Routing/MockRoutingMidpointPropertiesConfig.cs b/src/PepperDash.Essentials.Devices.Common/Routing/MockRoutingMidpointPropertiesConfig.cs
index 7c7d4bc3..b24e31aa 100644
--- a/src/PepperDash.Essentials.Devices.Common/Routing/MockRoutingMidpointPropertiesConfig.cs
+++ b/src/PepperDash.Essentials.Devices.Common/Routing/MockRoutingMidpointPropertiesConfig.cs
@@ -50,4 +50,12 @@ public class MockRoutingMidpointPortConfig
[JsonProperty("portType")]
[JsonConverter(typeof(StringEnumConverter))]
public eRoutingPortConnectionType PortType { get; set; } = eRoutingPortConnectionType.Hdmi;
+
+ ///
+ /// Optional friendly display name for this port (e.g. "Rm A Codec"), surfaced via
+ /// for named-routing-slot UI. Defaults to if
+ /// not specified.
+ ///
+ [JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
+ public string Label { get; set; }
}