Skip to content

Commit fec59d2

Browse files
committed
feat(mock-routing): add IHasNamedRoutingSlots support to MockRoutingMidpoint
1 parent 527262d commit fec59d2

2 files changed

Lines changed: 127 additions & 7 deletions

File tree

src/PepperDash.Essentials.Devices.Common/Routing/MockRoutingMidpoint.cs

Lines changed: 119 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@
1111
namespace PepperDash.Essentials.Devices.Common.Routing;
1212

1313
/// <summary>
14-
/// A mock midpoint routing device (e.g. a matrix switcher) that implements <see cref="IRoutingMidpointWithFeedback"/>
14+
/// A mock midpoint routing device (e.g. a matrix switcher) that implements <see cref="IHasNamedRoutingSlots"/>
1515
/// without any real hardware communication. Its input and output ports are configured via
1616
/// <see cref="MockRoutingMidpointPropertiesConfig"/>, each with a name, signal type, and physical port
17-
/// (connection) type - so it can stand in for a real switching device (such as a StreamSync matrix) for
18-
/// development and testing of routing logic.
17+
/// (connection) type - so it can stand in for a real switching device (such as a DM chassis or a StreamSync
18+
/// matrix) for development and testing of routing logic, including named-routing-slot UI that a bare
19+
/// <see cref="IRoutingMidpointWithFeedback"/> device cannot support.
1920
/// </summary>
2021
[Description("A mock routing midpoint (e.g. matrix switcher) device for testing routing logic without real hardware")]
21-
public class MockRoutingMidpoint : EssentialsDevice, IRoutingMidpointWithFeedback
22+
public class MockRoutingMidpoint : EssentialsDevice, IHasNamedRoutingSlots
2223
{
2324
/// <summary>
2425
/// The configuration properties for this device.
@@ -37,6 +38,14 @@ public class MockRoutingMidpoint : EssentialsDevice, IRoutingMidpointWithFeedbac
3738
/// <inheritdoc />
3839
public event RouteChangedEventHandler RouteChanged;
3940

41+
private readonly Dictionary<string, MockRoutingOutputSlotInfo> _outputSlotsByKey = new Dictionary<string, MockRoutingOutputSlotInfo>();
42+
43+
/// <inheritdoc />
44+
public IReadOnlyDictionary<string, IRoutingSlotInfo> InputSlots { get; private set; }
45+
46+
/// <inheritdoc />
47+
public IReadOnlyDictionary<string, IRoutingOutputSlotInfo> OutputSlots { get; private set; }
48+
4049
/// <summary>
4150
/// Initializes a new instance of the <see cref="MockRoutingMidpoint"/> class from a <see cref="DeviceConfig"/>.
4251
/// </summary>
@@ -56,14 +65,20 @@ public MockRoutingMidpoint(DeviceConfig config)
5665
}
5766

5867
/// <summary>
59-
/// Builds the input and output ports from <see cref="PropertiesConfig"/>. Each port's Key and Selector
60-
/// are both set to the configured port name, so selectors passed to <see cref="ExecuteSwitch"/> and
61-
/// <see cref="ClearRoute"/> can simply be looked up by matching against the port's Selector.
68+
/// Builds the input and output ports (and their <see cref="IHasNamedRoutingSlots"/> slot info) from
69+
/// <see cref="PropertiesConfig"/>. Each port's Key and Selector are both set to the configured port
70+
/// name, so selectors passed to <see cref="ExecuteSwitch"/> and <see cref="ClearRoute"/> can simply be
71+
/// looked up by matching against the port's Selector. Slot number is the 1-based position of the port
72+
/// within its input/output list.
6273
/// </summary>
6374
private void BuildPorts()
6475
{
6576
try
6677
{
78+
var inputSlots = new Dictionary<string, IRoutingSlotInfo>();
79+
var outputSlots = new Dictionary<string, IRoutingOutputSlotInfo>();
80+
var slotNumber = 0;
81+
6782
foreach (var portConfig in PropertiesConfig.InputPorts)
6883
{
6984
if (string.IsNullOrEmpty(portConfig.Name))
@@ -74,8 +89,13 @@ private void BuildPorts()
7489

7590
var port = new RoutingInputPort(portConfig.Name, portConfig.SignalType, portConfig.PortType, portConfig.Name, this);
7691
InputPorts.Add(port);
92+
93+
slotNumber++;
94+
inputSlots[portConfig.Name] = new MockRoutingSlotInfo(
95+
portConfig.Name, portConfig.Label ?? portConfig.Name, slotNumber, portConfig.SignalType);
7796
}
7897

98+
slotNumber = 0;
7999
foreach (var portConfig in PropertiesConfig.OutputPorts)
80100
{
81101
if (string.IsNullOrEmpty(portConfig.Name))
@@ -86,8 +106,17 @@ private void BuildPorts()
86106

87107
var port = new RoutingOutputPort(portConfig.Name, portConfig.SignalType, portConfig.PortType, portConfig.Name, this);
88108
OutputPorts.Add(port);
109+
110+
slotNumber++;
111+
var outputSlot = new MockRoutingOutputSlotInfo(
112+
portConfig.Name, portConfig.Label ?? portConfig.Name, slotNumber, portConfig.SignalType);
113+
_outputSlotsByKey[portConfig.Name] = outputSlot;
114+
outputSlots[portConfig.Name] = outputSlot;
89115
}
90116

117+
InputSlots = inputSlots;
118+
OutputSlots = outputSlots;
119+
91120
this.LogInformation("Built {inputCount} input port(s) and {outputCount} output port(s) for mock midpoint {key}",
92121
InputPorts.Count, OutputPorts.Count, Key);
93122
}
@@ -121,6 +150,11 @@ public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingS
121150
{
122151
this.LogInformation("Clearing route to output {output} on {key}", outputPort.Key, Key);
123152

153+
if (_outputSlotsByKey.TryGetValue(outputPort.Key, out var clearedSlot))
154+
{
155+
clearedSlot.ClearRoute(signalType);
156+
}
157+
124158
var clearedDescriptor = new RouteSwitchDescriptor(outputPort, null);
125159
RouteChanged?.Invoke(this, clearedDescriptor);
126160
return;
@@ -137,6 +171,11 @@ public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingS
137171
var descriptor = new RouteSwitchDescriptor(outputPort, inputPort);
138172
CurrentRoutes.Add(descriptor);
139173

174+
if (_outputSlotsByKey.TryGetValue(outputPort.Key, out var routedSlot))
175+
{
176+
routedSlot.SetRoute(signalType, inputPort.Key);
177+
}
178+
140179
this.LogInformation("Executed switch: {input} -> {output} ({signalType}) on {key}",
141180
inputPort.Key, outputPort.Key, signalType, Key);
142181

@@ -155,6 +194,79 @@ public void ClearRoute(object outputSelector, eRoutingSignalType signalType)
155194
}
156195
}
157196

197+
/// <summary>
198+
/// Named routing slot info for a <see cref="MockRoutingMidpoint"/> input or output port.
199+
/// </summary>
200+
class MockRoutingSlotInfo : IRoutingSlotInfo
201+
{
202+
/// <inheritdoc />
203+
public string Key { get; }
204+
205+
/// <inheritdoc />
206+
public string Name { get; }
207+
208+
/// <inheritdoc />
209+
public int SlotNumber { get; }
210+
211+
/// <inheritdoc />
212+
public eRoutingSignalType SupportedSignalTypes { get; }
213+
214+
/// <summary>
215+
/// Initializes a new instance of the <see cref="MockRoutingSlotInfo"/> class.
216+
/// </summary>
217+
public MockRoutingSlotInfo(string key, string name, int slotNumber, eRoutingSignalType supportedSignalTypes)
218+
{
219+
Key = key;
220+
Name = name;
221+
SlotNumber = slotNumber;
222+
SupportedSignalTypes = supportedSignalTypes;
223+
}
224+
}
225+
226+
/// <summary>
227+
/// Named output routing slot info for a <see cref="MockRoutingMidpoint"/> output port, tracking the
228+
/// currently routed input key per signal type since the mock's flat <see cref="MockRoutingMidpoint.CurrentRoutes"/>
229+
/// list does not carry signal type.
230+
/// </summary>
231+
class MockRoutingOutputSlotInfo : MockRoutingSlotInfo, IRoutingOutputSlotInfo
232+
{
233+
private readonly Dictionary<eRoutingSignalType, string> _currentRouteInputKeys = new Dictionary<eRoutingSignalType, string>();
234+
235+
/// <inheritdoc />
236+
public IReadOnlyDictionary<eRoutingSignalType, string> CurrentRouteInputKeys => _currentRouteInputKeys;
237+
238+
/// <inheritdoc />
239+
public event EventHandler OutputSlotChanged;
240+
241+
/// <summary>
242+
/// Initializes a new instance of the <see cref="MockRoutingOutputSlotInfo"/> class.
243+
/// </summary>
244+
public MockRoutingOutputSlotInfo(string key, string name, int slotNumber, eRoutingSignalType supportedSignalTypes)
245+
: base(key, name, slotNumber, supportedSignalTypes)
246+
{
247+
}
248+
249+
/// <summary>
250+
/// Records the input key routed to this output for the given signal type and raises <see cref="OutputSlotChanged"/>.
251+
/// </summary>
252+
public void SetRoute(eRoutingSignalType signalType, string inputKey)
253+
{
254+
_currentRouteInputKeys[signalType] = inputKey;
255+
OutputSlotChanged?.Invoke(this, EventArgs.Empty);
256+
}
257+
258+
/// <summary>
259+
/// Clears the routed input key for the given signal type and raises <see cref="OutputSlotChanged"/> if it changed.
260+
/// </summary>
261+
public void ClearRoute(eRoutingSignalType signalType)
262+
{
263+
if (_currentRouteInputKeys.Remove(signalType))
264+
{
265+
OutputSlotChanged?.Invoke(this, EventArgs.Empty);
266+
}
267+
}
268+
}
269+
158270
/// <summary>
159271
/// Factory for building <see cref="MockRoutingMidpoint"/> devices.
160272
/// </summary>

src/PepperDash.Essentials.Devices.Common/Routing/MockRoutingMidpointPropertiesConfig.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,12 @@ public class MockRoutingMidpointPortConfig
5050
[JsonProperty("portType")]
5151
[JsonConverter(typeof(StringEnumConverter))]
5252
public eRoutingPortConnectionType PortType { get; set; } = eRoutingPortConnectionType.Hdmi;
53+
54+
/// <summary>
55+
/// Optional friendly display name for this port (e.g. "Rm A Codec"), surfaced via
56+
/// <see cref="IHasNamedRoutingSlots"/> for named-routing-slot UI. Defaults to <see cref="Name"/> if
57+
/// not specified.
58+
/// </summary>
59+
[JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
60+
public string Label { get; set; }
5361
}

0 commit comments

Comments
 (0)