Compare commits

..

1 Commits

Author SHA1 Message Date
Nick Genovese
e294131f98 fix: now pushes the tag whenever not rc 2024-12-06 13:33:08 -05:00
19 changed files with 205 additions and 482 deletions

View File

@@ -22,7 +22,7 @@ env:
jobs:
Build_Project_4-Series:
runs-on: windows-latest
steps:
steps:
- uses: actions/checkout@v3
- name: Set Version Number
id: setVersion
@@ -57,66 +57,20 @@ jobs:
$phase = 'beta'
$newVersionString = "{0}-{1}-{2}" -f $newVersion, $phase, $Env:GITHUB_RUN_NUMBER
}
}
echo "version=$newVersionString" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
}
echo "version=$newVersionString" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
- name: Setup MS Build
uses: microsoft/setup-msbuild@v1.1
- name: restore Nuget Packages
run: nuget restore .\$($Env:SOLUTION_FILE).sln
# Build the solutions in the docker image
- name: Build Solution
- name: Build Solution
run: msbuild .\$($Env:SOLUTION_FILE).sln /p:Platform="Any CPU" /p:Configuration="Debug" /p:Version="${{ steps.setVersion.outputs.version }}" -m
- name: Debug CPZ Files
shell: powershell
run: |
Write-Host "Checking for CPZ files..."
# First, let's find out the actual directory structure
Write-Host "Current directory: $(Get-Location)"
Write-Host "Directory structure:"
Get-ChildItem -Path . -Directory -Recurse -Depth 2 | ForEach-Object { Write-Host $_.FullName }
# Look for all CPZ files in the repository
Write-Host "Searching for all CPZ files in the repository:"
$cpzFiles = Get-ChildItem -Path . -Recurse -Filter "*.cpz"
if ($cpzFiles.Count -eq 0) {
Write-Host "No CPZ files found in the repository."
} else {
Write-Host "Found $($cpzFiles.Count) CPZ files:"
foreach ($file in $cpzFiles) {
Write-Host " $($file.FullName)"
}
# Create output directory if it doesn't exist
$outputDir = ".\output\build"
if (-not (Test-Path $outputDir)) {
New-Item -ItemType Directory -Path $outputDir -Force
Write-Host "Created output directory: $outputDir"
}
# Copy all CPZ files to the output directory
foreach ($file in $cpzFiles) {
$destPath = Join-Path $outputDir $file.Name
Write-Host "Copying $($file.FullName) to $destPath"
Copy-Item -Path $file.FullName -Destination $destPath -Force
}
}
- name: Pack Solution
run: |
dotnet pack .\$($Env:SOLUTION_FILE).sln --configuration $env:BUILD_TYPE --output ./output /p:Version="${{ steps.setVersion.outputs.version }}"
# Ensure CPZ files are included in the package
$cpzFiles = Get-ChildItem -Path . -Recurse | Where-Object { $_.Extension -eq ".cpz" }
if ($cpzFiles.Count -eq 0) {
Write-Host "WARNING: No CPZ files found!"
} else {
Write-Host "Found $($cpzFiles.Count) CPZ files"
foreach ($file in $cpzFiles) {
Write-Host "CPZ file: $($file.FullName)"
}
}
run: dotnet pack .\$($Env:SOLUTION_FILE).sln --configuration $env:BUILD_TYPE --output ./output /p:Version="${{ steps.setVersion.outputs.version }}"
- name: Create tag for non-rc builds
if: contains(steps.setVersion.outputs.version, 'alpha')
if: ${{ !contains(steps.setVersion.outputs.version, 'rc') }}
run: |
git tag ${{ steps.setVersion.outputs.version }}
git push --tags origin
@@ -130,11 +84,11 @@ jobs:
prerelease: ${{contains('debug', env.BUILD_TYPE)}}
tag: ${{ steps.setVersion.outputs.version }}
- name: Setup Nuget
run: |
run: |
nuget sources add -name github -source https://nuget.pkg.github.com/pepperdash/index.json -username pepperdash -password ${{ secrets.GITHUB_TOKEN }}
nuget setApiKey ${{ secrets.GITHUB_TOKEN }} -Source github
nuget setApiKey ${{ secrets.NUGET_API_KEY }} -Source https://api.nuget.org/v3/index.json
nuget setApiKey ${{ secrets.NUGET_API_KEY }} -Source https://api.nuget.org/v3/index.json
- name: Publish to Nuget
run: nuget push .\output\*.nupkg -Source https://api.nuget.org/v3/index.json
- name: Publish to Github Nuget
run: nuget push .\output\*.nupkg -Source github
run: nuget push .\output\*.nupkg -Source github

View File

@@ -6,7 +6,6 @@
Provided under MIT license
## Overview
PepperDash Essentials is an open source Crestron framework that can be configured as a standalone program capable of running a wide variety of system designs and can also be utilized as a plug-in architecture to augment other Simpl# Pro and Simpl Windows programs.

View File

@@ -1,19 +1,6 @@
<Project>
<ItemGroup>
<!-- Include CPZ files from multiple possible locations -->
<None Include="$(TargetDir)*.cpz" Condition="$(ProjectType) == 'Program'">
<Pack>true</Pack>
<PackagePath>build;</PackagePath>
</None>
<None Include="$(OutputPath)*.cpz" Condition="$(ProjectType) == 'Program'">
<Pack>true</Pack>
<PackagePath>build;</PackagePath>
</None>
<None Include="$(MSBuildProjectDirectory)\bin\$(Configuration)\**\*.cpz" Condition="$(ProjectType) == 'Program'">
<Pack>true</Pack>
<PackagePath>build;</PackagePath>
</None>
<None Include="$(PackageOutputPath)\build\*.cpz" Condition="$(ProjectType) == 'Program'">
<None Include="$(PackageOutputPath)\$(AssemblyName)\*.cpz" Condition="$(ProjectType) == 'Program'">
<Pack>true</Pack>
<PackagePath>build;</PackagePath>
</None>
@@ -23,94 +10,17 @@
</None>
</ItemGroup>
<Target Name="Create CPLZ" AfterTargets="Build; AfterRebuild" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''">
<Message Text="Creating CPLZ $(TargetDir)" Importance="high" />
<Message Text="PackageOutputPath: $(PackageOutputPath)" Importance="high" />
<Message Text="AssemblyName: $(AssemblyName)" Importance="high" />
<Message Text="TargetName: $(TargetName)" Importance="high" />
<Message Text="Version: $(Version)" Importance="high" />
<Message Text="TargetFramework: $(TargetFramework)" Importance="high" />
<Message Text="Creating CPLZ $(TargetDir)"></Message>
<MakeDir Directories="$(PackageOutputPath)" Condition="!Exists($(PackageOutputPath))" />
<MakeDir Directories="$(PackageOutputPath)\$(AssemblyName)" Condition="!Exists('$(PackageOutputPath)\$(AssemblyName)')" />
<ZipDirectory SourceDirectory="$(TargetDir)" DestinationFile="$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cplz" Overwrite="true"/>
</Target>
<Target Name="Debug Variables" BeforeTargets="Build">
<Message Text="================ Debug Variables ================" Importance="high" />
<Message Text="ProjectType: '$(ProjectType)'" Importance="high" />
<Message Text="TargetFramework: '$(TargetFramework)'" Importance="high" />
<Message Text="TargetDir: '$(TargetDir)'" Importance="high" />
<Message Text="===============================================" Importance="high" />
<Target Name="Copy CPZ NET6" AfterTargets="SimplSharpPostProcess" Condition="($(ProjectType) == 'Program' And ( '$(TargetFramework)' == 'net6.0' ) Or ( '$(TargetFramework)' == 'net8.0' ))">
<Message Text="Copying CPZ"></Message>
<Move SourceFiles="$(TargetDir)$(TargetName).cpz" DestinationFiles="$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cpz" />
</Target>
<Target Name="Copy CPZ NET472"
AfterTargets="Build"
DependsOnTargets="BuildDependencies"
Condition="'$(ProjectType)' == 'Program' And '$(TargetFramework)' == 'net472'">
<Message Text="========================================" Importance="high" />
<Message Text="Starting CPZ Build Process for NET472" Importance="high" />
<Message Text="ProjectType: '$(ProjectType)'" Importance="high" />
<Message Text="TargetFramework: '$(TargetFramework)'" Importance="high" />
<Message Text="========================================" Importance="high" />
<!-- Create output directory -->
<MakeDir Directories="$(PackageOutputPath)\$(AssemblyName)"
Condition="!Exists('$(PackageOutputPath)\$(AssemblyName)')" />
<!-- Copy the CPZ file -->
<Copy SourceFiles="$(TargetDir)$(TargetName).cpz"
DestinationFiles="$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cpz"
Condition="Exists('$(TargetDir)$(TargetName).cpz')" />
<Message Text="CPZ Build completed for NET472"
Condition="Exists('$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cpz')"
Importance="high" />
<Target Name="Copy CPZ NET47" AfterTargets="SimplSharpPostProcess47" Condition="($(ProjectType) == 'Program' And ( '$(TargetFramework)' != 'net6.0' ) And ( '$(TargetFramework)' != 'net8.0' ))">
<Message Text="Copying CPZ"></Message>
<Move SourceFiles="$(TargetDir)$(TargetName).cpz" DestinationFiles="$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cpz" />
</Target>
<Target Name="Copy CPZ NET6"
AfterTargets="Build"
DependsOnTargets="BuildDependencies"
Condition="'$(ProjectType)' == 'Program' And ('$(TargetFramework)' == 'net6.0' Or '$(TargetFramework)' == 'net8.0')">
<Message Text="========================================" Importance="high" />
<Message Text="Starting CPZ Build Process" Importance="high" />
<Message Text="ProjectType: '$(ProjectType)'" Importance="high" />
<Message Text="TargetFramework: '$(TargetFramework)'" Importance="high" />
<Message Text="MSBuildProjectDirectory: '$(MSBuildProjectDirectory)'" Importance="high" />
<Message Text="PATH: '$(PATH)'" Importance="high" />
<Message Text="========================================" Importance="high" />
<!-- Check for SimplSharp compiler -->
<PropertyGroup>
<SimplSharpCompilerPath>$(HOME)/.crestron/SimplSharpPro/SimplSharpCompiler</SimplSharpCompilerPath>
</PropertyGroup>
<Warning Text="SimplSharpCompiler not found at: $(SimplSharpCompilerPath)"
Condition="!Exists('$(SimplSharpCompilerPath)')" />
<!-- Run the SimplSharp compiler to create CPZ -->
<Exec Command="&quot;$(SimplSharpCompilerPath)&quot; &quot;$(MSBuildProjectDirectory)&quot; &quot;$(TargetDir)&quot;"
IgnoreExitCode="false"
WorkingDirectory="$(MSBuildProjectDirectory)"
ConsoleToMSBuild="true"
Condition="Exists('$(SimplSharpCompilerPath)')">
<Output TaskParameter="ConsoleOutput" PropertyName="OutputOfExec" />
</Exec>
<Message Text="SimplSharp Output: $(OutputOfExec)" Importance="high" />
<!-- Create output directory -->
<MakeDir Directories="$(PackageOutputPath)\$(AssemblyName)"
Condition="!Exists('$(PackageOutputPath)\$(AssemblyName)')" />
<!-- Copy the CPZ file -->
<Copy SourceFiles="$(TargetDir)$(TargetName).cpz"
DestinationFiles="$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cpz"
Condition="Exists('$(TargetDir)$(TargetName).cpz')" />
<Message Text="CPZ Build completed"
Condition="Exists('$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cpz')"
Importance="high" />
</Target>
<Target Name="BuildDependencies">
<MSBuild Projects="@(ProjectReference)"
Targets="Build"
BuildInParallel="true" />
</Target>
</Project>
</Project>

View File

@@ -21,7 +21,7 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// <summary>
/// Represents a generic digital input deviced tied to a versiport
/// </summary>
public class GenericVersiportDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput, IPartitionStateProvider
public class GenericVersiportDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput
{
public Versiport InputPort { get; private set; }
@@ -35,15 +35,10 @@ namespace PepperDash.Essentials.Core.CrestronIO
}
}
public BoolFeedback PartitionPresentFeedback { get; }
public bool PartitionPresent => !InputStateFeedbackFunc();
public GenericVersiportDigitalInputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) :
base(key, name)
{
InputStateFeedback = new BoolFeedback(InputStateFeedbackFunc);
PartitionPresentFeedback = new BoolFeedback(() => !InputStateFeedbackFunc());
AddPostActivationAction(() =>
{
@@ -57,8 +52,7 @@ namespace PepperDash.Essentials.Core.CrestronIO
InputPort.VersiportChange += InputPort_VersiportChange;
InputStateFeedback.FireUpdate();
PartitionPresentFeedback.FireUpdate();
Debug.LogMessage(LogEventLevel.Debug, this, "Created GenericVersiportDigitalInputDevice on port '{0}'. DisablePullUpResistor: '{1}'", config.PortNumber, InputPort.DisablePullUpResistor);
@@ -71,10 +65,7 @@ namespace PepperDash.Essentials.Core.CrestronIO
Debug.LogMessage(LogEventLevel.Debug, this, "Versiport change: {0}", args.Event);
if(args.Event == eVersiportEvent.DigitalInChange)
{
InputStateFeedback.FireUpdate();
PartitionPresentFeedback.FireUpdate();
}
}

View File

@@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
public interface IEmergencyOSD
{
void ShowEmergencyMessage(string url);
void HideEmergencyMessage();
}
}

View File

@@ -1,3 +1,5 @@
using Crestron.SimplSharp;
using Newtonsoft.Json;
using PepperDash.Core;
@@ -9,6 +11,7 @@ using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core
{
public class DeviceJsonApi
@@ -138,26 +141,18 @@ namespace PepperDash.Essentials.Core
.Select((p, i) => ConvertType(action.Params[i], p.ParameterType))
.ToArray();
try
await Task.Run(() =>
{
Debug.LogMessage(LogEventLevel.Verbose, "Calling method {methodName} on device {deviceKey} with {@params}", null, method.Name, action.DeviceKey, action.Params);
var result = method.Invoke(obj, convertedParams);
// If the method returns a Task, await it
if (result is Task task)
try
{
await task;
Debug.LogMessage(LogEventLevel.Verbose, "Calling method {methodName} on device {deviceKey} with {@params}", null, method.Name, action.DeviceKey, action.Params);
method.Invoke(obj, convertedParams);
}
// If the method returns a Task<T>, await it
else if (result != null && result.GetType().IsGenericType && result.GetType().GetGenericTypeDefinition() == typeof(Task<>))
catch (Exception e)
{
await (Task)result;
Debug.LogMessage(e, "Error invoking method {methodName} on device {deviceKey}", null, method.Name, action.DeviceKey);
}
}
catch (Exception e)
{
Debug.LogMessage(e, "Error invoking method {methodName} on device {deviceKey}", null, method.Name, action.DeviceKey);
}
});
}
catch (Exception ex)
{

View File

@@ -26,7 +26,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.20.66" />
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-451" />
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-424" />
</ItemGroup>
<ItemGroup>
<None Include="Crestron\CrestronGenericBaseDevice.cs.orig" />

View File

@@ -81,10 +81,12 @@ namespace PepperDash.Essentials.Core
foreach (var action in activationActions)
{
this.LogInformation("Running Activation action {@action}", action);
await DeviceJsonApi.DoDeviceActionAsync(action);
tasks.Add(DeviceJsonApi.DoDeviceActionAsync(action));
}
}
await Task.WhenAll(tasks);
IsActive = true;
}
@@ -99,10 +101,12 @@ namespace PepperDash.Essentials.Core
foreach (var action in deactivationActions)
{
this.LogInformation("Running deactivation action {actionDeviceKey}:{actionMethod}", action.DeviceKey, action.MethodName);
await DeviceJsonApi.DoDeviceActionAsync(action);
tasks.Add( DeviceJsonApi.DoDeviceActionAsync(action));
}
}
await Task.WhenAll(tasks);
IsActive = false;
}

View File

@@ -24,7 +24,6 @@ namespace PepperDash.Essentials.Room.Config
//switch on emergency type here. Right now only contact and shutdown
var e = new EssentialsRoomEmergencyContactClosure(room.Key + "-emergency", props.Emergency, room);
DeviceManager.AddDevice(e);
return e;
}
return null;
}

View File

@@ -16,7 +16,7 @@
public class EssentialsRoomEmergencyTriggerConfig
{
/// <summary>
/// contact,versiport
/// contact,
/// </summary>
public string Type { get; set; }
/// <summary>

View File

@@ -4,16 +4,12 @@ using PepperDash.Essentials.Room.Config;
namespace PepperDash.Essentials.Core
{
public class EssentialsRoomEmergencyContactClosure : EssentialsRoomEmergencyBase, IEssentialsRoomEmergency
public class EssentialsRoomEmergencyContactClosure : EssentialsRoomEmergencyBase
{
public event EventHandler<EventArgs> EmergencyStateChange;
IEssentialsRoom Room;
string Behavior;
bool TriggerOnClose;
public bool InEmergency { get; private set; }
public EssentialsRoomEmergencyContactClosure(string key, EssentialsRoomEmergencyConfig config, IEssentialsRoom room) :
base(key)
{
@@ -29,49 +25,14 @@ namespace PepperDash.Essentials.Core
cs.DigitalInputPorts[portNum].StateChange += EsentialsRoomEmergencyContactClosure_StateChange;
}
}
else if (config.Trigger.Type.Equals("versiport", StringComparison.OrdinalIgnoreCase))
{
var portNum = (uint)config.Trigger.Number;
if (portNum <= cs.NumberOfVersiPorts)
{
cs.VersiPorts[portNum].Register();
cs.VersiPorts[portNum].SetVersiportConfiguration(eVersiportConfiguration.DigitalInput);
cs.VersiPorts[portNum].DisablePullUpResistor = true;
cs.VersiPorts[portNum].VersiportChange += EssentialsRoomEmergencyContactClosure_VersiportChange;
}
}
Behavior = config.Behavior;
TriggerOnClose = config.Trigger.TriggerOnClose;
}
private void EssentialsRoomEmergencyContactClosure_VersiportChange(Versiport port, VersiportEventArgs args)
{
if (args.Event == eVersiportEvent.DigitalInChange)
{
ContactClosure_StateChange(port.DigitalIn);
}
}
void EsentialsRoomEmergencyContactClosure_StateChange(DigitalInput digitalInput, DigitalInputEventArgs args)
{
ContactClosure_StateChange(args.State);
}
void ContactClosure_StateChange(bool portState)
{
if (portState && TriggerOnClose || !portState && !TriggerOnClose)
{
InEmergency = true;
if (EmergencyStateChange != null)
EmergencyStateChange(this, new EventArgs());
if (args.State && TriggerOnClose || !args.State && !TriggerOnClose)
RunEmergencyBehavior();
}
else
{
InEmergency = false;
if (EmergencyStateChange != null)
EmergencyStateChange(this, new EventArgs());
}
}
/// <summary>
@@ -83,14 +44,4 @@ namespace PepperDash.Essentials.Core
Room.Shutdown();
}
}
/// <summary>
/// Describes the functionality of a room emergency contact closure
/// </summary>
public interface IEssentialsRoomEmergency
{
event EventHandler<EventArgs> EmergencyStateChange;
bool InEmergency { get; }
}
}

View File

@@ -1,6 +1,4 @@
using PepperDash.Essentials.Core.Queues;
using PepperDash.Essentials.Core.Routing;
using Serilog.Events;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -19,8 +17,6 @@ namespace PepperDash.Essentials.Core
{
private static readonly Dictionary<string, RouteRequest> RouteRequests = new Dictionary<string, RouteRequest>();
private static readonly GenericQueue routeRequestQueue = new GenericQueue("routingQueue");
/// <summary>
/// Gets any existing RouteDescriptor for a destination, clears it using ReleaseRoute
/// and then attempts a new Route and if sucessful, stores that RouteDescriptor
@@ -37,15 +33,6 @@ namespace PepperDash.Essentials.Core
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)
{
@@ -58,6 +45,122 @@ namespace PepperDash.Essentials.Core
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)
{
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();
}
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)
{
Debug.LogMessage(LogEventLevel.Information, "Release route for {inputPortKey}", destination, 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.Debug, "Releasing current route: {0}", destination, current.Source.Key);
current.ReleaseRoutes();
}
}
/// <summary>
/// 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
@@ -76,8 +179,7 @@ namespace PepperDash.Essentials.Core
if (!destination.GetRouteToSource(source, null, null, signalType, 0, singleTypeRouteDescriptor, destinationPort, sourcePort))
singleTypeRouteDescriptor = null;
var routes = singleTypeRouteDescriptor?.Routes ?? new List<RouteSwitchDescriptor>();
foreach (var route in routes)
foreach (var route in singleTypeRouteDescriptor.Routes)
{
Debug.LogMessage(LogEventLevel.Verbose, "Route for device: {route}", destination, route.ToString());
}
@@ -120,126 +222,6 @@ namespace PepperDash.Essentials.Core
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>
/// 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

View File

@@ -12,19 +12,19 @@ namespace PepperDash.Essentials.Core
/// Represents an collection of individual route steps between Source and Destination
/// </summary>
public class RouteDescriptor
{
public IRoutingInputs Destination { get; private set; }
{
public IRoutingInputs Destination { get; private set; }
public RoutingInputPort InputPort { get; private set; }
public IRoutingOutputs Source { get; private set; }
public eRoutingSignalType SignalType { get; private set; }
public List<RouteSwitchDescriptor> Routes { get; private set; }
public IRoutingOutputs Source { get; private set; }
public eRoutingSignalType SignalType { get; private set; }
public List<RouteSwitchDescriptor> Routes { get; private set; }
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType) : this(source, destination, null, signalType)
{
}
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType):this(source,destination, null, signalType)
{
}
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, RoutingInputPort inputPort, eRoutingSignalType signalType)
{
@@ -35,20 +35,20 @@ namespace PepperDash.Essentials.Core
Routes = new List<RouteSwitchDescriptor>();
}
/// <summary>
/// Executes all routes described in this collection. Typically called via
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
/// </summary>
public void ExecuteRoutes()
{
foreach (var route in Routes)
{
Debug.LogMessage(LogEventLevel.Verbose, "ExecuteRoutes: {0}", null, route.ToString());
/// <summary>
/// Executes all routes described in this collection. Typically called via
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
/// </summary>
public void ExecuteRoutes()
{
foreach (var route in Routes)
{
Debug.LogMessage(LogEventLevel.Verbose, "ExecuteRoutes: {0}",null, route.ToString());
if (route.SwitchingDevice is IRoutingSinkWithSwitching sink)
{
{
sink.ExecuteSwitch(route.InputPort.Selector);
continue;
continue;
}
if (route.SwitchingDevice is IRouting switchingDevice)
@@ -59,15 +59,15 @@ namespace PepperDash.Essentials.Core
Debug.LogMessage(LogEventLevel.Verbose, "Output port {0} routing. Count={1}", null, route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
}
}
}
}
}
/// <summary>
/// Releases all routes in this collection. Typically called via
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
/// </summary>
public void ReleaseRoutes()
{
/// <summary>
/// Releases all routes in this collection. Typically called via
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
/// </summary>
public void ReleaseRoutes()
{
foreach (var route in Routes.Where(r => r.SwitchingDevice is IRouting))
{
if (route.SwitchingDevice is IRouting switchingDevice)
@@ -77,6 +77,8 @@ namespace PepperDash.Essentials.Core
continue;
}
switchingDevice.ExecuteSwitch(null, route.OutputPort.Selector, SignalType);
if (route.OutputPort.InUseTracker != null)
{
route.OutputPort.InUseTracker.RemoveUser(Destination, "destination-" + SignalType);
@@ -90,12 +92,12 @@ namespace PepperDash.Essentials.Core
}
}
public override string ToString()
{
var routesText = Routes.Select(r => r.ToString()).ToArray();
return string.Format("Route table from {0} to {1}:\r{2}", Source.Key, Destination.Key, string.Join("\r", routesText));
}
}
public override string ToString()
{
var routesText = Routes.Select(r => r.ToString()).ToArray();
return string.Format("Route table from {0} to {1}:\r{2}", Source.Key, Destination.Key, string.Join("\r", routesText));
}
}
/*/// <summary>
/// Represents an collection of individual route steps between Source and Destination

View File

@@ -41,7 +41,7 @@ namespace PepperDash.Essentials.Core
&& RouteDescriptors.Any(t => t.Destination == descriptor.Destination && t.InputPort != null && descriptor.InputPort != null && t.InputPort.Key == descriptor.InputPort.Key))
{
Debug.LogMessage(LogEventLevel.Debug, descriptor.Destination,
"Route to [{0}] already exists in global routes table", descriptor?.Source?.Key);
"Route to [{0}] already exists in global routes table", descriptor.Source.Key);
return;
}
RouteDescriptors.Add(descriptor);
@@ -53,14 +53,14 @@ namespace PepperDash.Essentials.Core
/// <returns>null if no RouteDescriptor for a destination exists</returns>
public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs destination)
{
Debug.LogMessage(LogEventLevel.Information, "Getting route descriptor for '{destination}'", destination?.Key ?? null);
Debug.LogMessage(LogEventLevel.Debug, "Getting route descriptor", destination);
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
}
public RouteDescriptor GetRouteDescriptorForDestinationAndInputPort(IRoutingInputs destination, string inputPortKey)
{
Debug.LogMessage(LogEventLevel.Information, "Getting route descriptor for '{destination}':'{inputPortKey}'", destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
Debug.LogMessage(LogEventLevel.Debug, "Getting route descriptor for {inputPortKey}", destination, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination && rd.InputPort != null && rd.InputPort.Key == inputPortKey);
}
@@ -70,7 +70,7 @@ namespace PepperDash.Essentials.Core
/// </summary>
public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination, string inputPortKey = "")
{
Debug.LogMessage(LogEventLevel.Information, "Removing route descriptor for '{destination}':'{inputPortKey}'", destination.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
Debug.LogMessage(LogEventLevel.Debug, "Removing route descriptor for {inputPortKey}", destination, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
var descr = string.IsNullOrEmpty(inputPortKey)
? GetRouteDescriptorForDestination(destination)
@@ -78,7 +78,7 @@ namespace PepperDash.Essentials.Core
if (descr != null)
RouteDescriptors.Remove(descr);
Debug.LogMessage(LogEventLevel.Information, "Found route descriptor {routeDescriptor}", destination, descr);
Debug.LogMessage(LogEventLevel.Debug, "Found route descriptor {routeDescriptor}", destination, descr);
return descr;
}

View File

@@ -38,10 +38,5 @@ namespace PepperDash.Essentials.Core
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"}";
}
}
}

View File

@@ -1,45 +0,0 @@
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);
}
}
}

View File

@@ -30,6 +30,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.20.66" />
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-451" />
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-424" />
</ItemGroup>
</Project>

View File

@@ -28,14 +28,14 @@ namespace PepperDash.Essentials.Devices.Common.SoftCodec
for(var i = 1; i <= props.OutputCount; i++)
{
var outputPort = new RoutingOutputPort($"output{i}", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null, this);
var outputPort = new RoutingOutputPort($"{Key}-output{i}", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null, this);
OutputPorts.Add(outputPort);
}
for(var i = 1; i<= props.ContentInputCount; i++)
{
var inputPort = new RoutingInputPort($"contentInput{i}", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, $"contentInput{i}", this);
var inputPort = new RoutingInputPort($"{Key}-contentInput{i}", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, $"contentInput{i}", this);
InputPorts.Add(inputPort);
}
@@ -47,7 +47,7 @@ namespace PepperDash.Essentials.Devices.Common.SoftCodec
for(var i = 1; i <=props.CameraInputCount; i++)
{
var cameraPort = new RoutingInputPort($"cameraInput{i}", eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, $"cameraInput{i}", this);
var cameraPort = new RoutingInputPort($"{Key}-cameraInput{i}", eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, $"cameraInput{i}", this);
InputPorts.Add(cameraPort);
}

View File

@@ -6,7 +6,7 @@
<PropertyGroup>
<RootNamespace>PepperDash.Essentials</RootNamespace>
<AssemblyName>PepperDashEssentials</AssemblyName>
<TargetFrameworks>net472</TargetFrameworks>
<TargetFrameworks>net472;net6</TargetFrameworks>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<OutputPath>bin\$(Configuration)\</OutputPath>
<Title>PepperDash Essentials</Title>
@@ -49,7 +49,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Crestron.SimplSharp.SDK.Program" Version="2.20.66" />
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-451" />
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-424" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PepperDash.Essentials.Core\PepperDash.Essentials.Core.csproj" />