mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-08-31 10:58:28 +00:00
feat: update RoutingFeedbackManager to map downstream sink input ports and optimize performance in route updates
This commit is contained in:
parent
fb4ba8ccfa
commit
9f18275f81
1 changed files with 53 additions and 28 deletions
|
|
@ -14,9 +14,12 @@ namespace PepperDash.Essentials.Core.Routing
|
||||||
public class RoutingFeedbackManager : EssentialsDevice
|
public class RoutingFeedbackManager : EssentialsDevice
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maps midpoint device keys to the set of sink device keys that are downstream
|
/// Maps midpoint device keys to the set of downstream sink input ports, derived from the
|
||||||
|
/// static tie-line topology. Because it is built from topology rather than a sink's currently
|
||||||
|
/// reported input, sinks that never report an input (i.e. <see cref="IRoutingSinkWithFeedback.CurrentInputPort"/>
|
||||||
|
/// stays null, such as codecs fed from an external matrix) are still mapped and updated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Dictionary<string, HashSet<string>> midpointToSinksMap;
|
private Dictionary<string, HashSet<RoutingInputPort>> midpointToSinkInputsMap;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Debounce timers for each sink device to prevent rapid successive updates
|
/// Debounce timers for each sink device to prevent rapid successive updates
|
||||||
|
|
@ -42,30 +45,39 @@ namespace PepperDash.Essentials.Core.Routing
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Builds a map of which sink devices are downstream of each midpoint device
|
/// Builds a map of which sink input ports are downstream of each midpoint device
|
||||||
/// for performance optimization in HandleMidpointUpdate
|
/// for performance optimization in HandleMidpointUpdate.
|
||||||
|
/// The map is derived from the static tie-line topology (every sink input port is traced
|
||||||
|
/// upstream), so it does not depend on a sink having already reported its current input.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void BuildMidpointSinkMap()
|
private void BuildMidpointSinkMap()
|
||||||
{
|
{
|
||||||
midpointToSinksMap = new Dictionary<string, HashSet<string>>();
|
midpointToSinkInputsMap = new Dictionary<string, HashSet<RoutingInputPort>>();
|
||||||
|
|
||||||
var sinks = DeviceManager.AllDevices.OfType<IRoutingSinkWithFeedback>();
|
var sinks = DeviceManager.AllDevices.OfType<IRoutingSinkWithFeedback>();
|
||||||
var midpoints = DeviceManager.AllDevices.OfType<IRoutingMidpointWithFeedback>();
|
|
||||||
|
|
||||||
foreach (var sink in sinks)
|
foreach (var sink in sinks)
|
||||||
{
|
{
|
||||||
if (sink.CurrentInputPort == null)
|
// Trace from every input port on the sink (static topology) rather than only the
|
||||||
|
// currently-selected input. Sinks that never report an input still get mapped.
|
||||||
|
var inputPorts = (sink as IRoutingInputs)?.InputPorts;
|
||||||
|
if (inputPorts == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Find all upstream midpoints for this sink
|
foreach (var inputPort in inputPorts)
|
||||||
var upstreamMidpoints = GetUpstreamMidpoints(sink);
|
|
||||||
|
|
||||||
foreach (var midpointKey in upstreamMidpoints)
|
|
||||||
{
|
{
|
||||||
if (!midpointToSinksMap.ContainsKey(midpointKey))
|
var upstreamMidpoints = GetUpstreamMidpointsForInput(inputPort);
|
||||||
midpointToSinksMap[midpointKey] = new HashSet<string>();
|
|
||||||
|
|
||||||
midpointToSinksMap[midpointKey].Add(sink.Key);
|
foreach (var midpointKey in upstreamMidpoints)
|
||||||
|
{
|
||||||
|
if (!midpointToSinkInputsMap.TryGetValue(midpointKey, out var inputs))
|
||||||
|
{
|
||||||
|
inputs = new HashSet<RoutingInputPort>();
|
||||||
|
midpointToSinkInputsMap[midpointKey] = inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs.Add(inputPort);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,24 +85,25 @@ namespace PepperDash.Essentials.Core.Routing
|
||||||
Serilog.Events.LogEventLevel.Information,
|
Serilog.Events.LogEventLevel.Information,
|
||||||
"Built midpoint-to-sink map with {count} midpoints",
|
"Built midpoint-to-sink map with {count} midpoints",
|
||||||
this,
|
this,
|
||||||
midpointToSinksMap.Count
|
midpointToSinkInputsMap.Count
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all upstream midpoint device keys for a given sink
|
/// Gets all upstream midpoint device keys reachable from a specific sink input port
|
||||||
|
/// by walking the static tie-line topology.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private HashSet<string> GetUpstreamMidpoints(IRoutingSinkWithFeedback sink)
|
private HashSet<string> GetUpstreamMidpointsForInput(RoutingInputPort inputPort)
|
||||||
{
|
{
|
||||||
var result = new HashSet<string>();
|
var result = new HashSet<string>();
|
||||||
var visited = new HashSet<string>();
|
var visited = new HashSet<string>();
|
||||||
|
|
||||||
if (sink.CurrentInputPort == null)
|
if (inputPort == null)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
var tieLine = TieLineCollection.Default.FirstOrDefault(tl =>
|
var tieLine = TieLineCollection.Default.FirstOrDefault(tl =>
|
||||||
tl.DestinationPort.Key == sink.CurrentInputPort.Key &&
|
tl.DestinationPort.Key == inputPort.Key &&
|
||||||
tl.DestinationPort.ParentDevice.Key == sink.CurrentInputPort.ParentDevice.Key);
|
tl.DestinationPort.ParentDevice.Key == inputPort.ParentDevice.Key);
|
||||||
|
|
||||||
if (tieLine == null)
|
if (tieLine == null)
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -171,22 +184,34 @@ namespace PepperDash.Essentials.Core.Routing
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Only update affected sinks (performance optimization)
|
// Only update affected sinks (performance optimization)
|
||||||
if (midpointToSinksMap != null && midpointToSinksMap.TryGetValue(midpoint.Key, out var affectedSinkKeys))
|
if (midpointToSinkInputsMap != null && midpointToSinkInputsMap.TryGetValue(midpoint.Key, out var affectedInputPorts))
|
||||||
{
|
{
|
||||||
Debug.LogMessage(
|
Debug.LogMessage(
|
||||||
Serilog.Events.LogEventLevel.Debug,
|
Serilog.Events.LogEventLevel.Debug,
|
||||||
"Midpoint {midpoint} changed, updating {count} downstream sinks",
|
"Midpoint {midpoint} changed, updating {count} downstream sink inputs",
|
||||||
this,
|
this,
|
||||||
midpoint.Key,
|
midpoint.Key,
|
||||||
affectedSinkKeys.Count
|
affectedInputPorts.Count
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach (var sinkKey in affectedSinkKeys)
|
// Avoid redundant updates when a feedback-reporting sink has several mapped inputs.
|
||||||
|
var updatedSinkKeys = new HashSet<string>();
|
||||||
|
|
||||||
|
foreach (var inputPort in affectedInputPorts)
|
||||||
{
|
{
|
||||||
if (DeviceManager.GetDeviceForKey(sinkKey) is IRoutingSinkWithFeedback sink)
|
if (!(inputPort.ParentDevice is IRoutingSinkWithFeedback sink))
|
||||||
{
|
continue;
|
||||||
UpdateDestination(sink, sink.CurrentInputPort);
|
|
||||||
}
|
// Sinks that report their input drive updates off the currently-selected input.
|
||||||
|
// Sinks that never report an input (CurrentInputPort == null, e.g. a codec fed
|
||||||
|
// from an external matrix) fall back to the static topology input port so that
|
||||||
|
// matrix route changes still propagate.
|
||||||
|
var portToUse = sink.CurrentInputPort ?? inputPort;
|
||||||
|
|
||||||
|
if (sink.CurrentInputPort != null && !updatedSinkKeys.Add(sink.Key))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
UpdateDestination(sink, portToUse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue