1111namespace 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>
0 commit comments