merge: bring in feat/named-routing-slots

This commit is contained in:
Erik Meyer 2026-08-26 15:31:43 -04:00
commit 6c20d46834
3 changed files with 142 additions and 0 deletions

View file

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using PepperDash.Core;
namespace PepperDash.Essentials.Core;
/// <summary>
/// Describes a single named routing slot (input or output) on a device implementing
/// <see cref="IHasNamedRoutingSlots"/>.
/// </summary>
public interface IRoutingSlotInfo : IKeyName
{
/// <summary>Matrix slot number.</summary>
int SlotNumber { get; }
/// <summary>Signal types this slot can carry.</summary>
eRoutingSignalType SupportedSignalTypes { get; }
}
/// <summary>
/// Describes a named output slot, adding per-signal-type current-route feedback (keyed by the
/// routed input slot's key) that <see cref="IRoutingMidpointWithFeedback.CurrentRoutes"/> does not
/// carry.
/// </summary>
public interface IRoutingOutputSlotInfo : IRoutingSlotInfo
{
/// <summary>The key of the input slot currently routed to this output, per signal type.</summary>
IReadOnlyDictionary<eRoutingSignalType, string> CurrentRouteInputKeys { get; }
/// <summary>Raised when the routed input on this output changes.</summary>
event EventHandler OutputSlotChanged;
}
/// <summary>
/// Optional extension to <see cref="IRoutingMidpointWithFeedback"/> for matrix-style routing
/// devices that track named input/output slots (with per-signal-type routing feedback) internally,
/// e.g. plugin-local slot abstractions that replaced the removed core <c>IRoutingInputSlot</c>/
/// <c>IRoutingOutputSlot</c> during the v3 routing refactor. Devices implementing this get a richer
/// mobile-control matrix routing message (names + per-signal-type feedback) instead of the bare
/// key-only ports from <see cref="IRoutingMidpointWithFeedback"/> alone.
/// </summary>
public interface IHasNamedRoutingSlots : IRoutingMidpointWithFeedback
{
/// <summary>Named input slots, keyed by slot key.</summary>
IReadOnlyDictionary<string, IRoutingSlotInfo> InputSlots { get; }
/// <summary>Named output slots, keyed by slot key.</summary>
IReadOnlyDictionary<string, IRoutingOutputSlotInfo> OutputSlots { get; }
}

View file

@ -0,0 +1,85 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Messenger for devices implementing <see cref="IHasNamedRoutingSlots"/> — sends named
/// input/output slots with per-signal-type current-route feedback, which the bare
/// <see cref="IRoutingMidpointWithFeedbackMessenger"/> (registered for every
/// <see cref="IRoutingMidpointWithFeedback"/> device, including this one) cannot provide.
/// </summary>
public class IHasNamedRoutingSlotsMessenger : MessengerBase
{
private readonly IHasNamedRoutingSlots _device;
public IHasNamedRoutingSlotsMessenger(string key, string messagePath, IHasNamedRoutingSlots device)
: base(key, messagePath, device as IKeyName)
{
_device = device;
}
protected override void RegisterActions()
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) => SendFullStatus(id));
foreach (var output in _device.OutputSlots.Values)
{
output.OutputSlotChanged += (sender, args) => SendFullStatus();
}
}
private static RoutingSlotMessage BuildSlotMessage(IRoutingSlotInfo slot) => new RoutingSlotMessage
{
Key = slot.Key,
Name = slot.Name,
SlotNumber = slot.SlotNumber,
SupportedSignalTypes = slot.SupportedSignalTypes.ToString()
};
private void SendFullStatus(string id = null)
{
var inputSlots = _device.InputSlots.ToDictionary(kvp => kvp.Key, kvp => BuildSlotMessage(kvp.Value));
var outputSlots = _device.OutputSlots.ToDictionary(kvp => kvp.Key, kvp =>
{
var message = BuildSlotMessage(kvp.Value);
message.CurrentRouteInputKeys = kvp.Value.CurrentRouteInputKeys
.ToDictionary(r => r.Key.ToString(), r => r.Value);
return message;
});
var content = JToken.FromObject(new
{
inputSlots,
outputSlots
});
PostStatusMessage(content, MessagePath, id);
}
}
public class RoutingSlotMessage
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("slotNumber")]
public int SlotNumber { get; set; }
[JsonProperty("supportedSignalTypes")]
public string SupportedSignalTypes { get; set; }
[JsonProperty("currentRouteInputKeys", NullValueHandling = NullValueHandling.Ignore)]
public Dictionary<string, string> CurrentRouteInputKeys { get; set; }
}
}

View file

@ -374,6 +374,14 @@ namespace PepperDash.Essentials
(d, mp, _) => new IRoutingMidpointWithFeedbackMessenger( (d, mp, _) => new IRoutingMidpointWithFeedbackMessenger(
$"{d.Key}-matrixRouting", mp, (IRoutingMidpointWithFeedback)d) $"{d.Key}-matrixRouting", mp, (IRoutingMidpointWithFeedback)d)
), ),
// Devices that also expose named slots (plugin-local IDmInputSlot/INvxInputSlot-
// style abstractions) get a second, richer message with names + per-signal-type
// route feedback the bare messenger above cannot provide.
new MessengerFactoryEntry(
typeof(IHasNamedRoutingSlots),
(d, mp, _) => new IHasNamedRoutingSlotsMessenger(
$"{d.Key}-namedRoutingSlots", mp, (IHasNamedRoutingSlots)d)
),
// ── Environmental sensors ───────────────────────────────────────────────── // ── Environmental sensors ─────────────────────────────────────────────────
// Preserving original key format (no controller key suffix) // Preserving original key format (no controller key suffix)