mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-02-16 13:15:03 +00:00
Merge pull request #1214 from PepperDash/feature-2.0.0/concurrent-routing-issues
Fix issues with concurrent routing actions
This commit is contained in:
@@ -26,7 +26,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.20.66" />
|
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.20.66" />
|
||||||
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-424" />
|
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-451" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="Crestron\CrestronGenericBaseDevice.cs.orig" />
|
<None Include="Crestron\CrestronGenericBaseDevice.cs.orig" />
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using Serilog.Events;
|
using PepperDash.Essentials.Core.Queues;
|
||||||
|
using PepperDash.Essentials.Core.Routing;
|
||||||
|
using Serilog.Events;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@@ -17,6 +19,8 @@ namespace PepperDash.Essentials.Core
|
|||||||
{
|
{
|
||||||
private static readonly Dictionary<string, RouteRequest> RouteRequests = new Dictionary<string, RouteRequest>();
|
private static readonly Dictionary<string, RouteRequest> RouteRequests = new Dictionary<string, RouteRequest>();
|
||||||
|
|
||||||
|
private static readonly GenericQueue routeRequestQueue = new GenericQueue("routingQueue");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets any existing RouteDescriptor for a destination, clears it using ReleaseRoute
|
/// Gets any existing RouteDescriptor for a destination, clears it using ReleaseRoute
|
||||||
/// and then attempts a new Route and if sucessful, stores that RouteDescriptor
|
/// and then attempts a new Route and if sucessful, stores that RouteDescriptor
|
||||||
@@ -33,6 +37,15 @@ namespace PepperDash.Essentials.Core
|
|||||||
|
|
||||||
ReleaseAndMakeRoute(destination, source, signalType, inputPort, outputPort);
|
ReleaseAndMakeRoute(destination, source, signalType, inputPort, outputPort);
|
||||||
}
|
}
|
||||||
|
public static void ReleaseRoute(this IRoutingInputs destination)
|
||||||
|
{
|
||||||
|
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, string.Empty));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ReleaseRoute(this IRoutingInputs destination, string inputPortKey)
|
||||||
|
{
|
||||||
|
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, inputPortKey));
|
||||||
|
}
|
||||||
|
|
||||||
public static void RemoveRouteRequestForDestination(string destinationKey)
|
public static void RemoveRouteRequestForDestination(string destinationKey)
|
||||||
{
|
{
|
||||||
@@ -45,133 +58,6 @@ namespace PepperDash.Essentials.Core
|
|||||||
Debug.LogMessage(LogEventLevel.Information, messageTemplate, null, destinationKey);
|
Debug.LogMessage(LogEventLevel.Information, messageTemplate, null, destinationKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReleaseAndMakeRoute(IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort = null, RoutingOutputPort sourcePort = null)
|
|
||||||
{
|
|
||||||
if (destination == null) throw new ArgumentNullException(nameof(destination));
|
|
||||||
if (source == null) throw new ArgumentNullException(nameof(source));
|
|
||||||
if (destinationPort == null) Debug.LogMessage(LogEventLevel.Information, "Destination port is null");
|
|
||||||
if (sourcePort == null) Debug.LogMessage(LogEventLevel.Information, "Source port is null");
|
|
||||||
|
|
||||||
var routeRequest = new RouteRequest
|
|
||||||
{
|
|
||||||
Destination = destination,
|
|
||||||
DestinationPort = destinationPort,
|
|
||||||
Source = source,
|
|
||||||
SourcePort = sourcePort,
|
|
||||||
SignalType = signalType
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var coolingDevice = destination as IWarmingCooling;
|
|
||||||
|
|
||||||
//We already have a route request for this device, and it's a cooling device and is cooling
|
|
||||||
if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
|
||||||
{
|
|
||||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
|
|
||||||
|
|
||||||
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
|
||||||
|
|
||||||
RouteRequests[destination.Key] = routeRequest;
|
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is cooling down and already has a routing request stored. Storing new route request to route to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//New Request
|
|
||||||
if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
|
||||||
{
|
|
||||||
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
|
||||||
|
|
||||||
RouteRequests.Add(destination.Key, routeRequest);
|
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is cooling down. Storing route request to route to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RouteRequests.ContainsKey(destination.Key) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == false)
|
|
||||||
{
|
|
||||||
var handledRequest = RouteRequests[destination.Key];
|
|
||||||
|
|
||||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= handledRequest.HandleCooldown;
|
|
||||||
|
|
||||||
RouteRequests.Remove(destination.Key);
|
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is NOT cooling down. Removing stored route request and routing to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key);
|
|
||||||
}
|
|
||||||
|
|
||||||
destination.ReleaseRoute(destinationPort?.Key ?? string.Empty);
|
|
||||||
|
|
||||||
RunRouteRequest(routeRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RunRouteRequest(RouteRequest request)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (request.Source == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var (audioOrSingleRoute, videoRoute) = request.Destination.GetRouteToSource(request.Source, request.SignalType, request.DestinationPort, request.SourcePort);
|
|
||||||
|
|
||||||
if (audioOrSingleRoute == null && videoRoute == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(audioOrSingleRoute);
|
|
||||||
|
|
||||||
if (videoRoute != null)
|
|
||||||
{
|
|
||||||
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(videoRoute);
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, "Executing full route", request.Destination);
|
|
||||||
|
|
||||||
audioOrSingleRoute.ExecuteRoutes();
|
|
||||||
videoRoute?.ExecuteRoutes();
|
|
||||||
} catch(Exception ex)
|
|
||||||
{
|
|
||||||
Debug.LogMessage(ex, "Exception Running Route Request {request}", null, request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ReleaseRoute(this IRoutingInputs destination)
|
|
||||||
{
|
|
||||||
ReleaseRoute(destination, string.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Will release the existing route on the destination, if it is found in
|
|
||||||
/// RouteDescriptorCollection.DefaultCollection
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="destination"></param>
|
|
||||||
public static void ReleaseRoute(this IRoutingInputs destination, string inputPortKey)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Release route for '{destination}':'{inputPortKey}'", destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
|
|
||||||
|
|
||||||
if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRequest) && destination is IWarmingCooling)
|
|
||||||
{
|
|
||||||
var coolingDevice = destination as IWarmingCooling;
|
|
||||||
|
|
||||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRequest.HandleCooldown;
|
|
||||||
}
|
|
||||||
|
|
||||||
RouteRequests.Remove(destination.Key);
|
|
||||||
|
|
||||||
var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination, inputPortKey);
|
|
||||||
if (current != null)
|
|
||||||
{
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Releasing current route: {0}", destination, current.Source.Key);
|
|
||||||
current.ReleaseRoutes();
|
|
||||||
}
|
|
||||||
} catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.LogMessage(ex, "Exception releasing route for '{destination}':'{inputPortKey}'",null, destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Builds a RouteDescriptor that contains the steps necessary to make a route between devices.
|
/// Builds a RouteDescriptor that contains the steps necessary to make a route between devices.
|
||||||
/// Routes of type AudioVideo will be built as two separate routes, audio and video. If
|
/// Routes of type AudioVideo will be built as two separate routes, audio and video. If
|
||||||
@@ -234,6 +120,126 @@ namespace PepperDash.Essentials.Core
|
|||||||
return (audioRouteDescriptor, videoRouteDescriptor);
|
return (audioRouteDescriptor, videoRouteDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ReleaseAndMakeRoute(IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort = null, RoutingOutputPort sourcePort = null)
|
||||||
|
{
|
||||||
|
if (destination == null) throw new ArgumentNullException(nameof(destination));
|
||||||
|
if (source == null) throw new ArgumentNullException(nameof(source));
|
||||||
|
if (destinationPort == null) Debug.LogMessage(LogEventLevel.Information, "Destination port is null");
|
||||||
|
if (sourcePort == null) Debug.LogMessage(LogEventLevel.Information, "Source port is null");
|
||||||
|
|
||||||
|
var routeRequest = new RouteRequest
|
||||||
|
{
|
||||||
|
Destination = destination,
|
||||||
|
DestinationPort = destinationPort,
|
||||||
|
Source = source,
|
||||||
|
SourcePort = sourcePort,
|
||||||
|
SignalType = signalType
|
||||||
|
};
|
||||||
|
|
||||||
|
var coolingDevice = destination as IWarmingCooling;
|
||||||
|
|
||||||
|
//We already have a route request for this device, and it's a cooling device and is cooling
|
||||||
|
if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
||||||
|
{
|
||||||
|
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
|
||||||
|
|
||||||
|
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
||||||
|
|
||||||
|
RouteRequests[destination.Key] = routeRequest;
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is cooling down and already has a routing request stored. Storing new route request to route to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//New Request
|
||||||
|
if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
||||||
|
{
|
||||||
|
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
||||||
|
|
||||||
|
RouteRequests.Add(destination.Key, routeRequest);
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is cooling down. Storing route request to route to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RouteRequests.ContainsKey(destination.Key) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == false)
|
||||||
|
{
|
||||||
|
var handledRequest = RouteRequests[destination.Key];
|
||||||
|
|
||||||
|
coolingDevice.IsCoolingDownFeedback.OutputChange -= handledRequest.HandleCooldown;
|
||||||
|
|
||||||
|
RouteRequests.Remove(destination.Key);
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is NOT cooling down. Removing stored route request and routing to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination,destinationPort?.Key ?? string.Empty));
|
||||||
|
|
||||||
|
routeRequestQueue.Enqueue(new RouteRequestQueueItem(RunRouteRequest, routeRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RunRouteRequest(RouteRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (request.Source == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var (audioOrSingleRoute, videoRoute) = request.Destination.GetRouteToSource(request.Source, request.SignalType, request.DestinationPort, request.SourcePort);
|
||||||
|
|
||||||
|
if (audioOrSingleRoute == null && videoRoute == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(audioOrSingleRoute);
|
||||||
|
|
||||||
|
if (videoRoute != null)
|
||||||
|
{
|
||||||
|
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(videoRoute);
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Executing full route", request.Destination);
|
||||||
|
|
||||||
|
audioOrSingleRoute.ExecuteRoutes();
|
||||||
|
videoRoute?.ExecuteRoutes();
|
||||||
|
} catch(Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogMessage(ex, "Exception Running Route Request {request}", null, request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Will release the existing route on the destination, if it is found in
|
||||||
|
/// RouteDescriptorCollection.DefaultCollection
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="destination"></param>
|
||||||
|
private static void ReleaseRouteInternal(IRoutingInputs destination, string inputPortKey)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Information, "Release route for '{destination}':'{inputPortKey}'", destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
|
||||||
|
|
||||||
|
if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRequest) && destination is IWarmingCooling)
|
||||||
|
{
|
||||||
|
var coolingDevice = destination as IWarmingCooling;
|
||||||
|
|
||||||
|
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRequest.HandleCooldown;
|
||||||
|
}
|
||||||
|
|
||||||
|
RouteRequests.Remove(destination.Key);
|
||||||
|
|
||||||
|
var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination, inputPortKey);
|
||||||
|
if (current != null)
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Information, "Releasing current route: {0}", destination, current.Source.Key);
|
||||||
|
current.ReleaseRoutes();
|
||||||
|
}
|
||||||
|
} catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogMessage(ex, "Exception releasing route for '{destination}':'{inputPortKey}'",null, destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The recursive part of this. Will stop on each device, search its inputs for the
|
/// The recursive part of this. Will stop on each device, search its inputs for the
|
||||||
/// desired source and if not found, invoke this function for the each input port
|
/// desired source and if not found, invoke this function for the each input port
|
||||||
|
|||||||
@@ -38,5 +38,10 @@ namespace PepperDash.Essentials.Core
|
|||||||
Debug.LogMessage(ex, "Exception handling cooldown", Destination);
|
Debug.LogMessage(ex, "Exception handling cooldown", Destination);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"Route {Source?.Key ?? "No Source Device"}:{SourcePort?.Key ?? "auto"} to {Destination?.Key ?? "No Destination Device"}:{DestinationPort?.Key ?? "auto"}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
using PepperDash.Core;
|
||||||
|
using PepperDash.Essentials.Core.Queues;
|
||||||
|
using System;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core.Routing
|
||||||
|
{
|
||||||
|
public class RouteRequestQueueItem : IQueueMessage
|
||||||
|
{
|
||||||
|
private readonly Action<RouteRequest> action;
|
||||||
|
private readonly RouteRequest routeRequest;
|
||||||
|
|
||||||
|
public RouteRequestQueueItem(Action<RouteRequest> routeAction, RouteRequest request)
|
||||||
|
{
|
||||||
|
action = routeAction;
|
||||||
|
routeRequest = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispatch()
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Information, "Dispatching route request {routeRequest}", null, routeRequest);
|
||||||
|
action(routeRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReleaseRouteQueueItem : IQueueMessage
|
||||||
|
{
|
||||||
|
private readonly Action<IRoutingInputs, string> action;
|
||||||
|
private readonly IRoutingInputs destination;
|
||||||
|
private readonly string inputPortKey;
|
||||||
|
|
||||||
|
public ReleaseRouteQueueItem(Action<IRoutingInputs, string> action, IRoutingInputs destination, string inputPortKey)
|
||||||
|
{
|
||||||
|
this.action = action;
|
||||||
|
this.destination = destination;
|
||||||
|
this.inputPortKey = inputPortKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispatch()
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Information, "Dispatching release route request for {destination}:{inputPortKey}", null, destination?.Key ?? "no destination", string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
|
||||||
|
action(destination, inputPortKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,6 +30,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.20.66" />
|
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.20.66" />
|
||||||
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-424" />
|
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-451" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -28,14 +28,14 @@ namespace PepperDash.Essentials.Devices.Common.SoftCodec
|
|||||||
|
|
||||||
for(var i = 1; i <= props.OutputCount; i++)
|
for(var i = 1; i <= props.OutputCount; i++)
|
||||||
{
|
{
|
||||||
var outputPort = new RoutingOutputPort($"{Key}-output{i}", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null, this);
|
var outputPort = new RoutingOutputPort($"output{i}", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null, this);
|
||||||
|
|
||||||
OutputPorts.Add(outputPort);
|
OutputPorts.Add(outputPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(var i = 1; i<= props.ContentInputCount; i++)
|
for(var i = 1; i<= props.ContentInputCount; i++)
|
||||||
{
|
{
|
||||||
var inputPort = new RoutingInputPort($"{Key}-contentInput{i}", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, $"contentInput{i}", this);
|
var inputPort = new RoutingInputPort($"contentInput{i}", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, $"contentInput{i}", this);
|
||||||
|
|
||||||
InputPorts.Add(inputPort);
|
InputPorts.Add(inputPort);
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ namespace PepperDash.Essentials.Devices.Common.SoftCodec
|
|||||||
|
|
||||||
for(var i = 1; i <=props.CameraInputCount; i++)
|
for(var i = 1; i <=props.CameraInputCount; i++)
|
||||||
{
|
{
|
||||||
var cameraPort = new RoutingInputPort($"{Key}-cameraInput{i}", eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, $"cameraInput{i}", this);
|
var cameraPort = new RoutingInputPort($"cameraInput{i}", eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, $"cameraInput{i}", this);
|
||||||
|
|
||||||
InputPorts.Add(cameraPort);
|
InputPorts.Add(cameraPort);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Crestron.SimplSharp.SDK.Program" Version="2.20.66" />
|
<PackageReference Include="Crestron.SimplSharp.SDK.Program" Version="2.20.66" />
|
||||||
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-424" />
|
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-451" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\PepperDash.Essentials.Core\PepperDash.Essentials.Core.csproj" />
|
<ProjectReference Include="..\PepperDash.Essentials.Core\PepperDash.Essentials.Core.csproj" />
|
||||||
|
|||||||
Reference in New Issue
Block a user