feat: add CWS endpoint to get routing devices & tielines together

This commit is contained in:
Andrew Welker
2026-01-14 10:22:35 -06:00
parent fb8216beed
commit 9c9a643b6a
2 changed files with 290 additions and 106 deletions

View File

@@ -10,71 +10,71 @@ using Serilog.Events;
namespace PepperDash.Essentials.Core.Web namespace PepperDash.Essentials.Core.Web
{ {
/// <summary> /// <summary>
/// Represents a EssentialsWebApi /// Represents a EssentialsWebApi
/// </summary> /// </summary>
public class EssentialsWebApi : EssentialsDevice public class EssentialsWebApi : EssentialsDevice
{ {
private readonly WebApiServer _server; private readonly WebApiServer _server;
///<example> ///<example>
/// http(s)://{ipaddress}/cws/{basePath} /// http(s)://{ipaddress}/cws/{basePath}
/// http(s)://{ipaddress}/VirtualControl/Rooms/{roomId}/cws/{basePath} /// http(s)://{ipaddress}/VirtualControl/Rooms/{roomId}/cws/{basePath}
/// </example> /// </example>
private readonly string _defaultBasePath = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance private readonly string _defaultBasePath = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance
? string.Format("/app{0:00}/api", InitialParametersClass.ApplicationNumber) ? string.Format("/app{0:00}/api", InitialParametersClass.ApplicationNumber)
: "/api"; : "/api";
private const int DebugTrace = 0; private const int DebugTrace = 0;
private const int DebugInfo = 1; private const int DebugInfo = 1;
private const int DebugVerbose = 2; private const int DebugVerbose = 2;
/// <summary> /// <summary>
/// Gets or sets the BasePath /// Gets or sets the BasePath
/// </summary> /// </summary>
public string BasePath { get; private set; } public string BasePath { get; private set; }
/// <summary> /// <summary>
/// Tracks if CWS is registered /// Tracks if CWS is registered
/// </summary> /// </summary>
public bool IsRegistered public bool IsRegistered
{ {
get { return _server.IsRegistered; } get { return _server.IsRegistered; }
} }
/// <summary> /// <summary>
/// Constructor /// Constructor
/// </summary> /// </summary>
/// <param name="key"></param> /// <param name="key"></param>
/// <param name="name"></param> /// <param name="name"></param>
public EssentialsWebApi(string key, string name) public EssentialsWebApi(string key, string name)
: this(key, name, null) : this(key, name, null)
{ {
} }
/// <summary> /// <summary>
/// Constructor /// Constructor
/// </summary> /// </summary>
/// <param name="key"></param> /// <param name="key"></param>
/// <param name="name"></param> /// <param name="name"></param>
/// <param name="config"></param> /// <param name="config"></param>
public EssentialsWebApi(string key, string name, EssentialsWebApiPropertiesConfig config) public EssentialsWebApi(string key, string name, EssentialsWebApiPropertiesConfig config)
: base(key, name) : base(key, name)
{ {
Key = key; Key = key;
if (config == null) if (config == null)
BasePath = _defaultBasePath; BasePath = _defaultBasePath;
else else
BasePath = string.IsNullOrEmpty(config.BasePath) ? _defaultBasePath : config.BasePath; BasePath = string.IsNullOrEmpty(config.BasePath) ? _defaultBasePath : config.BasePath;
_server = new WebApiServer(Key, Name, BasePath); _server = new WebApiServer(Key, Name, BasePath);
SetupRoutes(); SetupRoutes();
} }
private void SetupRoutes() private void SetupRoutes()
{ {
var routes = new List<HttpCwsRoute> var routes = new List<HttpCwsRoute>
{ {
new HttpCwsRoute("versions") new HttpCwsRoute("versions")
@@ -177,6 +177,11 @@ namespace PepperDash.Essentials.Core.Web
Name = "Get Routing Ports for a device", Name = "Get Routing Ports for a device",
RouteHandler = new GetRoutingPortsHandler() RouteHandler = new GetRoutingPortsHandler()
}, },
new HttpCwsRoute("routingDevicesAndTieLines")
{
Name = "Get Routing Devices and TieLines",
RouteHandler = new GetRoutingDevicesAndTieLinesHandler()
},
}; };
AddRoute(routes); AddRoute(routes);
@@ -211,78 +216,79 @@ namespace PepperDash.Essentials.Core.Web
/// </summary> /// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override void Initialize() public override void Initialize()
{ {
AddRoute(new HttpCwsRoute("apiPaths") { AddRoute(new HttpCwsRoute("apiPaths")
{
Name = "GetPaths", Name = "GetPaths",
RouteHandler = new GetRoutesHandler(_server.GetRouteCollection(), BasePath) RouteHandler = new GetRoutesHandler(_server.GetRouteCollection(), BasePath)
}); });
// If running on an appliance // If running on an appliance
if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance) if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)
{ {
/* /*
WEBSERVER [ON | OFF | TIMEOUT <VALUE IN SECONDS> | MAXSESSIONSPERUSER <Number of sessions>] WEBSERVER [ON | OFF | TIMEOUT <VALUE IN SECONDS> | MAXSESSIONSPERUSER <Number of sessions>]
*/ */
var response = string.Empty; var response = string.Empty;
CrestronConsole.SendControlSystemCommand("webserver", ref response); CrestronConsole.SendControlSystemCommand("webserver", ref response);
if (response.Contains("OFF")) return; if (response.Contains("OFF")) return;
var is4Series = eCrestronSeries.Series4 == (Global.ProcessorSeries & eCrestronSeries.Series4); var is4Series = eCrestronSeries.Series4 == (Global.ProcessorSeries & eCrestronSeries.Series4);
Debug.LogMessage(LogEventLevel.Verbose, "Starting Essentials Web API on {0} Appliance", is4Series ? "4-series" : "3-series"); Debug.LogMessage(LogEventLevel.Verbose, "Starting Essentials Web API on {0} Appliance", is4Series ? "4-series" : "3-series");
_server.Start(); _server.Start();
GetPaths(); GetPaths();
return; return;
} }
// Automatically start CWS when running on a server (Linux OS, Virtual Control) // Automatically start CWS when running on a server (Linux OS, Virtual Control)
Debug.LogMessage(LogEventLevel.Verbose, "Starting Essentials Web API on Virtual Control Server"); Debug.LogMessage(LogEventLevel.Verbose, "Starting Essentials Web API on Virtual Control Server");
_server.Start(); _server.Start();
GetPaths(); GetPaths();
} }
/// <summary> /// <summary>
/// Print the available pahts /// Print the available pahts
/// </summary> /// </summary>
/// <example> /// <example>
/// http(s)://{ipaddress}/cws/{basePath} /// http(s)://{ipaddress}/cws/{basePath}
/// http(s)://{ipaddress}/VirtualControl/Rooms/{roomId}/cws/{basePath} /// http(s)://{ipaddress}/VirtualControl/Rooms/{roomId}/cws/{basePath}
/// </example> /// </example>
/// <summary> /// <summary>
/// GetPaths method /// GetPaths method
/// </summary> /// </summary>
public void GetPaths() public void GetPaths()
{ {
Debug.LogMessage(LogEventLevel.Information, this, new string('-', 50)); Debug.LogMessage(LogEventLevel.Information, this, new string('-', 50));
var currentIp = CrestronEthernetHelper.GetEthernetParameter( var currentIp = CrestronEthernetHelper.GetEthernetParameter(
CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0); CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);
var hostname = CrestronEthernetHelper.GetEthernetParameter( var hostname = CrestronEthernetHelper.GetEthernetParameter(
CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0); CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0);
var path = CrestronEnvironment.DevicePlatform == eDevicePlatform.Server var path = CrestronEnvironment.DevicePlatform == eDevicePlatform.Server
? $"https://{hostname}/VirtualControl/Rooms/{InitialParametersClass.RoomId}/cws{BasePath}" ? $"https://{hostname}/VirtualControl/Rooms/{InitialParametersClass.RoomId}/cws{BasePath}"
: $"https://{currentIp}/cws{BasePath}"; : $"https://{currentIp}/cws{BasePath}";
Debug.LogMessage(LogEventLevel.Information, this, "Server:{path:l}", path);
var routeCollection = _server.GetRouteCollection(); Debug.LogMessage(LogEventLevel.Information, this, "Server:{path:l}", path);
if (routeCollection == null)
{ var routeCollection = _server.GetRouteCollection();
Debug.LogMessage(LogEventLevel.Information, this, "Server route collection is null"); if (routeCollection == null)
return; {
} Debug.LogMessage(LogEventLevel.Information, this, "Server route collection is null");
Debug.LogMessage(LogEventLevel.Information, this, "Configured Routes:"); return;
foreach (var route in routeCollection) }
{ Debug.LogMessage(LogEventLevel.Information, this, "Configured Routes:");
Debug.LogMessage(LogEventLevel.Information, this, "{routeName:l}: {routePath:l}/{routeUrl:l}", route.Name, path, route.Url); foreach (var route in routeCollection)
} {
Debug.LogMessage(LogEventLevel.Information, this, new string('-', 50)); Debug.LogMessage(LogEventLevel.Information, this, "{routeName:l}: {routePath:l}/{routeUrl:l}", route.Name, path, route.Url);
} }
} Debug.LogMessage(LogEventLevel.Information, this, new string('-', 50));
}
}
} }

View File

@@ -0,0 +1,178 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp.WebScripting;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Core.Web.RequestHandlers;
namespace PepperDash.Essentials.Core.Web.RequestHandlers
{
/// <summary>
/// Handles HTTP requests to retrieve routing devices and tielines information
/// </summary>
public class GetRoutingDevicesAndTieLinesHandler : WebApiBaseRequestHandler
{
public GetRoutingDevicesAndTieLinesHandler() : base(true) { }
protected override void HandleGet(HttpCwsContext context)
{
var devices = new List<RoutingDeviceInfo>();
// Get all devices from DeviceManager
foreach (var device in DeviceManager.AllDevices)
{
var deviceInfo = new RoutingDeviceInfo
{
Key = device.Key,
Name = (device as IKeyName)?.Name ?? device.Key
};
// Check if device implements IRoutingInputs
if (device is IRoutingInputs inputDevice)
{
deviceInfo.HasInputs = true;
deviceInfo.InputPorts = inputDevice.InputPorts.Select(p => new PortInfo
{
Key = p.Key,
SignalType = p.Type.ToString(),
ConnectionType = p.ConnectionType.ToString(),
IsInternal = p.IsInternal
}).ToList();
}
// Check if device implements IRoutingOutputs
if (device is IRoutingOutputs outputDevice)
{
deviceInfo.HasOutputs = true;
deviceInfo.OutputPorts = outputDevice.OutputPorts.Select(p => new PortInfo
{
Key = p.Key,
SignalType = p.Type.ToString(),
ConnectionType = p.ConnectionType.ToString(),
IsInternal = p.IsInternal
}).ToList();
}
// Check if device implements IRoutingInputsOutputs
if (device is IRoutingInputsOutputs)
{
deviceInfo.HasInputsAndOutputs = true;
}
// Only include devices that have routing capabilities
if (deviceInfo.HasInputs || deviceInfo.HasOutputs)
{
devices.Add(deviceInfo);
}
}
// Get all tielines
var tielines = TieLineCollection.Default.Select(tl => new TieLineInfo
{
SourceDeviceKey = tl.SourcePort.ParentDevice.Key,
SourcePortKey = tl.SourcePort.Key,
DestinationDeviceKey = tl.DestinationPort.ParentDevice.Key,
DestinationPortKey = tl.DestinationPort.Key,
SignalType = tl.Type.ToString(),
IsInternal = tl.IsInternal
}).ToList();
var response = new RoutingSystemInfo
{
Devices = devices,
TieLines = tielines
};
var jsonResponse = JsonConvert.SerializeObject(response, Formatting.Indented);
context.Response.StatusCode = 200;
context.Response.StatusDescription = "OK";
context.Response.ContentType = "application/json";
context.Response.ContentEncoding = Encoding.UTF8;
context.Response.Write(jsonResponse, false);
context.Response.End();
}
}
/// <summary>
/// Represents the complete routing system information including devices and tielines
/// </summary>
public class RoutingSystemInfo
{
[JsonProperty("devices")]
public List<RoutingDeviceInfo> Devices { get; set; }
[JsonProperty("tieLines")]
public List<TieLineInfo> TieLines { get; set; }
}
/// <summary>
/// Represents a routing device with its ports information
/// </summary>
public class RoutingDeviceInfo
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("hasInputs")]
public bool HasInputs { get; set; }
[JsonProperty("hasOutputs")]
public bool HasOutputs { get; set; }
[JsonProperty("hasInputsAndOutputs")]
public bool HasInputsAndOutputs { get; set; }
[JsonProperty("inputPorts", NullValueHandling = NullValueHandling.Ignore)]
public List<PortInfo> InputPorts { get; set; }
[JsonProperty("outputPorts", NullValueHandling = NullValueHandling.Ignore)]
public List<PortInfo> OutputPorts { get; set; }
}
/// <summary>
/// Represents a routing port with its properties
/// </summary>
public class PortInfo
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("signalType")]
public string SignalType { get; set; }
[JsonProperty("connectionType")]
public string ConnectionType { get; set; }
[JsonProperty("isInternal")]
public bool IsInternal { get; set; }
}
/// <summary>
/// Represents a tieline connection between two ports
/// </summary>
public class TieLineInfo
{
[JsonProperty("sourceDeviceKey")]
public string SourceDeviceKey { get; set; }
[JsonProperty("sourcePortKey")]
public string SourcePortKey { get; set; }
[JsonProperty("destinationDeviceKey")]
public string DestinationDeviceKey { get; set; }
[JsonProperty("destinationPortKey")]
public string DestinationPortKey { get; set; }
[JsonProperty("signalType")]
public string SignalType { get; set; }
[JsonProperty("isInternal")]
public bool IsInternal { get; set; }
}
}