Compare commits

..

8 Commits

Author SHA1 Message Date
jtalborough
64ba5a9f94 refactor: Update file paths for packaging commands 2024-04-26 12:23:06 -04:00
jtalborough
b9382216c1 ci: Install 7-Zip for packaging 2024-04-26 12:16:34 -04:00
jtalborough
d75d355dc7 ci: Add CPZ and CPLZ to NuGet Package 2024-04-26 12:11:42 -04:00
jtalborough
7277ccc687 style: Update NuGet package source and add NonInteractive 2024-04-26 12:06:41 -04:00
jtalborough
2e4601b5f9 style: update nuget add command order 2024-04-26 11:59:36 -04:00
jtalborough
fe1804f9c7 feat: Inject CPZ into NuGet Package 2024-04-26 10:28:27 -04:00
jtalborough
faa07e682f feat: Add PackageOutputPath to msbuild command 2024-04-26 10:06:56 -04:00
jtalborough
a1af0bf943 feature: include build in nuget package output 2024-04-24 12:08:13 -04:00
137 changed files with 2452 additions and 4613 deletions

View File

@@ -66,63 +66,23 @@ jobs:
# Build the solutions in the docker image # 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 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)"
}
}
- name: Create tag for non-rc builds - name: Create tag for non-rc builds
if: contains(steps.setVersion.outputs.version, 'alpha') if: contains(steps.setVersion.outputs.version, 'alpha')
run: | run: |
git tag ${{ steps.setVersion.outputs.version }} git tag ${{ steps.setVersion.outputs.version }}
git push --tags origin git push --tags origin
# Create the release on the source repo # Create the release on the source repo
- name: Install 7-Zip
run: choco install 7zip.install -y
- name: Add CPZ and CPLZ to NuGet Package
run: |
7z a PepperDash.Essentials.${{ steps.setVersion.outputs.version }}.nupkg output\**\*.cpz
7z a PepperDash.Essentials.${{ steps.setVersion.outputs.version }}.nupkg output\**\*.cplz
- name: Create Release - name: Create Release
id: create_release id: create_release
# if: contains(steps.setVersion.outputs.version,'-rc-') ||
# contains(steps.setVersion.outputs.version,'-hotfix-') ||
# contains(steps.setVersion.outputs.version, '-beta-')
uses: ncipollo/release-action@v1 uses: ncipollo/release-action@v1
with: with:
artifacts: 'output\**\*.*(cpz|cplz)' artifacts: 'output\**\*.*(cpz|cplz)'

View File

@@ -37,8 +37,6 @@ jobs:
run: nuget restore .\$($Env:SOLUTION_FILE).sln run: nuget restore .\$($Env:SOLUTION_FILE).sln
- 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 run: msbuild .\$($Env:SOLUTION_FILE).sln /p:Platform="Any CPU" /p:Configuration="Debug" /p:Version="${{ steps.setVersion.outputs.version }}" -m
- name: Pack Solution
run: dotnet pack .\$($Env:SOLUTION_FILE).sln --configuration $env:BUILD_TYPE --output ./output /p:Version="${{ steps.setVersion.outputs.version }}"
- name: Upload Release - name: Upload Release
id: create_release id: create_release
uses: ncipollo/release-action@v1 uses: ncipollo/release-action@v1

1
.gitignore vendored
View File

@@ -391,4 +391,3 @@ FodyWeavers.xsd
essentials-framework/Essentials Interfaces/PepperDash_Essentials_Interfaces/PepperDash_Essentials_Interfaces.csproj essentials-framework/Essentials Interfaces/PepperDash_Essentials_Interfaces/PepperDash_Essentials_Interfaces.csproj
.DS_Store .DS_Store
/._PepperDash.Essentials.sln /._PepperDash.Essentials.sln
.vscode/settings.json

View File

@@ -6,7 +6,6 @@
Provided under MIT license Provided under MIT license
## Overview ## 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. 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> <Project>
<ItemGroup> <ItemGroup>
<!-- Include CPZ files from multiple possible locations --> <None Include="$(PackageOutputPath)\$(AssemblyName)\*.cpz" Condition="$(ProjectType) == 'Program'">
<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'">
<Pack>true</Pack> <Pack>true</Pack>
<PackagePath>build;</PackagePath> <PackagePath>build;</PackagePath>
</None> </None>
@@ -23,94 +10,17 @@
</None> </None>
</ItemGroup> </ItemGroup>
<Target Name="Create CPLZ" AfterTargets="Build; AfterRebuild" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''"> <Target Name="Create CPLZ" AfterTargets="Build; AfterRebuild" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''">
<Message Text="Creating CPLZ $(TargetDir)" Importance="high" /> <Message Text="Creating CPLZ $(TargetDir)"></Message>
<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" />
<MakeDir Directories="$(PackageOutputPath)" Condition="!Exists($(PackageOutputPath))" /> <MakeDir Directories="$(PackageOutputPath)" Condition="!Exists($(PackageOutputPath))" />
<MakeDir Directories="$(PackageOutputPath)\$(AssemblyName)" Condition="!Exists('$(PackageOutputPath)\$(AssemblyName)')" /> <MakeDir Directories="$(PackageOutputPath)\$(AssemblyName)" Condition="!Exists('$(PackageOutputPath)\$(AssemblyName)')" />
<ZipDirectory SourceDirectory="$(TargetDir)" DestinationFile="$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cplz" Overwrite="true"/> <ZipDirectory SourceDirectory="$(TargetDir)" DestinationFile="$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cplz" Overwrite="true"/>
</Target> </Target>
<Target Name="Debug Variables" BeforeTargets="Build"> <Target Name="Copy CPZ NET6" AfterTargets="SimplSharpPostProcess" Condition="($(ProjectType) == 'Program' And ( '$(TargetFramework)' == 'net6.0' ) Or ( '$(TargetFramework)' == 'net8.0' ))">
<Message Text="================ Debug Variables ================" Importance="high" /> <Message Text="Copying CPZ"></Message>
<Message Text="ProjectType: '$(ProjectType)'" Importance="high" /> <Move SourceFiles="$(TargetDir)$(TargetName).cpz" DestinationFiles="$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cpz" />
<Message Text="TargetFramework: '$(TargetFramework)'" Importance="high" />
<Message Text="TargetDir: '$(TargetDir)'" Importance="high" />
<Message Text="===============================================" Importance="high" />
</Target> </Target>
<Target Name="Copy CPZ NET472" <Target Name="Copy CPZ NET47" AfterTargets="SimplSharpPostProcess47" Condition="($(ProjectType) == 'Program' And ( '$(TargetFramework)' != 'net6.0' ) And ( '$(TargetFramework)' != 'net8.0' ))">
AfterTargets="Build" <Message Text="Copying CPZ"></Message>
DependsOnTargets="BuildDependencies" <Move SourceFiles="$(TargetDir)$(TargetName).cpz" DestinationFiles="$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cpz" />
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> </Target>
<Target Name="Copy CPZ NET6" </Project>
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>

View File

@@ -3,7 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Crestron.SimplSharp; using Crestron.SimplSharp;
using System.Reflection; using Crestron.SimplSharp.Reflection;
using Crestron.SimplSharpPro; using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.EthernetCommunication; using Crestron.SimplSharpPro.EthernetCommunication;
@@ -18,6 +18,81 @@ using Serilog.Events;
namespace PepperDash.Essentials.Core.Bridges namespace PepperDash.Essentials.Core.Bridges
{ {
/// <summary>
/// Helper methods for bridges
/// </summary>
public static class BridgeHelper
{
public static void PrintJoinMap(string command)
{
var targets = command.Split(' ');
var bridgeKey = targets[0].Trim();
var bridge = DeviceManager.GetDeviceForKey(bridgeKey) as EiscApiAdvanced;
if (bridge == null)
{
Debug.LogMessage(LogEventLevel.Information, "Unable to find advanced bridge with key: '{0}'", bridgeKey);
return;
}
if (targets.Length > 1)
{
var deviceKey = targets[1].Trim();
if (string.IsNullOrEmpty(deviceKey)) return;
bridge.PrintJoinMapForDevice(deviceKey);
}
else
{
bridge.PrintJoinMaps();
}
}
public static void JoinmapMarkdown(string command)
{
var targets = command.Split(' ');
var bridgeKey = targets[0].Trim();
var bridge = DeviceManager.GetDeviceForKey(bridgeKey) as EiscApiAdvanced;
if (bridge == null)
{
Debug.LogMessage(LogEventLevel.Information, "Unable to find advanced bridge with key: '{0}'", bridgeKey);
return;
}
if (targets.Length > 1)
{
var deviceKey = targets[1].Trim();
if (string.IsNullOrEmpty(deviceKey)) return;
bridge.MarkdownJoinMapForDevice(deviceKey, bridgeKey);
}
else
{
bridge.MarkdownForBridge(bridgeKey);
}
}
}
/// <summary>
/// Base class for all bridge class variants
/// </summary>
public class BridgeBase : EssentialsDevice
{
public BridgeApi Api { get; protected set; }
public BridgeBase(string key) :
base(key)
{
}
}
/// <summary> /// <summary>
/// Base class for bridge API variants /// Base class for bridge API variants
/// </summary> /// </summary>
@@ -93,15 +168,19 @@ namespace PepperDash.Essentials.Core.Bridges
Debug.LogMessage(LogEventLevel.Debug, this, "Linking Device: '{0}'", device.Key); Debug.LogMessage(LogEventLevel.Debug, this, "Linking Device: '{0}'", device.Key);
if (device is IBridgeAdvanced bridge) if (!typeof(IBridgeAdvanced).IsAssignableFrom(device.GetType().GetCType()))
{ {
bridge.LinkToApi(Eisc, d.JoinStart, d.JoinMapKey, this); Debug.LogMessage(LogEventLevel.Information, this,
"{0} is not compatible with this bridge type. Please use 'eiscapi' instead, or updae the device.",
device.Key);
continue; continue;
} }
Debug.LogMessage(LogEventLevel.Information, this, var bridge = device as IBridgeAdvanced;
"{0} is not compatible with this bridge type. Please use 'eiscapi' instead, or updae the device.", if (bridge != null)
device.Key); {
bridge.LinkToApi(Eisc, d.JoinStart, d.JoinMapKey, this);
}
} }
} }
@@ -170,11 +249,11 @@ namespace PepperDash.Essentials.Core.Bridges
/// </summary> /// </summary>
public virtual void PrintJoinMaps() public virtual void PrintJoinMaps()
{ {
CrestronConsole.ConsoleCommandResponse("Join Maps for EISC IPID: {0}\r\n", Eisc.ID.ToString("X")); Debug.LogMessage(LogEventLevel.Information, this, "Join Maps for EISC IPID: {0}", Eisc.ID.ToString("X"));
foreach (var joinMap in JoinMaps) foreach (var joinMap in JoinMaps)
{ {
CrestronConsole.ConsoleCommandResponse("Join map for device '{0}':", joinMap.Key); Debug.LogMessage(LogEventLevel.Information, "Join map for device '{0}':", joinMap.Key);
joinMap.Value.PrintJoinMapInfo(); joinMap.Value.PrintJoinMapInfo();
} }
} }

View File

@@ -1,66 +0,0 @@
using PepperDash.Core;
using Serilog.Events;
//using PepperDash.Essentials.Devices.Common.Cameras;
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Helper methods for bridges
/// </summary>
public static class BridgeHelper
{
public static void PrintJoinMap(string command)
{
var targets = command.Split(' ');
var bridgeKey = targets[0].Trim();
if (!(DeviceManager.GetDeviceForKey(bridgeKey) is EiscApiAdvanced bridge))
{
Debug.LogMessage(LogEventLevel.Information, "Unable to find advanced bridge with key: '{0}'", bridgeKey);
return;
}
if (targets.Length > 1)
{
var deviceKey = targets[1].Trim();
if (string.IsNullOrEmpty(deviceKey)) return;
bridge.PrintJoinMapForDevice(deviceKey);
}
else
{
bridge.PrintJoinMaps();
}
}
public static void JoinmapMarkdown(string command)
{
var targets = command.Split(' ');
var bridgeKey = targets[0].Trim();
var bridge = DeviceManager.GetDeviceForKey(bridgeKey) as EiscApiAdvanced;
if (bridge == null)
{
Debug.LogMessage(LogEventLevel.Information, "Unable to find advanced bridge with key: '{0}'", bridgeKey);
return;
}
if (targets.Length > 1)
{
var deviceKey = targets[1].Trim();
if (string.IsNullOrEmpty(deviceKey)) return;
bridge.MarkdownJoinMapForDevice(deviceKey, bridgeKey);
}
else
{
bridge.MarkdownForBridge(bridgeKey);
}
}
}
}

View File

@@ -93,7 +93,6 @@ namespace PepperDash.Essentials.Core
void OnDataReceived(string s) void OnDataReceived(string s)
{ {
var eventSubscribed = false;
var bytesHandler = BytesReceived; var bytesHandler = BytesReceived;
if (bytesHandler != null) if (bytesHandler != null)
@@ -102,7 +101,6 @@ namespace PepperDash.Essentials.Core
if (StreamDebugging.RxStreamDebuggingIsEnabled) if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", ComTextHelper.GetEscapedText(bytes)); Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", ComTextHelper.GetEscapedText(bytes));
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes)); bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
eventSubscribed = true;
} }
var textHandler = TextReceived; var textHandler = TextReceived;
if (textHandler != null) if (textHandler != null)
@@ -110,10 +108,7 @@ namespace PepperDash.Essentials.Core
if (StreamDebugging.RxStreamDebuggingIsEnabled) if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", s); Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", s);
textHandler(this, new GenericCommMethodReceiveTextArgs(s)); textHandler(this, new GenericCommMethodReceiveTextArgs(s));
eventSubscribed = true;
} }
if(!eventSubscribed) Debug.LogMessage(LogEventLevel.Warning, this, "Received data but no handler is registered");
} }
public override bool Deactivate() public override bool Deactivate()

View File

@@ -23,12 +23,12 @@ namespace PepperDash.Essentials.Core
{ {
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{ {
if (objectType == typeof(ComPort.ComPortSpec?)) if (objectType == typeof(ComPort.ComPortSpec))
{ {
var newSer = new JsonSerializer(); var newSer = new JsonSerializer();
newSer.Converters.Add(new ComSpecPropsJsonConverter()); newSer.Converters.Add(new ComSpecPropsJsonConverter());
newSer.ObjectCreationHandling = ObjectCreationHandling.Replace; newSer.ObjectCreationHandling = ObjectCreationHandling.Replace;
return newSer.Deserialize<ComPort.ComPortSpec?>(reader); return newSer.Deserialize<ComPort.ComPortSpec>(reader);
} }
return null; return null;
} }
@@ -38,7 +38,7 @@ namespace PepperDash.Essentials.Core
/// </summary> /// </summary>
public override bool CanConvert(Type objectType) public override bool CanConvert(Type objectType)
{ {
return objectType == typeof(ComPort.ComPortSpec?); return objectType == typeof(ComPort.ComPortSpec);
} }
public override bool CanRead { get { return true; } } public override bool CanRead { get { return true; } }

View File

@@ -51,7 +51,7 @@ namespace PepperDash.Essentials.Core
switch (controlConfig.Method) switch (controlConfig.Method)
{ {
case eControlMethod.Com: case eControlMethod.Com:
comm = new ComPortController(deviceConfig.Key + "-com", GetComPort, controlConfig.ComParams.Value, controlConfig); comm = new ComPortController(deviceConfig.Key + "-com", GetComPort, controlConfig.ComParams, controlConfig);
break; break;
case eControlMethod.Cec: case eControlMethod.Cec:
comm = new CecPortController(deviceConfig.Key + "-cec", GetCecPort, controlConfig); comm = new CecPortController(deviceConfig.Key + "-cec", GetCecPort, controlConfig);
@@ -115,7 +115,7 @@ namespace PepperDash.Essentials.Core
var comPar = config.ComParams; var comPar = config.ComParams;
var dev = GetIComPortsDeviceFromManagedDevice(config.ControlPortDevKey); var dev = GetIComPortsDeviceFromManagedDevice(config.ControlPortDevKey);
if (dev != null && config.ControlPortNumber <= dev.NumberOfComPorts) if (dev != null && config.ControlPortNumber <= dev.NumberOfComPorts)
return dev.ComPorts[config.ControlPortNumber.Value]; return dev.ComPorts[config.ControlPortNumber];
Debug.LogMessage(LogEventLevel.Information, "GetComPort: Device '{0}' does not have com port {1}", config.ControlPortDevKey, config.ControlPortNumber); Debug.LogMessage(LogEventLevel.Information, "GetComPort: Device '{0}' does not have com port {1}", config.ControlPortDevKey, config.ControlPortNumber);
return null; return null;
} }
@@ -201,26 +201,23 @@ namespace PepperDash.Essentials.Core
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public class EssentialsControlPropertiesConfig : public class EssentialsControlPropertiesConfig :
ControlPropertiesConfig PepperDash.Core.ControlPropertiesConfig
{ {
[JsonProperty("comParams", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(ComSpecJsonConverter))] [JsonConverter(typeof(ComSpecJsonConverter))]
public ComPort.ComPortSpec? ComParams { get; set; } public ComPort.ComPortSpec ComParams { get; set; }
[JsonProperty("cresnetId", NullValueHandling = NullValueHandling.Ignore)] public string CresnetId { get; set; }
public string CresnetId { get; set; }
/// <summary> /// <summary>
/// Attempts to provide uint conversion of string CresnetId /// Attempts to provide uint conversion of string CresnetId
/// </summary> /// </summary>
[JsonIgnore]
public uint CresnetIdInt public uint CresnetIdInt
{ {
get get
{ {
try try
{ {
return Convert.ToUInt32(CresnetId, 16); return Convert.ToUInt32(CresnetId, 16);
} }
@@ -231,13 +228,11 @@ namespace PepperDash.Essentials.Core
} }
} }
[JsonProperty("infinetId", NullValueHandling = NullValueHandling.Ignore)]
public string InfinetId { get; set; } public string InfinetId { get; set; }
/// <summary> /// <summary>
/// Attepmts to provide uiont conversion of string InifinetId /// Attepmts to provide uiont conversion of string InifinetId
/// </summary> /// </summary>
[JsonIgnore]
public uint InfinetIdInt public uint InfinetIdInt
{ {
get get

View File

@@ -1,10 +1,15 @@
using Crestron.SimplSharp.Net.Http; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Net.Http;
using PepperDash.Core; using PepperDash.Core;
using System; using PepperDash.Core.DebugThings;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
[Obsolete("Please use the builtin HttpClient class instead: https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelines")] [Obsolete("Please use the builtin HttpClient class instead: https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelines")]
public class GenericHttpClient : Device, IBasicCommunication public class GenericHttpClient : Device, IBasicCommunication
{ {
public HttpClient Client; public HttpClient Client;

View File

@@ -1,20 +0,0 @@
using Crestron.SimplSharpPro;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core.Config
{
public class AudioControlPointListItem
{
[JsonProperty("levelControls")]
public Dictionary<string, LevelControlListItem> LevelControls { get; set; } = new Dictionary<string, LevelControlListItem>();
[JsonProperty("presets")]
public Dictionary<string, PresetListItem> Presets { get; set; } = new Dictionary<string, PresetListItem>();
}
}

View File

@@ -5,7 +5,6 @@ using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using PepperDash.Essentials.Core.Devices;
namespace PepperDash.Essentials.Core.Config namespace PepperDash.Essentials.Core.Config
{ {
@@ -24,13 +23,7 @@ namespace PepperDash.Essentials.Core.Config
public Dictionary<string, Dictionary<string, SourceListItem>> SourceLists { get; set; } public Dictionary<string, Dictionary<string, SourceListItem>> SourceLists { get; set; }
[JsonProperty("destinationLists")] [JsonProperty("destinationLists")]
public Dictionary<string, Dictionary<string, DestinationListItem>> DestinationLists { get; set; } public Dictionary<string, Dictionary<string,DestinationListItem>> DestinationLists { get; set; }
[JsonProperty("audioControlPointLists")]
public Dictionary<string, AudioControlPointListItem> AudioControlPointLists { get; set; }
[JsonProperty("cameraLists")]
public Dictionary<string, Dictionary<string, CameraListItem>> CameraLists { get; set; }
[JsonProperty("tieLines")] [JsonProperty("tieLines")]
public List<TieLineConfig> TieLines { get; set; } public List<TieLineConfig> TieLines { get; set; }
@@ -44,8 +37,6 @@ namespace PepperDash.Essentials.Core.Config
Devices = new List<DeviceConfig>(); Devices = new List<DeviceConfig>();
SourceLists = new Dictionary<string, Dictionary<string, SourceListItem>>(); SourceLists = new Dictionary<string, Dictionary<string, SourceListItem>>();
DestinationLists = new Dictionary<string, Dictionary<string, DestinationListItem>>(); DestinationLists = new Dictionary<string, Dictionary<string, DestinationListItem>>();
AudioControlPointLists = new Dictionary<string, AudioControlPointListItem>();
CameraLists = new Dictionary<string, Dictionary<string, CameraListItem>>();
TieLines = new List<TieLineConfig>(); TieLines = new List<TieLineConfig>();
JoinMaps = new Dictionary<string, JObject>(); JoinMaps = new Dictionary<string, JObject>();
} }
@@ -55,7 +46,7 @@ namespace PepperDash.Essentials.Core.Config
/// </summary> /// </summary>
public Dictionary<string, SourceListItem> GetSourceListForKey(string key) public Dictionary<string, SourceListItem> GetSourceListForKey(string key)
{ {
if (SourceLists == null || string.IsNullOrEmpty(key) || !SourceLists.ContainsKey(key)) if (string.IsNullOrEmpty(key) || !SourceLists.ContainsKey(key))
return null; return null;
return SourceLists[key]; return SourceLists[key];
@@ -64,41 +55,17 @@ namespace PepperDash.Essentials.Core.Config
/// <summary> /// <summary>
/// Retrieves a DestinationListItem based on the key /// Retrieves a DestinationListItem based on the key
/// </summary> /// </summary>
/// <param name="key">key of the list to retrieve</param> /// <param name="key">key of the item to retrieve</param>
/// <returns>DestinationList if the key exists, null otherwise</returns> /// <returns>DestinationListItem if the key exists, null otherwise</returns>
public Dictionary<string, DestinationListItem> GetDestinationListForKey(string key) public Dictionary<string, DestinationListItem> GetDestinationListForKey(string key)
{ {
if (DestinationLists == null || string.IsNullOrEmpty(key) || !DestinationLists.ContainsKey(key)) if (string.IsNullOrEmpty(key) || !DestinationLists.ContainsKey(key))
{ {
return null; return null;
} }
return DestinationLists[key]; return DestinationLists[key];
} }
/// <summary>
/// Retrieves a AudioControlPointList based on the key
/// </summary>
/// <param name="key">key of the list to retrieve</param>
/// <returns>AudioControlPointList if the key exists, null otherwise</returns>
public AudioControlPointListItem GetAudioControlPointListForKey(string key)
{
if (AudioControlPointLists == null || string.IsNullOrEmpty(key) || !AudioControlPointLists.ContainsKey(key))
return null;
return AudioControlPointLists[key];
}
/// <summary>
/// Checks CameraLists for a given list and returns it if found. Otherwise, returns null
/// </summary>
public Dictionary<string, CameraListItem> GetCameraListForKey(string key)
{
if (CameraLists == null || string.IsNullOrEmpty(key) || !CameraLists.ContainsKey(key))
return null;
return CameraLists[key];
}
/// <summary> /// <summary>
/// Checks Devices for an item with a Key that matches and returns it if found. Otherwise, retunes null /// Checks Devices for an item with a Key that matches and returns it if found. Otherwise, retunes null

View File

@@ -31,17 +31,9 @@ namespace PepperDash.Essentials.Core.Config
if (string.IsNullOrEmpty(SystemUrl)) if (string.IsNullOrEmpty(SystemUrl))
return "missing url"; return "missing url";
if (SystemUrl.Contains("#")) var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*");
{ string uuid = result.Groups[1].Value;
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*"); return uuid;
string uuid = result.Groups[1].Value;
return uuid;
} else
{
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/.*");
string uuid = result.Groups[1].Value;
return uuid;
}
} }
} }
@@ -52,18 +44,10 @@ namespace PepperDash.Essentials.Core.Config
{ {
if (string.IsNullOrEmpty(TemplateUrl)) if (string.IsNullOrEmpty(TemplateUrl))
return "missing template url"; return "missing template url";
if (TemplateUrl.Contains("#")) var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*");
{ string uuid = result.Groups[1].Value;
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*"); return uuid;
string uuid = result.Groups[1].Value;
return uuid;
} else
{
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/system-templates\/(.*)\/system-template-versions\/(.*)\/.*");
string uuid = result.Groups[2].Value;
return uuid;
}
} }
} }

View File

@@ -1,16 +1,18 @@
using Crestron.SimplSharp;
using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Reflection;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Core.Config namespace PepperDash.Essentials.Core.Config
{ {
/// <summary> /// <summary>
/// Represents the info section of a Config file /// Represents the info section of a Config file
/// </summary> /// </summary>
public class InfoConfig public class InfoConfig
{ {
[JsonProperty("name")] [JsonProperty("name")]
public string Name { get; set; } public string Name { get; set; }

View File

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

@@ -15,22 +15,10 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
/// <example> /// <example>
/// See MockDisplay for example implemntation /// See MockDisplay for example implemntation
/// </example> /// </example>
[Obsolete("Use IHasInputs<T> instead. Will be removed for 2.0 release")] public interface IHasInputs<TKey, TSelector>: IKeyName
public interface IHasInputs<T, TSelector>: IKeyName
{ {
ISelectableItems<T> Inputs { get; } ISelectableItems<TKey> Inputs { get; }
}
void SetInput(TSelector selector);
/// <summary>
/// Describes a device that has selectable inputs
/// </summary>
/// <typeparam name="T">the type to use as the key for each input item. Most likely an enum or string</typeparam>\
/// <example>
/// See MockDisplay for example implemntation
/// </example>
public interface IHasInputs<T> : IKeyName
{
ISelectableItems<T> Inputs { get; }
} }
} }

View File

@@ -1,18 +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 IHumiditySensor
{
/// <summary>
/// Reports the relative humidity level. Level ranging from 0 to 100 (for 0% to 100%
/// RH). EventIds: HumidityFeedbackFeedbackEventId will trigger to indicate change.
/// </summary>
IntFeedback HumidityFeedback { get; }
}
}

View File

@@ -44,10 +44,7 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
void AddDeviceMessenger(IMobileControlMessenger messenger); void AddDeviceMessenger(IMobileControlMessenger messenger);
bool CheckForDeviceMessenger(string key); bool CheckForDeviceMessenger(string key);
}
IMobileControlRoomMessenger GetRoomMessenger(string key);
}
/// <summary> /// <summary>
/// Describes a mobile control messenger /// Describes a mobile control messenger
@@ -109,11 +106,4 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
Action<string,string, JToken> Action { get; } Action<string,string, JToken> Action { get; }
} }
public interface IMobileControlTouchpanelController : IKeyed
{
string DefaultRoomKey { get; }
void SetAppUrl(string url);
bool UseDirectServer { get; }
bool ZoomRoomController { get; }
}
} }

View File

@@ -1,27 +0,0 @@
using Crestron.SimplSharpPro.DeviceSupport;
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Defines a class that has warm up and cool down
/// </summary>
public interface IProjectorScreenLiftControl
{
void Raise();
void Lower();
BoolFeedback IsInUpPosition { get; }
bool InUpPosition { get; }
event EventHandler<EventArgs> PositionChanged;
string DisplayDeviceKey { get; }
eScreenLiftControlType Type { get; } // screen/lift
}
public enum eScreenLiftControlType
{
lift,
screen
}
}

View File

@@ -17,6 +17,6 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
Dictionary<TKey, ISelectableItem> Items { get; set; } Dictionary<TKey, ISelectableItem> Items { get; set; }
[JsonProperty("currentItem")] [JsonProperty("currentItem")]
TKey CurrentItem { get; set; } string CurrentItem { get; set; }
} }
} }

View File

@@ -1,22 +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 ITemperatureSensor
{
/// <summary>
/// The values will range from -400 to +1760 (for -40° to +176° F) or -400 to +800
/// (for -40° to +80° C)in tenths of a degree.
/// </summary>
IntFeedback TemperatureFeedback { get; }
BoolFeedback TemperatureInCFeedback { get; }
void SetTemperatureFormat(bool setToC);
}
}

View File

@@ -1,42 +0,0 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core
{
public abstract class AudioControlListItemBase
{
/// <summary>
/// Key of the parent device in the DeviceManager
/// </summary>
[JsonProperty("parentDeviceKey")]
public string ParentDeviceKey { get; set; }
/// <summary>
/// Optional key of the item in the parent device
/// </summary>
[JsonProperty("itemKey")]
public string ItemKey { get; set; }
/// <summary>
/// A name that will override the items's name on the UI
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// Indicates if the item should be included in the user accessible list
/// </summary>
[JsonProperty("includeInUserList")]
public bool IncludeInUserList { get; set; }
/// <summary>
/// Used to specify the order of the items in the source list when displayed
/// </summary>
[JsonProperty("order")]
public int Order { get; set; }
}
}

View File

@@ -1,76 +0,0 @@
using Newtonsoft.Json;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
public class CameraListItem
{
[JsonProperty("deviceKey")]
public string DeviceKey { get; set; }
/// <summary>
/// Returns the source Device for this, if it exists in DeviceManager
/// </summary>
[JsonIgnore]
public Device CameraDevice
{
get
{
if (_cameraDevice == null)
_cameraDevice = DeviceManager.GetDeviceForKey(DeviceKey) as Device;
return _cameraDevice;
}
}
Device _cameraDevice;
/// <summary>
/// Gets either the source's Name or this AlternateName property, if
/// defined. If source doesn't exist, returns "Missing source"
/// </summary>
[JsonProperty("preferredName")]
public string PreferredName
{
get
{
if (string.IsNullOrEmpty(Name))
{
if (CameraDevice == null)
return "---";
return CameraDevice.Name;
}
return Name;
}
}
/// <summary>
/// A name that will override the source's name on the UI
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// Specifies and icon for the source list item
/// </summary>
[JsonProperty("icon")]
public string Icon { get; set; }
/// <summary>
/// Alternate icon
/// </summary>
[JsonProperty("altIcon", NullValueHandling = NullValueHandling.Ignore)]
public string AltIcon { get; set; }
/// <summary>
/// Indicates if the item should be included in the user facing list
/// </summary>
[JsonProperty("includeInUserList")]
public bool IncludeInUserList { get; set; }
/// <summary>
/// Used to specify the order of the items in the source list when displayed
/// </summary>
[JsonProperty("order")]
public int Order { get; set; }
}
}

View File

@@ -1,203 +1,131 @@
using Crestron.SimplSharp;
using Newtonsoft.Json;
using PepperDash.Core;
using Serilog.Events;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Text;
using System.Threading.Tasks; using Crestron.SimplSharp;
using Crestron.SimplSharp.Reflection;
using Newtonsoft.Json;
using PepperDash.Core;
using Serilog.Events;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
public class DeviceJsonApi public class DeviceJsonApi
{ {
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="json"></param> /// <param name="json"></param>
public static void DoDeviceActionWithJson(string json) public static void DoDeviceActionWithJson(string json)
{ {
if (String.IsNullOrEmpty(json)) if (String.IsNullOrEmpty(json))
{ {
CrestronConsole.ConsoleCommandResponse( CrestronConsole.ConsoleCommandResponse(
"Please provide a JSON object matching the format {\"deviceKey\":\"myDevice\", \"methodName\":\"someMethod\", \"params\": [\"param1\", true]}.\r\nIf the method has no parameters, the \"params\" object may be omitted."); "Please provide a JSON object matching the format {\"deviceKey\":\"myDevice\", \"methodName\":\"someMethod\", \"params\": [\"param1\", true]}.\r\nIf the method has no parameters, the \"params\" object may be omitted.");
return; return;
} }
try try
{ {
var action = JsonConvert.DeserializeObject<DeviceActionWrapper>(json); var action = JsonConvert.DeserializeObject<DeviceActionWrapper>(json);
DoDeviceAction(action); DoDeviceAction(action);
} }
catch (Exception) catch (Exception ex)
{ {
CrestronConsole.ConsoleCommandResponse("Incorrect format for JSON. Please check that the format matches {\"deviceKey\":\"myDevice\", \"methodName\":\"someMethod\", \"params\": [\"param1\", true]}"); CrestronConsole.ConsoleCommandResponse("Incorrect format for JSON. Please check that the format matches {\"deviceKey\":\"myDevice\", \"methodName\":\"someMethod\", \"params\": [\"param1\", true]}");
} }
} }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="action"></param> /// <param name="action"></param>
public static void DoDeviceAction(DeviceActionWrapper action) public static void DoDeviceAction(DeviceActionWrapper action)
{ {
var key = action.DeviceKey; var key = action.DeviceKey;
var obj = FindObjectOnPath(key); var obj = FindObjectOnPath(key);
if (obj == null) if (obj == null)
{ {
CrestronConsole.ConsoleCommandResponse("Unable to find object at path {0}", key); CrestronConsole.ConsoleCommandResponse("Unable to find object at path {0}", key);
return; return;
} }
if (action.Params == null) if (action.Params == null)
{ {
//no params, so setting action.Params to empty array //no params, so setting action.Params to empty array
action.Params = new object[0]; action.Params = new object[0];
} }
Type t = obj.GetType(); CType t = obj.GetType();
try try
{ {
var methods = t.GetMethods().Where(m => m.Name == action.MethodName).ToList(); var methods = t.GetMethods().Where(m => m.Name == action.MethodName).ToList();
var method = methods.Count == 1 ? methods[0] : methods.FirstOrDefault(m => m.GetParameters().Length == action.Params.Length); var method = methods.Count == 1 ? methods[0] : methods.FirstOrDefault(m => m.GetParameters().Length == action.Params.Length);
if (method == null) if (method == null)
{ {
CrestronConsole.ConsoleCommandResponse( CrestronConsole.ConsoleCommandResponse(
"Unable to find method with name {0} and that matches parameters {1}", action.MethodName, "Unable to find method with name {0} and that matches parameters {1}", action.MethodName,
action.Params); action.Params);
return; return;
} }
var mParams = method.GetParameters(); var mParams = method.GetParameters();
var convertedParams = mParams var convertedParams = mParams
.Select((p, i) => ConvertType(action.Params[i], p.ParameterType)) .Select((p, i) => ConvertType(action.Params[i], p.ParameterType))
.ToArray(); .ToArray();
method.Invoke(obj, convertedParams);
Task.Run(() => CrestronConsole.ConsoleCommandResponse("Method {0} successfully called on device {1}", method.Name,
{ action.DeviceKey);
try }
{ catch (Exception ex)
Debug.LogMessage(LogEventLevel.Verbose, "Calling method {methodName} on device {deviceKey}", null, method.Name, action.DeviceKey); {
method.Invoke(obj, convertedParams); CrestronConsole.ConsoleCommandResponse("Unable to call method with name {0}. {1}", action.MethodName,
} ex.Message);}
catch (Exception e) }
{
Debug.LogMessage(e, "Error invoking method {methodName} on device {deviceKey}", null, method.Name, action.DeviceKey);
}
});
CrestronConsole.ConsoleCommandResponse("Method {0} successfully called on device {1}", method.Name, private static object ConvertType(object value, Type conversionType)
action.DeviceKey); {
} if (!conversionType.IsEnum)
catch (Exception ex) {
{ return Convert.ChangeType(value, conversionType, System.Globalization.CultureInfo.InvariantCulture);
CrestronConsole.ConsoleCommandResponse("Unable to call method with name {0}. {1}", action.MethodName, }
ex.Message);
}
}
public static async Task DoDeviceActionAsync(DeviceActionWrapper action) var stringValue = Convert.ToString(value);
{
var key = action.DeviceKey;
var obj = FindObjectOnPath(key);
if (obj == null)
{
Debug.LogMessage(LogEventLevel.Warning, "Unable to find object at path {deviceKey}", null, key);
return;
}
if (action.Params == null) if (String.IsNullOrEmpty(stringValue))
{ {
//no params, so setting action.Params to empty array throw new InvalidCastException(
action.Params = new object[0]; String.Format("{0} cannot be converted to a string prior to conversion to enum"));
} }
return Enum.Parse(conversionType, stringValue, true);
}
Type t = obj.GetType(); /// <summary>
try /// Gets the properties on a device
{ /// </summary>
var methods = t.GetMethods().Where(m => m.Name == action.MethodName).ToList(); /// <param name="key"></param>
/// <returns></returns>
public static string GetProperties(string deviceObjectPath)
{
var obj = FindObjectOnPath(deviceObjectPath);
if (obj == null)
return "{ \"error\":\"No Device\"}";
var method = methods.Count == 1 ? methods[0] : methods.FirstOrDefault(m => m.GetParameters().Length == action.Params.Length); CType t = obj.GetType();
// get the properties and set them into a new collection of NameType wrappers
if (method == null) var props = t.GetProperties().Select(p => new PropertyNameType(p, obj));
{ return JsonConvert.SerializeObject(props, Formatting.Indented);
Debug.LogMessage(LogEventLevel.Warning, }
"Unable to find method with name {methodName} and that matches parameters {@parameters}", null, action.MethodName,
action.Params);
return;
}
var mParams = method.GetParameters();
var convertedParams = mParams
.Select((p, i) => ConvertType(action.Params[i], p.ParameterType))
.ToArray();
try
{
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)
{
await task;
}
// If the method returns a Task<T>, await it
else if (result != null && result.GetType().IsGenericType && result.GetType().GetGenericTypeDefinition() == typeof(Task<>))
{
await (Task)result;
}
}
catch (Exception e)
{
Debug.LogMessage(e, "Error invoking method {methodName} on device {deviceKey}", null, method.Name, action.DeviceKey);
}
}
catch (Exception ex)
{
Debug.LogMessage(ex, "Unable to call method with name {methodName} with {@parameters}", null, action.MethodName, action.Params);
}
}
private static object ConvertType(object value, Type conversionType)
{
if (!conversionType.IsEnum)
{
return Convert.ChangeType(value, conversionType, System.Globalization.CultureInfo.InvariantCulture);
}
var stringValue = Convert.ToString(value);
if (String.IsNullOrEmpty(stringValue))
{
throw new InvalidCastException(
String.Format("{0} cannot be converted to a string prior to conversion to enum"));
}
return Enum.Parse(conversionType, stringValue, true);
}
/// <summary>
/// Gets the properties on a device
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static string GetProperties(string deviceObjectPath)
{
var obj = FindObjectOnPath(deviceObjectPath);
if (obj == null)
return "{ \"error\":\"No Device\"}";
Type t = obj.GetType();
// get the properties and set them into a new collection of NameType wrappers
var props = t.GetProperties().Select(p => new PropertyNameType(p, obj));
return JsonConvert.SerializeObject(props, Formatting.Indented);
}
/// <summary> /// <summary>
/// Gets a property from a device path by name /// Gets a property from a device path by name
@@ -208,10 +136,10 @@ namespace PepperDash.Essentials.Core
public static object GetPropertyByName(string deviceObjectPath, string propertyName) public static object GetPropertyByName(string deviceObjectPath, string propertyName)
{ {
var dev = FindObjectOnPath(deviceObjectPath); var dev = FindObjectOnPath(deviceObjectPath);
if (dev == null) if(dev == null)
return "{ \"error\":\"No Device\"}"; return "{ \"error\":\"No Device\"}";
object prop = dev.GetType().GetProperty(propertyName).GetValue(dev, null); object prop = dev.GetType().GetCType().GetProperty(propertyName).GetValue(dev, null);
// var prop = t.GetProperty(propertyName); // var prop = t.GetProperty(propertyName);
if (prop != null) if (prop != null)
@@ -225,126 +153,126 @@ namespace PepperDash.Essentials.Core
} }
} }
/// <summary> /// <summary>
/// Gets the methods on a device /// Gets the methods on a device
/// </summary> /// </summary>
/// <param name="key"></param> /// <param name="key"></param>
/// <returns></returns> /// <returns></returns>
public static string GetMethods(string deviceObjectPath) public static string GetMethods(string deviceObjectPath)
{ {
var obj = FindObjectOnPath(deviceObjectPath); var obj = FindObjectOnPath(deviceObjectPath);
if (obj == null) if (obj == null)
return "{ \"error\":\"No Device\"}"; return "{ \"error\":\"No Device\"}";
// Package up method names using helper objects // Package up method names using helper objects
Type t = obj.GetType(); CType t = obj.GetType();
var methods = t.GetMethods() var methods = t.GetMethods()
.Where(m => !m.IsSpecialName) .Where(m => !m.IsSpecialName)
.Select(p => new MethodNameParams(p)); .Select(p => new MethodNameParams(p));
return JsonConvert.SerializeObject(methods, Formatting.Indented); return JsonConvert.SerializeObject(methods, Formatting.Indented);
} }
public static string GetApiMethods(string deviceObjectPath) public static string GetApiMethods(string deviceObjectPath)
{ {
var obj = FindObjectOnPath(deviceObjectPath); var obj = FindObjectOnPath(deviceObjectPath);
if (obj == null) if (obj == null)
return "{ \"error\":\"No Device\"}"; return "{ \"error\":\"No Device\"}";
// Package up method names using helper objects // Package up method names using helper objects
Type t = obj.GetType(); CType t = obj.GetType();
var methods = t.GetMethods() var methods = t.GetMethods()
.Where(m => !m.IsSpecialName) .Where(m => !m.IsSpecialName)
.Where(m => m.GetCustomAttributes(typeof(ApiAttribute), true).Any()) .Where(m => m.GetCustomAttributes(typeof(ApiAttribute), true).Any())
.Select(p => new MethodNameParams(p)); .Select(p => new MethodNameParams(p));
return JsonConvert.SerializeObject(methods, Formatting.Indented); return JsonConvert.SerializeObject(methods, Formatting.Indented);
} }
/// <summary>
/// Walks down a dotted object path, starting with a Device, and returns the object
/// at the end of the path
/// </summary>
public static object FindObjectOnPath(string deviceObjectPath)
{
var path = deviceObjectPath.Split('.');
/// <summary> var dev = DeviceManager.GetDeviceForKey(path[0]);
/// Walks down a dotted object path, starting with a Device, and returns the object if (dev == null)
/// at the end of the path {
/// </summary> Debug.LogMessage(LogEventLevel.Information, "Device {0} not found", path[0]);
public static object FindObjectOnPath(string deviceObjectPath) return null;
{ }
var path = deviceObjectPath.Split('.');
var dev = DeviceManager.GetDeviceForKey(path[0]); // loop through any dotted properties
if (dev == null) object obj = dev;
{ if (path.Length > 1)
Debug.LogMessage(LogEventLevel.Information, "Device {0} not found", path[0]); {
return null; for (int i = 1; i < path.Length; i++)
} {
var objName = path[i];
string indexStr = null;
var indexOpen = objName.IndexOf('[');
if (indexOpen != -1)
{
var indexClose = objName.IndexOf(']');
if (indexClose == -1)
{
Debug.LogMessage(LogEventLevel.Information, dev, "ERROR Unmatched index brackets");
return null;
}
// Get the index and strip quotes if any
indexStr = objName.Substring(indexOpen + 1, indexClose - indexOpen - 1).Replace("\"", "");
objName = objName.Substring(0, indexOpen);
Debug.LogMessage(LogEventLevel.Information, dev, " Checking for collection '{0}', index '{1}'", objName, indexStr);
}
// loop through any dotted properties CType oType = obj.GetType();
object obj = dev; var prop = oType.GetProperty(objName);
if (path.Length > 1) if (prop == null)
{ {
for (int i = 1; i < path.Length; i++) Debug.LogMessage(LogEventLevel.Information, dev, "Property {0} not found on {1}", objName, path[i - 1]);
{ return null;
var objName = path[i]; }
string indexStr = null; // if there's an index, try to get the property
var indexOpen = objName.IndexOf('['); if (indexStr != null)
if (indexOpen != -1) {
{ if (!typeof(ICollection).IsAssignableFrom(prop.PropertyType))
var indexClose = objName.IndexOf(']'); {
if (indexClose == -1) Debug.LogMessage(LogEventLevel.Information, dev, "Property {0} is not collection", objName);
{ return null;
Debug.LogMessage(LogEventLevel.Information, dev, "ERROR Unmatched index brackets"); }
return null; var collection = prop.GetValue(obj, null) as ICollection;
} // Get the indexed items "property"
// Get the index and strip quotes if any var indexedPropInfo = prop.PropertyType.GetProperty("Item");
indexStr = objName.Substring(indexOpen + 1, indexClose - indexOpen - 1).Replace("\"", ""); // These are the parameters for the indexing. Only care about one
objName = objName.Substring(0, indexOpen); var indexParams = indexedPropInfo.GetIndexParameters();
Debug.LogMessage(LogEventLevel.Information, dev, " Checking for collection '{0}', index '{1}'", objName, indexStr); if (indexParams.Length > 0)
} {
Debug.LogMessage(LogEventLevel.Information, " Indexed, param type: {0}", indexParams[0].ParameterType.Name);
var properParam = Convert.ChangeType(indexStr, indexParams[0].ParameterType,
System.Globalization.CultureInfo.InvariantCulture);
try
{
obj = indexedPropInfo.GetValue(collection, new object[] { properParam });
}
// if the index is bad, catch it here.
catch (Crestron.SimplSharp.Reflection.TargetInvocationException e)
{
if (e.InnerException is ArgumentOutOfRangeException)
Debug.LogMessage(LogEventLevel.Information, " Index Out of range");
else if (e.InnerException is KeyNotFoundException)
Debug.LogMessage(LogEventLevel.Information, " Key not found");
return null;
}
}
Type oType = obj.GetType(); }
var prop = oType.GetProperty(objName); else
if (prop == null) obj = prop.GetValue(obj, null);
{ }
Debug.LogMessage(LogEventLevel.Information, dev, "Property {0} not found on {1}", objName, path[i - 1]); }
return null; return obj;
} }
// if there's an index, try to get the property
if (indexStr != null)
{
if (!typeof(ICollection).IsAssignableFrom(prop.PropertyType))
{
Debug.LogMessage(LogEventLevel.Information, dev, "Property {0} is not collection", objName);
return null;
}
var collection = prop.GetValue(obj, null) as ICollection;
// Get the indexed items "property"
var indexedPropInfo = prop.PropertyType.GetProperty("Item");
// These are the parameters for the indexing. Only care about one
var indexParams = indexedPropInfo.GetIndexParameters();
if (indexParams.Length > 0)
{
Debug.LogMessage(LogEventLevel.Information, " Indexed, param type: {0}", indexParams[0].ParameterType.Name);
var properParam = Convert.ChangeType(indexStr, indexParams[0].ParameterType,
System.Globalization.CultureInfo.InvariantCulture);
try
{
obj = indexedPropInfo.GetValue(collection, new object[] { properParam });
}
// if the index is bad, catch it here.
catch (TargetInvocationException e)
{
if (e.InnerException is ArgumentOutOfRangeException)
Debug.LogMessage(LogEventLevel.Information, " Index Out of range");
else if (e.InnerException is KeyNotFoundException)
Debug.LogMessage(LogEventLevel.Information, " Key not found");
return null;
}
}
}
else
obj = prop.GetValue(obj, null);
}
}
return obj;
}
/// <summary> /// <summary>
/// Sets a property on an object. /// Sets a property on an object.
@@ -359,7 +287,7 @@ namespace PepperDash.Essentials.Core
//if (obj == null) //if (obj == null)
// return "{\"error\":\"No object found\"}"; // return "{\"error\":\"No object found\"}";
//Type t = obj.GetType(); //CType t = obj.GetType();
//// get the properties and set them into a new collection of NameType wrappers //// get the properties and set them into a new collection of NameType wrappers
@@ -367,85 +295,78 @@ namespace PepperDash.Essentials.Core
//return JsonConvert.SerializeObject(props, Formatting.Indented); //return JsonConvert.SerializeObject(props, Formatting.Indented);
} }
}
} public class DeviceActionWrapper
{
public string DeviceKey { get; set; }
public string MethodName { get; set; }
public object[] Params { get; set; }
}
public class DeviceActionWrapper public class PropertyNameType
{ {
public string DeviceKey { get; set; } object Parent;
public string MethodName { get; set; }
public object[] Params { get; set; }
}
public class PropertyNameType [JsonIgnore]
{ public PropertyInfo PropInfo { get; private set; }
private object Parent; public string Name { get { return PropInfo.Name; } }
public string Type { get { return PropInfo.PropertyType.Name; } }
[JsonIgnore] public string Value { get
public PropertyInfo PropInfo { get; private set; }
public string Name { get { return PropInfo.Name; } }
public string Type { get { return PropInfo.PropertyType.Name; } }
public string Value
{ {
get if (PropInfo.CanRead)
{ {
if (PropInfo.CanRead) try
{ {
try return PropInfo.GetValue(Parent, null).ToString();
{
return PropInfo.GetValue(Parent, null).ToString();
}
catch (Exception)
{
return null;
}
} }
else catch (Exception)
{
return null; return null;
}
} }
} else
return null;
} }
public bool CanRead { get { return PropInfo.CanRead; } } public bool CanRead { get { return PropInfo.CanRead; } }
public bool CanWrite { get { return PropInfo.CanWrite; } } public bool CanWrite { get { return PropInfo.CanWrite; } }
public PropertyNameType(PropertyInfo info, object parent) public PropertyNameType(PropertyInfo info, object parent)
{ {
PropInfo = info; PropInfo = info;
Parent = parent; Parent = parent;
} }
} }
public class MethodNameParams public class MethodNameParams
{ {
[JsonIgnore] [JsonIgnore]
public MethodInfo MethodInfo { get; private set; } public MethodInfo MethodInfo { get; private set; }
public string Name { get { return MethodInfo.Name; } } public string Name { get { return MethodInfo.Name; } }
public IEnumerable<NameType> Params public IEnumerable<NameType> Params { get {
{ return MethodInfo.GetParameters().Select(p =>
get new NameType { Name = p.Name, Type = p.ParameterType.Name });
{ } }
return MethodInfo.GetParameters().Select(p =>
new NameType { Name = p.Name, Type = p.ParameterType.Name });
}
}
public MethodNameParams(MethodInfo info) public MethodNameParams(MethodInfo info)
{ {
MethodInfo = info; MethodInfo = info;
} }
} }
public class NameType public class NameType
{ {
public string Name { get; set; } public string Name { get; set; }
public string Type { get; set; } public string Type { get; set; }
} }
[AttributeUsage(AttributeTargets.All)] [AttributeUsage(AttributeTargets.All)]
public class ApiAttribute : Attribute public class ApiAttribute : CAttribute
{ {
} }
} }

View File

@@ -1,50 +1,50 @@
using Crestron.SimplSharp; using System;
using Crestron.SimplSharpPro;
using PepperDash.Core;
using Serilog.Events;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using PepperDash.Core;
using Serilog.Events;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
public static class DeviceManager public static class DeviceManager
{ {
public static event EventHandler<EventArgs> AllDevicesActivated; public static event EventHandler<EventArgs> AllDevicesActivated;
public static event EventHandler<EventArgs> AllDevicesRegistered; public static event EventHandler<EventArgs> AllDevicesRegistered;
public static event EventHandler<EventArgs> AllDevicesInitialized;
private static readonly CCriticalSection DeviceCriticalSection = new CCriticalSection(); private static readonly CCriticalSection DeviceCriticalSection = new CCriticalSection();
private static readonly CEvent AllowAddDevicesCEvent = new CEvent(false, true); private static readonly CEvent AllowAddDevicesCEvent = new CEvent(false, true);
//public static List<Device> Devices { get { return _Devices; } }
//static List<Device> _Devices = new List<Device>();
//public static List<Device> Devices { get { return _Devices; } } static readonly Dictionary<string, IKeyed> Devices = new Dictionary<string, IKeyed>(StringComparer.OrdinalIgnoreCase);
//static List<Device> _Devices = new List<Device>();
private static readonly Dictionary<string, IKeyed> Devices = new Dictionary<string, IKeyed>(StringComparer.OrdinalIgnoreCase); /// <summary>
/// Returns a copy of all the devices in a list
/// </summary>
public static List<IKeyed> AllDevices { get { return new List<IKeyed>(Devices.Values); } }
/// <summary> public static bool AddDeviceEnabled;
/// Returns a copy of all the devices in a list
/// </summary>
public static List<IKeyed> AllDevices { get { return new List<IKeyed>(Devices.Values); } }
public static bool AddDeviceEnabled; public static void Initialize(CrestronControlSystem cs)
{
public static void Initialize(CrestronControlSystem cs) AddDeviceEnabled = true;
{ CrestronConsole.AddNewConsoleCommand(ListDeviceCommStatuses, "devcommstatus", "Lists the communication status of all devices",
AddDeviceEnabled = true; ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(ListDeviceCommStatuses, "devcommstatus", "Lists the communication status of all devices", CrestronConsole.AddNewConsoleCommand(ListDeviceFeedbacks, "devfb", "Lists current feedbacks",
ConsoleAccessLevelEnum.AccessOperator); ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(ListDeviceFeedbacks, "devfb", "Lists current feedbacks", CrestronConsole.AddNewConsoleCommand(ListDevices, "devlist", "Lists current managed devices",
ConsoleAccessLevelEnum.AccessOperator); ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(ListDevices, "devlist", "Lists current managed devices", CrestronConsole.AddNewConsoleCommand(DeviceJsonApi.DoDeviceActionWithJson, "devjson", "",
ConsoleAccessLevelEnum.AccessOperator); ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(DeviceJsonApi.DoDeviceActionWithJson, "devjson", "", CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetProperties(s)), "devprops", "", ConsoleAccessLevelEnum.AccessOperator);
ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetMethods(s)), "devmethods", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetProperties(s)), "devprops", "", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetApiMethods(s)), "apimethods", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetMethods(s)), "devmethods", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetApiMethods(s)), "apimethods", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(SimulateComReceiveOnDevice, "devsimreceive", CrestronConsole.AddNewConsoleCommand(SimulateComReceiveOnDevice, "devsimreceive",
"Simulates incoming data on a com device", ConsoleAccessLevelEnum.AccessOperator); "Simulates incoming data on a com device", ConsoleAccessLevelEnum.AccessOperator);
@@ -52,89 +52,77 @@ namespace PepperDash.Essentials.Core
CrestronConsole.AddNewConsoleCommand(s => DisableAllDeviceStreamDebugging(), "disableallstreamdebug", "disables stream debugging on all devices", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(s => DisableAllDeviceStreamDebugging(), "disableallstreamdebug", "disables stream debugging on all devices", ConsoleAccessLevelEnum.AccessOperator);
} }
/// <summary> /// <summary>
/// Calls activate steps on all Device class items /// Calls activate steps on all Device class items
/// </summary> /// </summary>
public static void ActivateAll() public static void ActivateAll()
{ {
try try
{ {
OnAllDevicesRegistered(); OnAllDevicesRegistered();
DeviceCriticalSection.Enter(); DeviceCriticalSection.Enter();
AddDeviceEnabled = false; AddDeviceEnabled = false;
// PreActivate all devices // PreActivate all devices
Debug.LogMessage(LogEventLevel.Information, "****PreActivation starting...****"); Debug.LogMessage(LogEventLevel.Information,"****PreActivation starting...****");
foreach (var d in Devices.Values) foreach (var d in Devices.Values)
{ {
try try
{ {
if (d is Device) if (d is Device)
(d as Device).PreActivate(); (d as Device).PreActivate();
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogMessage(LogEventLevel.Information, d, "ERROR: Device {1} PreActivation failure: {0}", e.Message, d.Key); Debug.LogMessage(LogEventLevel.Information, d, "ERROR: Device {1} PreActivation failure: {0}", e.Message, d.Key);
Debug.LogMessage(LogEventLevel.Debug, d, "Stack Trace: {0}", e.StackTrace); Debug.LogMessage(LogEventLevel.Debug, d, "Stack Trace: {0}", e.StackTrace);
} }
} }
Debug.LogMessage(LogEventLevel.Information, "****PreActivation complete****"); Debug.LogMessage(LogEventLevel.Information, "****PreActivation complete****");
Debug.LogMessage(LogEventLevel.Information, "****Activation starting...****"); Debug.LogMessage(LogEventLevel.Information, "****Activation starting...****");
// Activate all devices // Activate all devices
foreach (var d in Devices.Values) foreach (var d in Devices.Values)
{ {
try try
{ {
if (d is Device) if (d is Device)
(d as Device).Activate(); (d as Device).Activate();
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogMessage(LogEventLevel.Information, d, "ERROR: Device {1} Activation failure: {0}", e.Message, d.Key); Debug.LogMessage(LogEventLevel.Information, d, "ERROR: Device {1} Activation failure: {0}", e.Message, d.Key);
Debug.LogMessage(LogEventLevel.Debug, d, "Stack Trace: {0}", e.StackTrace); Debug.LogMessage(LogEventLevel.Debug, d, "Stack Trace: {0}", e.StackTrace);
} }
} }
Debug.LogMessage(LogEventLevel.Information, "****Activation complete****"); Debug.LogMessage(LogEventLevel.Information, "****Activation complete****");
Debug.LogMessage(LogEventLevel.Information, "****PostActivation starting...****"); Debug.LogMessage(LogEventLevel.Information, "****PostActivation starting...****");
// PostActivate all devices // PostActivate all devices
foreach (var d in Devices.Values) foreach (var d in Devices.Values)
{ {
try try
{ {
if (d is Device) if (d is Device)
(d as Device).PostActivate(); (d as Device).PostActivate();
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogMessage(LogEventLevel.Information, d, "ERROR: Device {1} PostActivation failure: {0}", e.Message, d.Key); Debug.LogMessage(LogEventLevel.Information, d, "ERROR: Device {1} PostActivation failure: {0}", e.Message, d.Key);
Debug.LogMessage(LogEventLevel.Debug, d, "Stack Trace: {0}", e.StackTrace); Debug.LogMessage(LogEventLevel.Debug, d, "Stack Trace: {0}", e.StackTrace);
} }
} }
Debug.LogMessage(LogEventLevel.Information, "****PostActivation complete****"); Debug.LogMessage(LogEventLevel.Information, "****PostActivation complete****");
OnAllDevicesActivated(); OnAllDevicesActivated();
} }
finally finally
{ {
DeviceCriticalSection.Leave(); DeviceCriticalSection.Leave();
} }
} }
private static void DeviceManager_Initialized(object sender, EventArgs e)
{
var allInitialized = Devices.Values.OfType<EssentialsDevice>().All(d => d.IsInitialized);
if (allInitialized)
{
Debug.LogMessage(LogEventLevel.Information, "****All Devices Initalized****");
OnAllDevicesInitialized();
}
}
private static void OnAllDevicesActivated() private static void OnAllDevicesActivated()
{ {
@@ -154,85 +142,77 @@ namespace PepperDash.Essentials.Core
} }
} }
private static void OnAllDevicesInitialized() /// <summary>
{ /// Calls activate on all Device class items
var handler = AllDevicesInitialized; /// </summary>
if (handler != null) public static void DeactivateAll()
{ {
handler(null, new EventArgs()); try
} {
} DeviceCriticalSection.Enter();
foreach (var d in Devices.Values.OfType<Device>())
{
d.Deactivate();
}
}
finally
{
DeviceCriticalSection.Leave();
}
}
/// <summary> //static void ListMethods(string devKey)
/// Calls activate on all Device class items //{
/// </summary> // var dev = GetDeviceForKey(devKey);
public static void DeactivateAll() // if(dev != null)
{ // {
try // var type = dev.GetType().GetCType();
{ // var methods = type.GetMethods(BindingFlags.Public|BindingFlags.Instance);
DeviceCriticalSection.Enter(); // var sb = new StringBuilder();
foreach (var d in Devices.Values.OfType<Device>()) // sb.AppendLine(string.Format("{2} methods on [{0}] ({1}):", dev.Key, type.Name, methods.Length));
{ // foreach (var m in methods)
d.Deactivate(); // {
} // sb.Append(string.Format("{0}(", m.Name));
} // var pars = m.GetParameters();
finally // foreach (var p in pars)
{ // sb.Append(string.Format("({1}){0} ", p.Name, p.ParameterType.Name));
DeviceCriticalSection.Leave(); // sb.AppendLine(")");
} // }
} // CrestronConsole.ConsoleCommandResponse(sb.ToString());
// }
//}
//static void ListMethods(string devKey) private static void ListDevices(string s)
//{ {
// var dev = GetDeviceForKey(devKey); Debug.LogMessage(LogEventLevel.Information, "{0} Devices registered with Device Manager:", Devices.Count);
// if(dev != null) var sorted = Devices.Values.ToList();
// { sorted.Sort((a, b) => a.Key.CompareTo(b.Key));
// var type = dev.GetType().GetType();
// var methods = type.GetMethods(BindingFlags.Public|BindingFlags.Instance);
// var sb = new StringBuilder();
// sb.AppendLine(string.Format("{2} methods on [{0}] ({1}):", dev.Key, type.Name, methods.Length));
// foreach (var m in methods)
// {
// sb.Append(string.Format("{0}(", m.Name));
// var pars = m.GetParameters();
// foreach (var p in pars)
// sb.Append(string.Format("({1}){0} ", p.Name, p.ParameterType.Name));
// sb.AppendLine(")");
// }
// CrestronConsole.ConsoleCommandResponse(sb.ToString());
// }
//}
private static void ListDevices(string s) foreach (var d in sorted)
{ {
Debug.LogMessage(LogEventLevel.Information, "{0} Devices registered with Device Manager:", Devices.Count); var name = d is IKeyName ? (d as IKeyName).Name : "---";
var sorted = Devices.Values.ToList(); Debug.LogMessage(LogEventLevel.Information, " [{0}] {1}", d.Key, name);
sorted.Sort((a, b) => a.Key.CompareTo(b.Key)); }
}
foreach (var d in sorted) private static void ListDeviceFeedbacks(string devKey)
{ {
var name = d is IKeyName ? (d as IKeyName).Name : "---"; var dev = GetDeviceForKey(devKey);
Debug.LogMessage(LogEventLevel.Information, " [{0}] {1}", d.Key, name); if (dev == null)
} {
} Debug.LogMessage(LogEventLevel.Information, "Device '{0}' not found", devKey);
return;
}
var statusDev = dev as IHasFeedback;
if (statusDev == null)
{
Debug.LogMessage(LogEventLevel.Information, "Device '{0}' does not have visible feedbacks", devKey);
return;
}
statusDev.DumpFeedbacksToConsole(true);
}
private static void ListDeviceFeedbacks(string devKey) //static void ListDeviceCommands(string devKey)
{
var dev = GetDeviceForKey(devKey);
if (dev == null)
{
Debug.LogMessage(LogEventLevel.Information, "Device '{0}' not found", devKey);
return;
}
if (!(dev is IHasFeedback statusDev))
{
Debug.LogMessage(LogEventLevel.Information, "Device '{0}' does not have visible feedbacks", devKey);
return;
}
statusDev.DumpFeedbacksToConsole(true);
}
//static void ListDeviceCommands(string devKey)
//{ //{
// var dev = GetDeviceForKey(devKey); // var dev = GetDeviceForKey(devKey);
// if (dev == null) // if (dev == null)
@@ -243,135 +223,134 @@ namespace PepperDash.Essentials.Core
// Debug.LogMessage(LogEventLevel.Information, "This needs to be reworked. Stay tuned.", devKey); // Debug.LogMessage(LogEventLevel.Information, "This needs to be reworked. Stay tuned.", devKey);
//} //}
private static void ListDeviceCommStatuses(string input) private static void ListDeviceCommStatuses(string input)
{ {
var sb = new StringBuilder();
foreach (var dev in Devices.Values.OfType<ICommunicationMonitor>()) foreach (var dev in Devices.Values.OfType<ICommunicationMonitor>())
{ {
CrestronConsole.ConsoleCommandResponse($"{dev}: {dev.CommunicationMonitor.Status}{Environment.NewLine}"); sb.Append(string.Format("{0}: {1}\r", dev,
} dev.CommunicationMonitor.Status));
} }
CrestronConsole.ConsoleCommandResponse(sb.ToString());
}
//static void DoDeviceCommand(string command) //static void DoDeviceCommand(string command)
//{ //{
// Debug.LogMessage(LogEventLevel.Information, "Not yet implemented. Stay tuned"); // Debug.LogMessage(LogEventLevel.Information, "Not yet implemented. Stay tuned");
//} //}
public static void AddDevice(IKeyed newDev) public static void AddDevice(IKeyed newDev)
{ {
try try
{ {
if (!DeviceCriticalSection.TryEnter()) if (!DeviceCriticalSection.TryEnter())
{ {
Debug.LogMessage(LogEventLevel.Information, "Currently unable to add devices to Device Manager. Please try again"); Debug.LogMessage(LogEventLevel.Information, "Currently unable to add devices to Device Manager. Please try again");
return; return;
} }
// Check for device with same key // Check for device with same key
//var existingDevice = _Devices.FirstOrDefault(d => d.Key.Equals(newDev.Key, StringComparison.OrdinalIgnoreCase)); //var existingDevice = _Devices.FirstOrDefault(d => d.Key.Equals(newDev.Key, StringComparison.OrdinalIgnoreCase));
////// If it exists, remove or warn?? ////// If it exists, remove or warn??
//if (existingDevice != null) //if (existingDevice != null)
if (!AddDeviceEnabled) if (!AddDeviceEnabled)
{ {
Debug.LogMessage(LogEventLevel.Information, "All devices have been activated. Adding new devices is not allowed."); Debug.LogMessage(LogEventLevel.Information, "All devices have been activated. Adding new devices is not allowed.");
return; return;
} }
if (Devices.ContainsKey(newDev.Key)) if (Devices.ContainsKey(newDev.Key))
{ {
Debug.LogMessage(LogEventLevel.Information, newDev, "WARNING: A device with this key already exists. Not added to manager"); Debug.LogMessage(LogEventLevel.Information, newDev, "WARNING: A device with this key already exists. Not added to manager");
return; return;
} }
Devices.Add(newDev.Key, newDev); Devices.Add(newDev.Key, newDev);
//if (!(_Devices.Contains(newDev))) //if (!(_Devices.Contains(newDev)))
// _Devices.Add(newDev); // _Devices.Add(newDev);
}
finally
{
DeviceCriticalSection.Leave();
}
}
if (newDev is EssentialsDevice essentialsDev) public static void AddDevice(IEnumerable<IKeyed> devicesToAdd)
essentialsDev.Initialized += DeviceManager_Initialized; {
} try
finally {
{ if (!DeviceCriticalSection.TryEnter())
DeviceCriticalSection.Leave(); {
} Debug.LogMessage(LogEventLevel.Information,
} "Currently unable to add devices to Device Manager. Please try again");
return;
}
if (!AddDeviceEnabled)
{
Debug.LogMessage(LogEventLevel.Information,
"All devices have been activated. Adding new devices is not allowed.");
return;
}
public static void AddDevice(IEnumerable<IKeyed> devicesToAdd) foreach (var dev in devicesToAdd)
{ {
try try
{ {
if (!DeviceCriticalSection.TryEnter()) Devices.Add(dev.Key, dev);
{ }
Debug.LogMessage(LogEventLevel.Information, catch (ArgumentException ex)
"Currently unable to add devices to Device Manager. Please try again"); {
return; Debug.LogMessage(LogEventLevel.Information, "Error adding device with key {0} to Device Manager: {1}\r\nStack Trace: {2}",
} dev.Key, ex.Message, ex.StackTrace);
if (!AddDeviceEnabled) }
{ }
Debug.LogMessage(LogEventLevel.Information, }
"All devices have been activated. Adding new devices is not allowed."); finally
return; {
} DeviceCriticalSection.Leave();
}
}
foreach (var dev in devicesToAdd) public static void RemoveDevice(IKeyed newDev)
{ {
try try
{ {
Devices.Add(dev.Key, dev);
}
catch (ArgumentException ex)
{
Debug.LogMessage(LogEventLevel.Information, "Error adding device with key {0} to Device Manager: {1}\r\nStack Trace: {2}",
dev.Key, ex.Message, ex.StackTrace);
}
}
}
finally
{
DeviceCriticalSection.Leave();
}
}
public static void RemoveDevice(IKeyed newDev)
{
try
{
DeviceCriticalSection.Enter(); DeviceCriticalSection.Enter();
if (newDev == null) if (newDev == null)
return; return;
if (Devices.ContainsKey(newDev.Key)) if (Devices.ContainsKey(newDev.Key))
Devices.Remove(newDev.Key); Devices.Remove(newDev.Key);
//if (_Devices.Contains(newDev)) //if (_Devices.Contains(newDev))
// _Devices.Remove(newDev); // _Devices.Remove(newDev);
else else
Debug.LogMessage(LogEventLevel.Information, "Device manager: Device '{0}' does not exist in manager. Cannot remove", newDev.Key); Debug.LogMessage(LogEventLevel.Information, "Device manager: Device '{0}' does not exist in manager. Cannot remove", newDev.Key);
} }
finally finally
{ {
DeviceCriticalSection.Leave(); DeviceCriticalSection.Leave();
} }
} }
public static IEnumerable<string> GetDeviceKeys() public static IEnumerable<string> GetDeviceKeys()
{ {
//return _Devices.Select(d => d.Key).ToList(); //return _Devices.Select(d => d.Key).ToList();
return Devices.Keys; return Devices.Keys;
} }
public static IEnumerable<IKeyed> GetDevices() public static IEnumerable<IKeyed> GetDevices()
{ {
//return _Devices.Select(d => d.Key).ToList(); //return _Devices.Select(d => d.Key).ToList();
return Devices.Values; return Devices.Values;
} }
public static IKeyed GetDeviceForKey(string key) public static IKeyed GetDeviceForKey(string key)
{ {
//return _Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase)); //return _Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
if (key != null && Devices.ContainsKey(key)) if (key != null && Devices.ContainsKey(key))
return Devices[key]; return Devices[key];
return null; return null;
} }
/// <summary> /// <summary>
/// Console handler that simulates com port data receive /// Console handler that simulates com port data receive
@@ -388,7 +367,8 @@ namespace PepperDash.Essentials.Core
} }
//Debug.LogMessage(LogEventLevel.Verbose, "**** {0} - {1} ****", match.Groups[1].Value, match.Groups[2].Value); //Debug.LogMessage(LogEventLevel.Verbose, "**** {0} - {1} ****", match.Groups[1].Value, match.Groups[2].Value);
if (!(GetDeviceForKey(match.Groups[1].Value) is ComPortController com)) var com = GetDeviceForKey(match.Groups[1].Value) as ComPortController;
if (com == null)
{ {
CrestronConsole.ConsoleCommandResponse("'{0}' is not a comm port device", match.Groups[1].Value); CrestronConsole.ConsoleCommandResponse("'{0}' is not a comm port device", match.Groups[1].Value);
return; return;
@@ -443,15 +423,16 @@ namespace PepperDash.Essentials.Core
var deviceKey = args[0]; var deviceKey = args[0];
var setting = args[1]; var setting = args[1];
var timeout = String.Empty; var timeout= String.Empty;
if (args.Length >= 3) if (args.Length >= 3)
{ {
timeout = args[2]; timeout = args[2];
} }
var device = GetDeviceForKey(deviceKey) as IStreamDebugging;
if (!(GetDeviceForKey(deviceKey) is IStreamDebugging device)) if (device == null)
{ {
CrestronConsole.ConsoleCommandResponse("Unable to get device with key: {0}", deviceKey); CrestronConsole.ConsoleCommandResponse("Unable to get device with key: {0}", deviceKey);
return; return;
@@ -498,11 +479,13 @@ namespace PepperDash.Essentials.Core
{ {
foreach (var device in AllDevices) foreach (var device in AllDevices)
{ {
if (device is IStreamDebugging streamDevice) var streamDevice = device as IStreamDebugging;
if (streamDevice != null)
{ {
streamDevice.StreamDebugging.SetDebuggingWithDefaultTimeout(eStreamDebuggingSetting.Off); streamDevice.StreamDebugging.SetDebuggingWithDefaultTimeout(eStreamDebuggingSetting.Off);
} }
} }
} }
} }
} }

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Crestron.SimplSharp; using Crestron.SimplSharp;
using System.Reflection; using Crestron.SimplSharp.Reflection;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
@@ -17,24 +17,6 @@ namespace PepperDash.Essentials.Core
[Description("The base Essentials Device Class")] [Description("The base Essentials Device Class")]
public abstract class EssentialsDevice : Device public abstract class EssentialsDevice : Device
{ {
public event EventHandler Initialized;
private bool _isInitialized;
public bool IsInitialized {
get { return _isInitialized; }
private set
{
if (_isInitialized == value) return;
_isInitialized = value;
if (_isInitialized)
{
Initialized?.Invoke(this, new EventArgs());
}
}
}
protected EssentialsDevice(string key) protected EssentialsDevice(string key)
: base(key) : base(key)
{ {
@@ -59,8 +41,6 @@ namespace PepperDash.Essentials.Core
try try
{ {
Initialize(); Initialize();
IsInitialized = true;
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -0,0 +1,17 @@
using System.Collections.Generic;
namespace PepperDash.Essentials.Core
{
public interface IHasDspPresets
{
List<IDspPreset> Presets { get; }
void RecallPreset(IDspPreset preset);
}
public interface IDspPreset
{
string Name { get; }
}
}

View File

@@ -1,12 +0,0 @@
using PepperDash.Core;
using System.Collections.Generic;
namespace PepperDash.Essentials.Core
{
public interface IDspPresets
{
Dictionary<string, IKeyName> Presets { get; }
void RecallPreset(string key);
}
}

View File

@@ -1,5 +1,7 @@
using System; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharp.Reflection;
using PepperDash.Core; using PepperDash.Core;
using Serilog.Events; using Serilog.Events;
@@ -21,7 +23,7 @@ namespace PepperDash.Essentials.Core
{ {
public static void DumpFeedbacksToConsole(this IHasFeedback source, bool getCurrentStates) public static void DumpFeedbacksToConsole(this IHasFeedback source, bool getCurrentStates)
{ {
Type t = source.GetType(); CType t = source.GetType();
// get the properties and set them into a new collection of NameType wrappers // get the properties and set them into a new collection of NameType wrappers
var props = t.GetProperties().Select(p => new PropertyNameType(p, t)); var props = t.GetProperties().Select(p => new PropertyNameType(p, t));

View File

@@ -1,82 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Essentials.Core.Devices;
namespace PepperDash.Essentials.Core
{
public class LevelControlListItem : AudioControlListItemBase
{
[JsonIgnore]
public IBasicVolumeWithFeedback LevelControl
{
get
{
if (_levelControl == null)
_levelControl = DeviceManager.GetDeviceForKey(ParentDeviceKey) as IBasicVolumeWithFeedback;
return _levelControl;
}
}
IBasicVolumeWithFeedback _levelControl;
/// <summary>
/// Gets the name from the device if it implements IKeyName or else returns the Name property
/// </summary>
[JsonProperty("preferredName")]
public string PreferredName
{
get
{
if (!string.IsNullOrEmpty(Name)) return Name;
else
{
if (LevelControl is IKeyName namedLevelControl)
{
if (namedLevelControl == null)
return "---";
return namedLevelControl.Name;
}
else return "---";
}
}
}
/// <summary>
/// The key of the device in the DeviceManager for control
/// </summary>
[JsonProperty("deviceKey")]
public string DeviceKey
{
get
{
if(string.IsNullOrEmpty(ItemKey)) return ParentDeviceKey;
else
{
return DeviceManager.AllDevices.
Where(d => d.Key.Contains(ParentDeviceKey) && d.Key.Contains(ItemKey)).FirstOrDefault()?.Key ?? $"{ParentDeviceKey}--{ItemKey}";
}
}
}
/// <summary>
/// Indicates if the item is a level, mute , or both
/// </summary>
[JsonProperty("type")]
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public eLevelControlType Type { get; set; }
}
[Flags]
public enum eLevelControlType
{
Level = 1,
Mute = 2,
LevelAndMute = Level | Mute,
}
}

View File

@@ -5,6 +5,7 @@ using Crestron.SimplSharpPro;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Routing;
using PepperDash.Core; using PepperDash.Core;
using Serilog.Events; using Serilog.Events;

View File

@@ -5,6 +5,7 @@ using Crestron.SimplSharpPro;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Routing;
using PepperDash.Core; using PepperDash.Core;
using Serilog.Events; using Serilog.Events;
@@ -69,7 +70,7 @@ namespace PepperDash.Essentials.Core.Devices
{ {
public LaptopFactory() public LaptopFactory()
{ {
TypeNames = new List<string>() { "deprecated" }; TypeNames = new List<string>() { "laptop" };
} }
public override EssentialsDevice BuildDevice(DeviceConfig dc) public override EssentialsDevice BuildDevice(DeviceConfig dc)

View File

@@ -1,45 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
public class PresetListItem : AudioControlListItemBase
{
[JsonIgnore]
public IKeyName Preset
{
get
{
if (_preset == null)
{
var parent = DeviceManager.GetDeviceForKey(ParentDeviceKey) as IDspPresets;
if (parent == null || !parent.Presets.ContainsKey(ItemKey))
return null;
_preset = parent.Presets[ItemKey];
}
return _preset;
}
}
private IKeyName _preset;
/// <summary>
/// Gets the name from the device if it implements IKeyName or else returns the Name property
/// </summary>
[JsonProperty("preferredName")]
public string PreferredName
{
get
{
if (!string.IsNullOrEmpty(Name)) return Name;
else return Preset.Name;
}
}
}
}

View File

@@ -1,121 +1,128 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using PepperDash.Core; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharpPro;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public enum eSourceListItemType public enum eSourceListItemType
{ {
Route, Off, Other, SomethingAwesomerThanThese Route, Off, Other, SomethingAwesomerThanThese
} }
/// <summary> /// <summary>
/// Represents an item in a source list - can be deserialized into. /// Represents an item in a source list - can be deserialized into.
/// </summary> /// </summary>
public class SourceListItem public class SourceListItem
{ {
[JsonProperty("sourceKey")] [JsonProperty("sourceKey")]
public string SourceKey { get; set; } public string SourceKey { get; set; }
/// <summary> /// <summary>
/// Returns the source Device for this, if it exists in DeviceManager /// Returns the source Device for this, if it exists in DeviceManager
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public Device SourceDevice public Device SourceDevice
{ {
get get
{ {
if (_SourceDevice == null) if (_SourceDevice == null)
_SourceDevice = DeviceManager.GetDeviceForKey(SourceKey) as Device; _SourceDevice = DeviceManager.GetDeviceForKey(SourceKey) as Device;
return _SourceDevice; return _SourceDevice;
} }
} }
Device _SourceDevice;
private Device _SourceDevice; /// <summary>
/// Gets either the source's Name or this AlternateName property, if
/// defined. If source doesn't exist, returns "Missing source"
/// </summary>
[JsonProperty("preferredName")]
public string PreferredName
{
get
{
if (string.IsNullOrEmpty(Name))
{
if (SourceDevice == null)
return "---";
return SourceDevice.Name;
}
return Name;
}
}
/// <summary> /// <summary>
/// Gets either the source's Name or this AlternateName property, if /// A name that will override the source's name on the UI
/// defined. If source doesn't exist, returns "Missing source" /// </summary>
/// </summary> [JsonProperty("name")]
[JsonProperty("preferredName")] public string Name { get; set; }
public string PreferredName
{
get
{
if (string.IsNullOrEmpty(Name))
{
if (SourceDevice == null)
return "---";
return SourceDevice.Name;
}
return Name;
}
}
/// <summary>
/// A name that will override the source's name on the UI
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary> /// <summary>
/// Specifies and icon for the source list item /// Specifies and icon for the source list item
/// </summary> /// </summary>
[JsonProperty("icon")] [JsonProperty("icon")]
public string Icon { get; set; } public string Icon { get; set; }
/// <summary> /// <summary>
/// Alternate icon /// Alternate icon
/// </summary> /// </summary>
[JsonProperty("altIcon")] [JsonProperty("altIcon")]
public string AltIcon { get; set; } public string AltIcon { get; set; }
/// <summary> /// <summary>
/// Indicates if the item should be included in the source list /// Indicates if the item should be included in the source list
/// </summary> /// </summary>
[JsonProperty("includeInSourceList")] [JsonProperty("includeInSourceList")]
public bool IncludeInSourceList { get; set; } public bool IncludeInSourceList { get; set; }
/// <summary> /// <summary>
/// Used to specify the order of the items in the source list when displayed /// Used to specify the order of the items in the source list when displayed
/// </summary> /// </summary>
[JsonProperty("order")] [JsonProperty("order")]
public int Order { get; set; } public int Order { get; set; }
/// <summary> /// <summary>
/// The key of the device for volume control /// The key of the device for volume control
/// </summary> /// </summary>
[JsonProperty("volumeControlKey")] [JsonProperty("volumeControlKey")]
public string VolumeControlKey { get; set; } public string VolumeControlKey { get; set; }
/// <summary> /// <summary>
/// The type of source list item /// The type of source list item
/// </summary> /// </summary>
[JsonProperty("type")] [JsonProperty("type")]
[JsonConverter(typeof(StringEnumConverter))] [JsonConverter(typeof(StringEnumConverter))]
public eSourceListItemType Type { get; set; } public eSourceListItemType Type { get; set; }
/// <summary> /// <summary>
/// The list of routes to execute for this source list item /// The list of routes to execute for this source list item
/// </summary> /// </summary>
[JsonProperty("routeList")] [JsonProperty("routeList")]
public List<SourceRouteListItem> RouteList { get; set; } public List<SourceRouteListItem> RouteList { get; set; }
/// <summary> /// <summary>
/// Indicates if this source should be disabled for sharing to the far end call participants via codec content /// Indicates if this source should be disabled for sharing to the far end call participants via codec content
/// </summary> /// </summary>
[JsonProperty("disableCodecSharing")] [JsonProperty("disableCodecSharing")]
public bool DisableCodecSharing { get; set; } public bool DisableCodecSharing { get; set; }
/// <summary> /// <summary>
/// Indicates if this source should be disabled for routing to a shared output /// Indicates if this source should be disabled for routing to a shared output
/// </summary> /// </summary>
[JsonProperty("disableRoutedSharing")] [JsonProperty("disableRoutedSharing")]
public bool DisableRoutedSharing { get; set; } public bool DisableRoutedSharing { get; set; }
[JsonProperty("destinations")] [JsonProperty("destinations")]
public List<eSourceListItemDestinationTypes> Destinations { get; set; } public List<eSourceListItemDestinationTypes> Destinations { get; set; }
@@ -137,46 +144,25 @@ namespace PepperDash.Essentials.Core
[JsonProperty("isAudioSource")] [JsonProperty("isAudioSource")]
public bool IsAudioSource { get; set; } public bool IsAudioSource { get; set; }
/// <summary> public SourceListItem()
/// Hide source on UI when Avanced Sharing is enabled {
/// </summary> Icon = "Blank";
[JsonProperty("disableAdvancedRouting")] }
public bool DisableAdvancedRouting { get; set; }
/// <summary>
/// Hide source on UI when Simpl Sharing is enabled }
/// </summary>
[JsonProperty("disableSimpleRouting")]
public bool DisableSimpleRouting { get; set; }
public SourceListItem() public class SourceRouteListItem
{ {
Icon = "Blank"; [JsonProperty("sourceKey")]
} public string SourceKey { get; set; }
public override string ToString() [JsonProperty("destinationKey")]
{ public string DestinationKey { get; set; }
return $"{SourceKey}:{Name}";
}
}
public class SourceRouteListItem [JsonProperty("type")]
{ public eRoutingSignalType Type { get; set; }
[JsonProperty("sourceKey")] }
public string SourceKey { get; set; }
[JsonProperty("sourcePortKey")]
public string SourcePortKey { get; set; }
[JsonProperty("destinationKey")]
public string DestinationKey { get; set; }
[JsonProperty("destinationPortKey")]
public string DestinationPortKey { get; set; }
[JsonProperty("type")]
public eRoutingSignalType Type { get; set; }
}
/// <summary> /// <summary>
/// Defines the valid destination types for SourceListItems in a room /// Defines the valid destination types for SourceListItems in a room
@@ -186,12 +172,7 @@ namespace PepperDash.Essentials.Core
defaultDisplay, defaultDisplay,
leftDisplay, leftDisplay,
rightDisplay, rightDisplay,
centerDisplay,
programAudio, programAudio,
codecContent, codecContent
frontLeftDisplay,
frontRightDisplay,
rearLeftDisplay,
rearRightDisplay,
} }
} }

View File

@@ -10,6 +10,7 @@ using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Routing;
using Serilog.Events; using Serilog.Events;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core

View File

@@ -1,23 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Crestron.SimplSharp; using Crestron.SimplSharp;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.DM;
using Crestron.SimplSharpPro.DM.Endpoints;
using Crestron.SimplSharpPro.DM.Endpoints.Transmitters;
using Newtonsoft.Json; using Newtonsoft.Json;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Bridges;
using Serilog.Events; using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Linq;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
[Obsolete("Please use PepperDash.Essentials.Devices.Common, this will be removed in 2.1")] [Obsolete("Please use PepperDash.Essentials.Devices.Common, this will be removed in 2.1")]
public abstract class DisplayBase : EssentialsDevice, IHasFeedback, IRoutingSinkWithSwitching, IHasPowerControl, IWarmingCooling, IUsageTracking public abstract class DisplayBase : EssentialsDevice, IHasFeedback, IRoutingSinkWithSwitching, IHasPowerControl, IWarmingCooling, IUsageTracking
{ {
public event SourceInfoChangeHandler CurrentSourceChange; public event SourceInfoChangeHandler CurrentSourceChange;
public event InputChangedEventHandler InputChanged;
public string CurrentSourceInfoKey { get; set; } public string CurrentSourceInfoKey { get; set; }
public SourceListItem CurrentSourceInfo public SourceListItem CurrentSourceInfo
@@ -94,9 +96,7 @@ namespace PepperDash.Essentials.Core
} }
} }
public RoutingInputPort CurrentInputPort => throw new NotImplementedException(); public abstract void ExecuteSwitch(object selector);
public abstract void ExecuteSwitch(object selector);
protected void LinkDisplayToApi(DisplayBase displayDevice, BasicTriList trilist, uint joinStart, string joinMapKey, protected void LinkDisplayToApi(DisplayBase displayDevice, BasicTriList trilist, uint joinStart, string joinMapKey,
EiscApiAdvanced bridge) EiscApiAdvanced bridge)
@@ -266,16 +266,16 @@ namespace PepperDash.Essentials.Core
abstract protected Func<bool> PowerIsOnFeedbackFunc { get; } abstract protected Func<bool> PowerIsOnFeedbackFunc { get; }
// public static MockDisplay DefaultDisplay public static MockDisplay DefaultDisplay
// { {
// get get
// { {
// if (_DefaultDisplay == null) if (_DefaultDisplay == null)
// _DefaultDisplay = new MockDisplay("default", "Default Display"); _DefaultDisplay = new MockDisplay("default", "Default Display");
// return _DefaultDisplay; return _DefaultDisplay;
// } }
//} }
//static MockDisplay _DefaultDisplay; static MockDisplay _DefaultDisplay;
public TwoWayDisplayBase(string key, string name) public TwoWayDisplayBase(string key, string name)
: base(key, name) : base(key, name)

View File

@@ -0,0 +1,238 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.DM;
using Crestron.SimplSharpPro.DM.Endpoints;
using Crestron.SimplSharpPro.DM.Endpoints.Transmitters;
using PepperDash.Core;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Routing;
using PepperDash.Essentials.Core.Config;
using Serilog.Events;
namespace PepperDash.Essentials.Core
{
[Obsolete("Please use PepperDash.Essentials.Devices.Common, this will be removed in 2.1")]
public class MockDisplay : TwoWayDisplayBase, IBasicVolumeWithFeedback, IBridgeAdvanced
{
public RoutingInputPort HdmiIn1 { get; private set; }
public RoutingInputPort HdmiIn2 { get; private set; }
public RoutingInputPort HdmiIn3 { get; private set; }
public RoutingInputPort ComponentIn1 { get; private set; }
public RoutingInputPort VgaIn1 { get; private set; }
bool _PowerIsOn;
bool _IsWarmingUp;
bool _IsCoolingDown;
protected override Func<bool> PowerIsOnFeedbackFunc
{
get
{
return () =>
{
Debug.LogMessage(LogEventLevel.Verbose, this, "*************************************************** Display Power is {0}", _PowerIsOn ? "on" : "off");
return _PowerIsOn;
};
} }
protected override Func<bool> IsCoolingDownFeedbackFunc
{
get
{
return () =>
{
Debug.LogMessage(LogEventLevel.Verbose, this, "*************************************************** {0}", _IsCoolingDown ? "Display is cooling down" : "Display has finished cooling down");
return _IsCoolingDown;
};
}
}
protected override Func<bool> IsWarmingUpFeedbackFunc
{
get
{
return () =>
{
Debug.LogMessage(LogEventLevel.Verbose, this, "*************************************************** {0}", _IsWarmingUp ? "Display is warming up" : "Display has finished warming up");
return _IsWarmingUp;
};
}
}
protected override Func<string> CurrentInputFeedbackFunc { get { return () => "Not Implemented"; } }
int VolumeHeldRepeatInterval = 200;
ushort VolumeInterval = 655;
ushort _FakeVolumeLevel = 31768;
bool _IsMuted;
public MockDisplay(string key, string name)
: base(key, name)
{
HdmiIn1 = new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, null, this);
HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, null, this);
HdmiIn3 = new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, null, this);
ComponentIn1 = new RoutingInputPort(RoutingPortNames.ComponentIn, eRoutingSignalType.Video,
eRoutingPortConnectionType.Component, null, this);
VgaIn1 = new RoutingInputPort(RoutingPortNames.VgaIn, eRoutingSignalType.Video,
eRoutingPortConnectionType.Composite, null, this);
InputPorts.AddRange(new[] { HdmiIn1, HdmiIn2, HdmiIn3, ComponentIn1, VgaIn1 });
VolumeLevelFeedback = new IntFeedback(() => { return _FakeVolumeLevel; });
MuteFeedback = new BoolFeedback("MuteOn", () => _IsMuted);
WarmupTime = 10000;
CooldownTime = 10000;
}
public override void PowerOn()
{
if (!PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown)
{
_IsWarmingUp = true;
IsWarmingUpFeedback.InvokeFireUpdate();
// Fake power-up cycle
WarmupTimer = new CTimer(o =>
{
_IsWarmingUp = false;
_PowerIsOn = true;
IsWarmingUpFeedback.InvokeFireUpdate();
PowerIsOnFeedback.InvokeFireUpdate();
}, WarmupTime);
}
}
public override void PowerOff()
{
// If a display has unreliable-power off feedback, just override this and
// remove this check.
if (PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown)
{
_IsCoolingDown = true;
IsCoolingDownFeedback.InvokeFireUpdate();
// Fake cool-down cycle
CooldownTimer = new CTimer(o =>
{
Debug.LogMessage(LogEventLevel.Verbose, this, "Cooldown timer ending");
_IsCoolingDown = false;
IsCoolingDownFeedback.InvokeFireUpdate();
_PowerIsOn = false;
PowerIsOnFeedback.InvokeFireUpdate();
}, CooldownTime);
}
}
public override void PowerToggle()
{
if (PowerIsOnFeedback.BoolValue && !IsWarmingUpFeedback.BoolValue)
PowerOff();
else if (!PowerIsOnFeedback.BoolValue && !IsCoolingDownFeedback.BoolValue)
PowerOn();
}
public override void ExecuteSwitch(object selector)
{
Debug.LogMessage(LogEventLevel.Verbose, this, "ExecuteSwitch: {0}", selector);
if (!_PowerIsOn)
{
PowerOn();
}
}
#region IBasicVolumeWithFeedback Members
public IntFeedback VolumeLevelFeedback { get; private set; }
public void SetVolume(ushort level)
{
_FakeVolumeLevel = level;
VolumeLevelFeedback.InvokeFireUpdate();
}
public void MuteOn()
{
_IsMuted = true;
MuteFeedback.InvokeFireUpdate();
}
public void MuteOff()
{
_IsMuted = false;
MuteFeedback.InvokeFireUpdate();
}
public BoolFeedback MuteFeedback { get; private set; }
#endregion
#region IBasicVolumeControls Members
public void VolumeUp(bool pressRelease)
{
//while (pressRelease)
//{
Debug.LogMessage(LogEventLevel.Verbose, this, "Volume Down {0}", pressRelease);
if (pressRelease)
{
var newLevel = _FakeVolumeLevel + VolumeInterval;
SetVolume((ushort)newLevel);
CrestronEnvironment.Sleep(VolumeHeldRepeatInterval);
}
//}
}
public void VolumeDown(bool pressRelease)
{
//while (pressRelease)
//{
Debug.LogMessage(LogEventLevel.Verbose, this, "Volume Up {0}", pressRelease);
if (pressRelease)
{
var newLevel = _FakeVolumeLevel - VolumeInterval;
SetVolume((ushort)newLevel);
CrestronEnvironment.Sleep(VolumeHeldRepeatInterval);
}
//}
}
public void MuteToggle()
{
_IsMuted = !_IsMuted;
MuteFeedback.InvokeFireUpdate();
}
#endregion
public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
{
LinkDisplayToApi(this, trilist, joinStart, joinMapKey, bridge);
}
}
[Obsolete("Please use PepperDash.Essentials.Devices.Common, this will be removed in 2.1")]
public class MockDisplayFactory : EssentialsDeviceFactory<MockDisplay>
{
public MockDisplayFactory()
{
TypeNames = new List<string>() { "mockdisplay" };
}
public override EssentialsDevice BuildDevice(DeviceConfig dc)
{
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Mock Display Device");
return new MockDisplay(dc.Key, dc.Name);
}
}
}

View File

@@ -1,7 +1,7 @@
using Crestron.SimplSharp; using Crestron.SimplSharp;
using System.Reflection; using Crestron.SimplSharp.Reflection;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
@@ -14,13 +14,13 @@ namespace PepperDash.Essentials.Core
{ {
public class DeviceFactoryWrapper public class DeviceFactoryWrapper
{ {
public Type Type { get; set; } public CType CType { get; set; }
public string Description { get; set; } public string Description { get; set; }
public Func<DeviceConfig, IKeyed> FactoryMethod { get; set; } public Func<DeviceConfig, IKeyed> FactoryMethod { get; set; }
public DeviceFactoryWrapper() public DeviceFactoryWrapper()
{ {
Type = null; CType = null;
Description = "Not Available"; Description = "Not Available";
} }
} }
@@ -40,7 +40,7 @@ namespace PepperDash.Essentials.Core
{ {
try try
{ {
var factory = (IDeviceFactory)Activator.CreateInstance(type); var factory = (IDeviceFactory)Crestron.SimplSharp.Reflection.Activator.CreateInstance(type);
factory.LoadTypeFactories(); factory.LoadTypeFactories();
} }
catch (Exception e) catch (Exception e)
@@ -69,7 +69,7 @@ namespace PepperDash.Essentials.Core
DeviceFactory.FactoryMethods.Add(typeName, new DeviceFactoryWrapper() { FactoryMethod = method}); DeviceFactory.FactoryMethods.Add(typeName, new DeviceFactoryWrapper() { FactoryMethod = method});
} }
public static void AddFactoryForType(string typeName, string description, Type Type, Func<DeviceConfig, IKeyed> method) public static void AddFactoryForType(string typeName, string description, CType cType, Func<DeviceConfig, IKeyed> method)
{ {
//Debug.LogMessage(LogEventLevel.Debug, "Adding factory method for type '{0}'", typeName); //Debug.LogMessage(LogEventLevel.Debug, "Adding factory method for type '{0}'", typeName);
@@ -79,7 +79,7 @@ namespace PepperDash.Essentials.Core
return; return;
} }
var wrapper = new DeviceFactoryWrapper() { Type = Type, Description = description, FactoryMethod = method }; var wrapper = new DeviceFactoryWrapper() { CType = cType, Description = description, FactoryMethod = method };
DeviceFactory.FactoryMethods.Add(typeName, wrapper); DeviceFactory.FactoryMethods.Add(typeName, wrapper);
} }
@@ -149,7 +149,18 @@ namespace PepperDash.Essentials.Core
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogMessage(ex, "Exception occurred while creating device {0}: {1}", null, dc.Key, ex.Message); Debug.LogMessage(LogEventLevel.Error, "Exception occurred while creating device {0}: {1}", dc.Key, ex.Message);
Debug.LogMessage(LogEventLevel.Verbose, "{0}", ex.StackTrace);
if (ex.InnerException == null)
{
return null;
}
Debug.LogMessage(LogEventLevel.Error, "Inner exception while creating device {0}: {1}", dc.Key,
ex.InnerException.Message);
Debug.LogMessage(LogEventLevel.Verbose, "{0}", ex.InnerException.StackTrace);
return null; return null;
} }
} }
@@ -169,17 +180,17 @@ namespace PepperDash.Essentials.Core
foreach (var type in types.OrderBy(t => t.Key)) foreach (var type in types.OrderBy(t => t.Key))
{ {
var description = type.Value.Description; var description = type.Value.Description;
var Type = "Not Specified by Plugin"; var cType = "Not Specified by Plugin";
if (type.Value.Type != null) if (type.Value.CType != null)
{ {
Type = type.Value.Type.FullName; cType = type.Value.CType.FullName;
} }
CrestronConsole.ConsoleCommandResponse( CrestronConsole.ConsoleCommandResponse(
@"Type: '{0}' @"Type: '{0}'
Type: '{1}' CType: '{1}'
Description: {2}{3}", type.Key, Type, description, CrestronEnvironment.NewLine); Description: {2}{3}", type.Key, cType, description, CrestronEnvironment.NewLine);
} }
} }

View File

@@ -1,4 +1,14 @@
namespace PepperDash.Essentials.Core using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Reflection;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
namespace PepperDash.Essentials.Core
{ {
/// <summary> /// <summary>
/// Defines a class that is capable of loading device types /// Defines a class that is capable of loading device types

View File

@@ -1,5 +1,5 @@
using System.Reflection; using Crestron.SimplSharp.Reflection;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using Serilog.Events; using Serilog.Events;
@@ -25,7 +25,7 @@ namespace PepperDash.Essentials.Core
{ {
try try
{ {
var factory = (IProcessorExtensionDeviceFactory)Activator.CreateInstance(extension); var factory = (IProcessorExtensionDeviceFactory)Crestron.SimplSharp.Reflection.Activator.CreateInstance(extension);
factory.LoadFactories(); factory.LoadFactories();
} }
catch( Exception e ) catch( Exception e )
@@ -55,7 +55,7 @@ namespace PepperDash.Essentials.Core
ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, new DeviceFactoryWrapper() { FactoryMethod = method }); ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, new DeviceFactoryWrapper() { FactoryMethod = method });
} }
public static void AddFactoryForType(string extensionName, string description, Type Type, Func<DeviceConfig, IKeyed> method) public static void AddFactoryForType(string extensionName, string description, CType cType, Func<DeviceConfig, IKeyed> method)
{ {
//Debug.LogMessage(LogEventLevel.Debug, "Adding factory method for type '{0}'", typeName); //Debug.LogMessage(LogEventLevel.Debug, "Adding factory method for type '{0}'", typeName);
@@ -65,7 +65,7 @@ namespace PepperDash.Essentials.Core
return; return;
} }
var wrapper = new DeviceFactoryWrapper() { Type = Type, Description = description, FactoryMethod = method }; var wrapper = new DeviceFactoryWrapper() { CType = cType, Description = description, FactoryMethod = method };
ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, wrapper); ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, wrapper);
} }

View File

@@ -1,5 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp; using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp.CrestronXml; using Crestron.SimplSharp.CrestronXml;
@@ -10,10 +14,6 @@ using Newtonsoft.Json;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using Serilog.Events; using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PepperDash.Essentials.Core.Fusion namespace PepperDash.Essentials.Core.Fusion
{ {

View File

@@ -137,7 +137,6 @@ namespace PepperDash.Essentials.Core
public static void SetFilePathPrefix(string prefix) public static void SetFilePathPrefix(string prefix)
{ {
FilePathPrefix = prefix; FilePathPrefix = prefix;
Debug.LogMessage(LogEventLevel.Information, "File Path Prefix set to '{0}'", FilePathPrefix);
} }
static string _AssemblyVersion; static string _AssemblyVersion;

View File

@@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Reflection; using Crestron.SimplSharp.Reflection;
using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp; using Crestron.SimplSharp;
@@ -107,20 +107,19 @@ namespace PepperDash.Essentials.Core
} }
protected void AddJoins(Type type) protected void AddJoins(Type type)
{ {
var fields = var fields =
type.GetFields(BindingFlags.Public | BindingFlags.Instance) type.GetCType()
.Where(f => f.IsDefined(typeof (JoinNameAttribute), true)).ToList(); .GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(f => f.IsDefined(typeof (JoinNameAttribute), true));
Debug.LogMessage(LogEventLevel.Debug, "Got {fields} with JoinNameAttribute", fields.Count);
foreach (var field in fields) foreach (var field in fields)
{ {
var childClass = Convert.ChangeType(this, type, null); var childClass = Convert.ChangeType(this, type, null);
//this here is JoinMapBaseAdvanced, not the child class. JoinMapBaseAdvanced has no fields. var value = field.GetValue(childClass) as JoinDataComplete; //this here is JoinMapBaseAdvanced, not the child class. JoinMapBaseAdvanced has no fields.
if (!(field.GetValue(childClass) is JoinDataComplete value)) if (value == null)
{ {
Debug.LogMessage(LogEventLevel.Information, "Unable to cast base class to {0}", type.Name); Debug.LogMessage(LogEventLevel.Information, "Unable to cast base class to {0}", type.Name);
continue; continue;
@@ -130,7 +129,7 @@ namespace PepperDash.Essentials.Core
var joinName = value.GetNameAttribute(field); var joinName = value.GetNameAttribute(field);
if (string.IsNullOrEmpty(joinName)) continue; if (String.IsNullOrEmpty(joinName)) continue;
Joins.Add(joinName, value); Joins.Add(joinName, value);
} }
@@ -156,37 +155,29 @@ namespace PepperDash.Essentials.Core
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
var lineEnding = "\r\n"; // Get the joins of each type and print them
sb.AppendLine(String.Format("# {0}", GetType().Name));
sb.AppendLine();
sb.AppendLine("## Digitals");
sb.AppendLine();
// Get the joins of each type and print them
var digitals = var digitals =
Joins.Where(j => j.Value.Metadata.JoinType.HasFlag(eJoinType.Digital)) Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Digital) == eJoinType.Digital)
.ToDictionary(j => j.Key, j => j.Value); .ToDictionary(j => j.Key, j => j.Value);
var digitalSb = AppendJoinList(GetSortedJoins(digitals));
digitalSb.AppendLine("## Analogs");
digitalSb.AppendLine();
var analogs = Joins.Where(j => j.Value.Metadata.JoinType.HasFlag(eJoinType.Analog)) var analogs =
Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Analog) == eJoinType.Analog)
.ToDictionary(j => j.Key, j => j.Value); .ToDictionary(j => j.Key, j => j.Value);
var analogSb = AppendJoinList(GetSortedJoins(analogs));
analogSb.AppendLine("## Serials");
analogSb.AppendLine();
var serials = var serials =
Joins.Where(j => j.Value.Metadata.JoinType.HasFlag(eJoinType.Serial)) Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Serial) == eJoinType.Serial)
.ToDictionary(j => j.Key, j => j.Value); .ToDictionary(j => j.Key, j => j.Value);
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Digital join count {digitalCount} Analog join count {analogCount} Serial join count {serialCount}", null, digitals.Count, analogs.Count, serials.Count);
// Get the joins of each type and print them
sb.Append($"# {GetType().Name}\r\n");
sb.Append(lineEnding);
sb.Append($"## Digitals{lineEnding}");
sb.Append(lineEnding);
// Get the joins of each type and print them
var digitalSb = AppendJoinList(GetSortedJoins(digitals));
digitalSb.Append($"## Analogs{lineEnding}");
digitalSb.Append(lineEnding);
var analogSb = AppendJoinList(GetSortedJoins(analogs));
analogSb.Append($"## Serials{lineEnding}");
analogSb.Append(lineEnding);
var serialSb = AppendJoinList(GetSortedJoins(serials)); var serialSb = AppendJoinList(GetSortedJoins(serials));
sb.EnsureCapacity(sb.Length + digitalSb.Length + analogSb.Length + serialSb.Length); sb.EnsureCapacity(sb.Length + digitalSb.Length + analogSb.Length + serialSb.Length);
@@ -211,7 +202,7 @@ namespace PepperDash.Essentials.Core
private static void WriteJoinmapMarkdown(StringBuilder stringBuilder, string pluginType, string bridgeKey, string deviceKey) private static void WriteJoinmapMarkdown(StringBuilder stringBuilder, string pluginType, string bridgeKey, string deviceKey)
{ {
var fileName = string.Format("{0}{1}{2}__{3}__{4}.md", Global.FilePathPrefix, "joinMaps/", pluginType, bridgeKey, deviceKey); var fileName = String.Format("{0}{1}{2}__{3}__{4}.md", Global.FilePathPrefix, "joinMaps/", pluginType, bridgeKey, deviceKey);
using (var sw = new StreamWriter(fileName)) using (var sw = new StreamWriter(fileName))
{ {
@@ -239,7 +230,7 @@ namespace PepperDash.Essentials.Core
static StringBuilder AppendJoinList(List<KeyValuePair<string, JoinDataComplete>> joins) static StringBuilder AppendJoinList(List<KeyValuePair<string, JoinDataComplete>> joins)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
const string stringFormatter = "| {0} | {1} | {2} | {3} | {4} |\r\n"; const string stringFormatter = "| {0} | {1} | {2} | {3} | {4} |";
const int joinNumberLen = 11; const int joinNumberLen = 11;
const int joinSpanLen = 9; const int joinSpanLen = 9;
const int typeLen = 19; const int typeLen = 19;
@@ -247,25 +238,25 @@ namespace PepperDash.Essentials.Core
var descriptionLen = (from @join in joins select @join.Value into j select j.Metadata.Description.Length).Concat(new[] {11}).Max(); var descriptionLen = (from @join in joins select @join.Value into j select j.Metadata.Description.Length).Concat(new[] {11}).Max();
//build header //build header
sb.Append(string.Format(stringFormatter, sb.AppendLine(String.Format(stringFormatter,
string.Format("Join Number").PadRight(joinNumberLen, ' '), String.Format("Join Number").PadRight(joinNumberLen, ' '),
string.Format("Join Span").PadRight(joinSpanLen, ' '), String.Format("Join Span").PadRight(joinSpanLen, ' '),
string.Format("Description").PadRight(descriptionLen, ' '), String.Format("Description").PadRight(descriptionLen, ' '),
string.Format("Type").PadRight(typeLen, ' '), String.Format("Type").PadRight(typeLen, ' '),
string.Format("Capabilities").PadRight(capabilitiesLen, ' '))); String.Format("Capabilities").PadRight(capabilitiesLen, ' ')));
//build table seperator //build table seperator
sb.Append(string.Format(stringFormatter, sb.AppendLine(String.Format(stringFormatter,
new string('-', joinNumberLen), new String('-', joinNumberLen),
new string('-', joinSpanLen), new String('-', joinSpanLen),
new string('-', descriptionLen), new String('-', descriptionLen),
new string('-', typeLen), new String('-', typeLen),
new string('-', capabilitiesLen))); new String('-', capabilitiesLen)));
foreach (var join in joins) foreach (var join in joins)
{ {
sb.Append(join.Value.GetMarkdownFormattedData(stringFormatter, descriptionLen)); sb.AppendLine(join.Value.GetMarkdownFormattedData(stringFormatter, descriptionLen));
} }
sb.Append("\r\n"); sb.AppendLine();
return sb; return sb;
} }
@@ -420,10 +411,10 @@ namespace PepperDash.Essentials.Core
{ {
//Fixed Width Headers //Fixed Width Headers
var joinNumberLen = string.Format("Join Number").Length; var joinNumberLen = String.Format("Join Number").Length;
var joinSpanLen = string.Format("Join Span").Length; var joinSpanLen = String.Format("Join Span").Length;
var typeLen = string.Format("AnalogDigitalSerial").Length; var typeLen = String.Format("AnalogDigitalSerial").Length;
var capabilitiesLen = string.Format("ToFromFusion").Length; var capabilitiesLen = String.Format("ToFromFusion").Length;
//Track which one failed, if it did //Track which one failed, if it did
const string placeholder = "unknown"; const string placeholder = "unknown";
@@ -439,13 +430,13 @@ namespace PepperDash.Essentials.Core
try try
{ {
dataArray["joinNumber"] = string.Format("{0}", JoinNumber.ToString(CultureInfo.InvariantCulture).ReplaceIfNullOrEmpty(placeholder)).PadRight(joinNumberLen, ' '); dataArray["joinNumber"] = String.Format("{0}", JoinNumber.ToString(CultureInfo.InvariantCulture).ReplaceIfNullOrEmpty(placeholder)).PadRight(joinNumberLen, ' ');
dataArray["joinSpan"] = string.Format("{0}", JoinSpan.ToString(CultureInfo.InvariantCulture).ReplaceIfNullOrEmpty(placeholder)).PadRight(joinSpanLen, ' '); dataArray["joinSpan"] = String.Format("{0}", JoinSpan.ToString(CultureInfo.InvariantCulture).ReplaceIfNullOrEmpty(placeholder)).PadRight(joinSpanLen, ' ');
dataArray["description"] = string.Format("{0}", Metadata.Description.ReplaceIfNullOrEmpty(placeholder)).PadRight(descriptionLen, ' '); dataArray["description"] = String.Format("{0}", Metadata.Description.ReplaceIfNullOrEmpty(placeholder)).PadRight(descriptionLen, ' ');
dataArray["joinType"] = string.Format("{0}", Metadata.JoinType.ToString().ReplaceIfNullOrEmpty(placeholder)).PadRight(typeLen, ' '); dataArray["joinType"] = String.Format("{0}", Metadata.JoinType.ToString().ReplaceIfNullOrEmpty(placeholder)).PadRight(typeLen, ' ');
dataArray["capabilities"] = string.Format("{0}", Metadata.JoinCapabilities.ToString().ReplaceIfNullOrEmpty(placeholder)).PadRight(capabilitiesLen, ' '); dataArray["capabilities"] = String.Format("{0}", Metadata.JoinCapabilities.ToString().ReplaceIfNullOrEmpty(placeholder)).PadRight(capabilitiesLen, ' ');
return string.Format(stringFormatter, return String.Format(stringFormatter,
dataArray["joinNumber"], dataArray["joinNumber"],
dataArray["joinSpan"], dataArray["joinSpan"],
dataArray["description"], dataArray["description"],
@@ -463,8 +454,8 @@ namespace PepperDash.Essentials.Core
errorKey = item.Key; errorKey = item.Key;
break; break;
} }
Debug.LogMessage(LogEventLevel.Information, "Unable to decode join metadata {1}- {0}", e.Message, !string.IsNullOrEmpty(errorKey) ? (' ' + errorKey) : string.Empty); Debug.LogMessage(LogEventLevel.Information, "Unable to decode join metadata {1}- {0}", e.Message, !String.IsNullOrEmpty(errorKey) ? (' ' + errorKey) : String.Empty);
return string.Format(stringFormatter, return String.Format(stringFormatter,
dataArray["joinNumber"], dataArray["joinNumber"],
dataArray["joinSpan"], dataArray["joinSpan"],
dataArray["description"], dataArray["description"],
@@ -510,7 +501,7 @@ namespace PepperDash.Essentials.Core
public string GetNameAttribute(MemberInfo memberInfo) public string GetNameAttribute(MemberInfo memberInfo)
{ {
var name = string.Empty; var name = string.Empty;
var attribute = (JoinNameAttribute)Attribute.GetCustomAttribute(memberInfo, typeof(JoinNameAttribute)); var attribute = (JoinNameAttribute)CAttribute.GetCustomAttribute(memberInfo, typeof(JoinNameAttribute));
if (attribute == null) return name; if (attribute == null) return name;
@@ -523,13 +514,13 @@ namespace PepperDash.Essentials.Core
[AttributeUsage(AttributeTargets.All)] [AttributeUsage(AttributeTargets.All)]
public class JoinNameAttribute : Attribute public class JoinNameAttribute : CAttribute
{ {
private string _Name; private string _Name;
public JoinNameAttribute(string name) public JoinNameAttribute(string name)
{ {
Debug.LogMessage(LogEventLevel.Verbose, "Setting Attribute Name: {0}",null, name); Debug.LogMessage(LogEventLevel.Verbose, "Setting Attribute Name: {0}", name);
_Name = name; _Name = name;
} }

View File

@@ -110,7 +110,7 @@ namespace PepperDash.Essentials.Core.Monitoring
_uptimePollTimer = null; _uptimePollTimer = null;
} }
public void PollUptime(object obj) private void PollUptime(object obj)
{ {
var consoleResponse = string.Empty; var consoleResponse = string.Empty;
@@ -142,22 +142,19 @@ namespace PepperDash.Essentials.Core.Monitoring
_uptime = uptimeRaw.Substring(forIndex + 4); _uptime = uptimeRaw.Substring(forIndex + 4);
} }
public static void ProcessorReboot() private static void ProcessorReboot()
{ {
if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server) return; if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server) return;
Debug.LogMessage(LogEventLevel.Information, "Rebooting...");
var response = string.Empty;
var response = string.Empty;
CrestronConsole.SendControlSystemCommand("reboot", ref response); CrestronConsole.SendControlSystemCommand("reboot", ref response);
} }
public static void ProgramReset(uint index) private static void ProgramReset(uint index)
{ {
if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server) return; if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server) return;
Debug.LogMessage(LogEventLevel.Information, "Resetting Program {0}...", index);
if (index <= 0 || index > 10) return; if (index <= 0 || index > 10) return;
var cmd = string.Format("progreset -p:{0}", index); var cmd = string.Format("progreset -p:{0}", index);

View File

@@ -1,5 +1,8 @@
using PepperDash.Core; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
@@ -14,36 +17,9 @@ namespace PepperDash.Essentials.Core
{ {
private IPartitionStateProvider _partitionSensor; private IPartitionStateProvider _partitionSensor;
public bool IsInAutoMode { get; private set; } private bool isInAutoMode;
private bool _partitionPresent; private bool partitionPresent;
public bool PartitionPresent
{
get
{
if (IsInAutoMode)
{
return _partitionSensor.PartitionPresentFeedback.BoolValue;
}
return _partitionPresent;
}
set
{
if (_partitionPresent == value)
{
return;
}
_partitionPresent = value;
if (PartitionPresentFeedback != null)
{
PartitionPresentFeedback.FireUpdate();
}
}
}
public EssentialsPartitionController(string key, string name, IPartitionStateProvider sensor, bool defaultToManualMode, List<string> adjacentRoomKeys) public EssentialsPartitionController(string key, string name, IPartitionStateProvider sensor, bool defaultToManualMode, List<string> adjacentRoomKeys)
{ {
@@ -74,11 +50,11 @@ namespace PepperDash.Essentials.Core
PartitionPresentFeedback.FireUpdate(); PartitionPresentFeedback.FireUpdate();
} }
private void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e) void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
{ {
if (IsInAutoMode) if (isInAutoMode)
{ {
PartitionPresent = e.BoolValue; PartitionPresentFeedback.FireUpdate();
} }
} }
@@ -88,9 +64,7 @@ namespace PepperDash.Essentials.Core
public void SetAutoMode() public void SetAutoMode()
{ {
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Setting {Key} to Auto Mode", this); isInAutoMode = true;
IsInAutoMode = true;
if (PartitionPresentFeedback != null) if (PartitionPresentFeedback != null)
{ {
PartitionPresentFeedback.SetValueFunc(() => _partitionSensor.PartitionPresentFeedback.BoolValue); PartitionPresentFeedback.SetValueFunc(() => _partitionSensor.PartitionPresentFeedback.BoolValue);
@@ -102,64 +76,52 @@ namespace PepperDash.Essentials.Core
if (_partitionSensor != null) if (_partitionSensor != null)
{ {
_partitionSensor.PartitionPresentFeedback.OutputChange -= PartitionPresentFeedback_OutputChange;
_partitionSensor.PartitionPresentFeedback.OutputChange += PartitionPresentFeedback_OutputChange; _partitionSensor.PartitionPresentFeedback.OutputChange += PartitionPresentFeedback_OutputChange;
PartitionPresent = _partitionSensor.PartitionPresentFeedback.BoolValue;
} }
PartitionPresentFeedback.FireUpdate();
} }
public void SetManualMode() public void SetManualMode()
{ {
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Setting {Key} to Manual Mode", this); isInAutoMode = false;
IsInAutoMode = false;
if (PartitionPresentFeedback != null) if (PartitionPresentFeedback != null)
{ {
PartitionPresentFeedback.SetValueFunc(() => _partitionPresent); PartitionPresentFeedback.SetValueFunc(() => partitionPresent);
} }
else else
{ {
PartitionPresentFeedback = new BoolFeedback(() => _partitionPresent); PartitionPresentFeedback = new BoolFeedback(() => partitionPresent);
} }
if (_partitionSensor != null) if (_partitionSensor != null)
{ {
_partitionSensor.PartitionPresentFeedback.OutputChange -= PartitionPresentFeedback_OutputChange; _partitionSensor.PartitionPresentFeedback.OutputChange -= PartitionPresentFeedback_OutputChange;
PartitionPresent = _partitionSensor.PartitionPresentFeedback.BoolValue;
} }
PartitionPresentFeedback.FireUpdate();
} }
public void SetPartitionStatePresent() public void SetPartitionStatePresent()
{ {
if (!IsInAutoMode) if (!isInAutoMode)
{ {
PartitionPresent = true; partitionPresent = true;
PartitionPresentFeedback.FireUpdate(); PartitionPresentFeedback.FireUpdate();
} }
} }
public void SetPartitionStateNotPresent() public void SetPartitionStateNotPresent()
{ {
if (!IsInAutoMode) if (!isInAutoMode)
{ {
PartitionPresent = false; partitionPresent = false;
PartitionPresentFeedback.FireUpdate(); PartitionPresentFeedback.FireUpdate();
} }
} }
public void ToggglePartitionState() public void ToggglePartitionState()
{ {
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Toggling Partition State for {Key}", this); if (!isInAutoMode)
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"IsInAutoMode: {IsInAutoMode}", this);
if (!IsInAutoMode)
{ {
PartitionPresent = !PartitionPresent; partitionPresent = !partitionPresent;
PartitionPresentFeedback.FireUpdate(); PartitionPresentFeedback.FireUpdate();
} }
} }

View File

@@ -1,5 +1,9 @@
using System.Collections.Generic; using System;
using Newtonsoft.Json; using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core; using PepperDash.Core;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
@@ -9,11 +13,7 @@ namespace PepperDash.Essentials.Core
/// </summary> /// </summary>
public interface IPartitionStateProvider : IKeyName public interface IPartitionStateProvider : IKeyName
{ {
[JsonIgnore]
BoolFeedback PartitionPresentFeedback { get; } BoolFeedback PartitionPresentFeedback { get; }
[JsonProperty("partitionPresent")]
bool PartitionPresent { get; }
} }
/// <summary> /// <summary>
@@ -21,12 +21,8 @@ namespace PepperDash.Essentials.Core
/// </summary> /// </summary>
public interface IPartitionController : IPartitionStateProvider public interface IPartitionController : IPartitionStateProvider
{ {
[JsonProperty("adjacentRoomKeys")]
List<string> AdjacentRoomKeys { get; } List<string> AdjacentRoomKeys { get; }
[JsonProperty("isInAutoMode")]
bool IsInAutoMode { get; }
void SetPartitionStatePresent(); void SetPartitionStatePresent();
void SetPartitionStateNotPresent(); void SetPartitionStateNotPresent();

View File

@@ -11,9 +11,6 @@
<RootNamespace>PepperDash.Essentials.Core</RootNamespace> <RootNamespace>PepperDash.Essentials.Core</RootNamespace>
<Title>PepperDash Essentials Core</Title> <Title>PepperDash Essentials Core</Title>
<PackageId>PepperDash.Essentials.Core</PackageId> <PackageId>PepperDash.Essentials.Core</PackageId>
<InformationalVersion>$(Version)</InformationalVersion>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<Version>2.0.0-local</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType> <DebugType>full</DebugType>
@@ -25,8 +22,8 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.20.66" /> <PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.20.42" />
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-451" /> <PackageReference Include="PepperDashCore" Version="2.0.0-alpha-402" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Crestron\CrestronGenericBaseDevice.cs.orig" /> <None Include="Crestron\CrestronGenericBaseDevice.cs.orig" />

View File

@@ -1,14 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using Crestron.SimplSharp; using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharp.CrestronIO;
using System.Reflection; using Crestron.SimplSharp.Reflection;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using Serilog.Events; using Serilog.Events;
using Newtonsoft.Json;
namespace PepperDash.Essentials namespace PepperDash.Essentials
{ {
@@ -27,31 +27,24 @@ namespace PepperDash.Essentials
/// </summary> /// </summary>
static List<LoadedAssembly> LoadedPluginFolderAssemblies; static List<LoadedAssembly> LoadedPluginFolderAssemblies;
public static LoadedAssembly EssentialsAssembly { get; private set; }
public static LoadedAssembly PepperDashCoreAssembly { get; private set; }
public static List<LoadedAssembly> EssentialsPluginAssemblies { get; private set; }
/// <summary> /// <summary>
/// The directory to look in for .cplz plugin packages /// The directory to look in for .cplz plugin packages
/// </summary> /// </summary>
static string _pluginDirectory => Global.FilePathPrefix + "plugins"; static string _pluginDirectory = Global.FilePathPrefix + "plugins";
/// <summary> /// <summary>
/// The directory where plugins will be moved to and loaded from /// The directory where plugins will be moved to and loaded from
/// </summary> /// </summary>
static string _loadedPluginsDirectoryPath => _pluginDirectory + Global.DirectorySeparator + "loadedAssemblies"; static string _loadedPluginsDirectoryPath = _pluginDirectory + Global.DirectorySeparator + "loadedAssemblies";
// The temp directory where .cplz archives will be unzipped to // The temp directory where .cplz archives will be unzipped to
static string _tempDirectory => _pluginDirectory + Global.DirectorySeparator + "temp"; static string _tempDirectory = _pluginDirectory + Global.DirectorySeparator + "temp";
static PluginLoader() static PluginLoader()
{ {
LoadedAssemblies = new List<LoadedAssembly>(); LoadedAssemblies = new List<LoadedAssembly>();
LoadedPluginFolderAssemblies = new List<LoadedAssembly>(); LoadedPluginFolderAssemblies = new List<LoadedAssembly>();
EssentialsPluginAssemblies = new List<LoadedAssembly>();
} }
/// <summary> /// <summary>
@@ -66,7 +59,7 @@ namespace PepperDash.Essentials
Debug.LogMessage(LogEventLevel.Verbose, "Found {0} Assemblies", assemblyFiles.Length); Debug.LogMessage(LogEventLevel.Verbose, "Found {0} Assemblies", assemblyFiles.Length);
foreach (var fi in assemblyFiles.Where(fi => fi.Name.Contains("Essentials") || fi.Name.Contains("PepperDash"))) foreach (var fi in assemblyFiles)
{ {
string version = string.Empty; string version = string.Empty;
Assembly assembly = null; Assembly assembly = null;
@@ -76,7 +69,6 @@ namespace PepperDash.Essentials
case ("PepperDashEssentials.dll"): case ("PepperDashEssentials.dll"):
{ {
version = Global.AssemblyVersion; version = Global.AssemblyVersion;
EssentialsAssembly = new LoadedAssembly(fi.Name, version, assembly);
break; break;
} }
case ("PepperDash_Essentials_Core.dll"): case ("PepperDash_Essentials_Core.dll"):
@@ -89,12 +81,9 @@ namespace PepperDash.Essentials
version = Global.AssemblyVersion; version = Global.AssemblyVersion;
break; break;
} }
case ("PepperDashCore.dll"): case ("PepperDash_Core.dll"):
{ {
Debug.LogMessage(LogEventLevel.Verbose, "Found PepperDash_Core.dll"); version = PepperDash.Core.Debug.PepperDashCoreVersion;
version = Debug.PepperDashCoreVersion;
Debug.LogMessage(LogEventLevel.Verbose, "PepperDash_Core Version: {0}", version);
PepperDashCoreAssembly = new LoadedAssembly(fi.Name, version, assembly);
break; break;
} }
} }
@@ -130,30 +119,23 @@ namespace PepperDash.Essentials
/// <param name="fileName"></param> /// <param name="fileName"></param>
static LoadedAssembly LoadAssembly(string filePath) static LoadedAssembly LoadAssembly(string filePath)
{ {
try //Debug.LogMessage(LogEventLevel.Verbose, "Attempting to load {0}", filePath);
var assembly = Assembly.LoadFrom(filePath);
if (assembly != null)
{ {
//Debug.LogMessage(LogEventLevel.Verbose, "Attempting to load {0}", filePath); var assyVersion = GetAssemblyVersion(assembly);
var assembly = Assembly.LoadFrom(filePath);
if (assembly != null)
{
var assyVersion = GetAssemblyVersion(assembly);
var loadedAssembly = new LoadedAssembly(assembly.GetName().Name, assyVersion, assembly); var loadedAssembly = new LoadedAssembly(assembly.GetName().Name, assyVersion, assembly);
LoadedAssemblies.Add(loadedAssembly); LoadedAssemblies.Add(loadedAssembly);
Debug.LogMessage(LogEventLevel.Information, "Loaded assembly '{0}', version {1}", loadedAssembly.Name, loadedAssembly.Version); Debug.LogMessage(LogEventLevel.Information, "Loaded assembly '{0}', version {1}", loadedAssembly.Name, loadedAssembly.Version);
return loadedAssembly; return loadedAssembly;
}
else
{
Debug.LogMessage(LogEventLevel.Information, "Unable to load assembly: '{0}'", filePath);
}
return null;
} catch(Exception ex)
{
Debug.LogMessage(ex, "Error loading assembly from {path}", null, filePath);
return null;
} }
else
{
Debug.LogMessage(LogEventLevel.Information, "Unable to load assembly: '{0}'", filePath);
}
return null;
} }
@@ -162,7 +144,7 @@ namespace PepperDash.Essentials
/// </summary> /// </summary>
/// <param name="assembly"></param> /// <param name="assembly"></param>
/// <returns></returns> /// <returns></returns>
public static string GetAssemblyVersion(Assembly assembly) static string GetAssemblyVersion(Assembly assembly)
{ {
var ver = assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false); var ver = assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false);
if (ver != null && ver.Length > 0) if (ver != null && ver.Length > 0)
@@ -208,19 +190,12 @@ namespace PepperDash.Essentials
/// <param name="command"></param> /// <param name="command"></param>
public static void ReportAssemblyVersions(string command) public static void ReportAssemblyVersions(string command)
{ {
CrestronConsole.ConsoleCommandResponse("Essentials Version: {0}" + CrestronEnvironment.NewLine, Global.AssemblyVersion);
CrestronConsole.ConsoleCommandResponse("PepperDash Core Version: {0}" + CrestronEnvironment.NewLine, PepperDashCoreAssembly.Version); CrestronConsole.ConsoleCommandResponse("Loaded Assemblies:" + CrestronEnvironment.NewLine);
CrestronConsole.ConsoleCommandResponse("Essentials Plugin Versions:" + CrestronEnvironment.NewLine); foreach (var assembly in LoadedAssemblies)
foreach (var assembly in EssentialsPluginAssemblies)
{ {
CrestronConsole.ConsoleCommandResponse("{0} Version: {1}" + CrestronEnvironment.NewLine, assembly.Name, assembly.Version); CrestronConsole.ConsoleCommandResponse("{0} Version: {1}" + CrestronEnvironment.NewLine, assembly.Name, assembly.Version);
} }
//CrestronConsole.ConsoleCommandResponse("Loaded Assemblies:" + CrestronEnvironment.NewLine);
//foreach (var assembly in LoadedAssemblies)
//{
// CrestronConsole.ConsoleCommandResponse("{0} Version: {1}" + CrestronEnvironment.NewLine, assembly.Name, assembly.Version);
//}
} }
/// <summary> /// <summary>
/// Moves any .dll assemblies not already loaded from the plugins folder to loadedPlugins folder /// Moves any .dll assemblies not already loaded from the plugins folder to loadedPlugins folder
@@ -379,7 +354,7 @@ namespace PepperDash.Essentials
try try
{ {
var assy = loadedAssembly.Assembly; var assy = loadedAssembly.Assembly;
Type[] types = {}; CType[] types = {};
try try
{ {
types = assy.GetTypes(); types = assy.GetTypes();
@@ -400,7 +375,7 @@ namespace PepperDash.Essentials
if (typeof (IPluginDeviceFactory).IsAssignableFrom(type) && !type.IsAbstract) if (typeof (IPluginDeviceFactory).IsAssignableFrom(type) && !type.IsAbstract)
{ {
var plugin = var plugin =
(IPluginDeviceFactory)Activator.CreateInstance(type); (IPluginDeviceFactory) Crestron.SimplSharp.Reflection.Activator.CreateInstance(type);
LoadCustomPlugin(plugin, loadedAssembly); LoadCustomPlugin(plugin, loadedAssembly);
} }
} }
@@ -457,9 +432,6 @@ namespace PepperDash.Essentials
Debug.LogMessage(LogEventLevel.Information, "Loading plugin: {0}", loadedAssembly.Name); Debug.LogMessage(LogEventLevel.Information, "Loading plugin: {0}", loadedAssembly.Name);
plugin.LoadTypeFactories(); plugin.LoadTypeFactories();
if(!EssentialsPluginAssemblies.Contains(loadedAssembly))
EssentialsPluginAssemblies.Add(loadedAssembly);
} }
/// <summary> /// <summary>
@@ -467,7 +439,7 @@ namespace PepperDash.Essentials
/// </summary> /// </summary>
/// <param name="type"></param> /// <param name="type"></param>
/// <param name="loadPlugin"></param> /// <param name="loadPlugin"></param>
static void LoadCustomLegacyPlugin(Type type, MethodInfo loadPlugin, LoadedAssembly loadedAssembly) static void LoadCustomLegacyPlugin(CType type, MethodInfo loadPlugin, LoadedAssembly loadedAssembly)
{ {
Debug.LogMessage(LogEventLevel.Verbose, "LoadPlugin method found in {0}", type.Name); Debug.LogMessage(LogEventLevel.Verbose, "LoadPlugin method found in {0}", type.Name);
@@ -514,8 +486,6 @@ namespace PepperDash.Essentials
/// </summary> /// </summary>
public static void LoadPlugins() public static void LoadPlugins()
{ {
Debug.LogMessage(LogEventLevel.Information, "Attempting to Load Plugins from {_pluginDirectory}", _pluginDirectory);
if (Directory.Exists(_pluginDirectory)) if (Directory.Exists(_pluginDirectory))
{ {
Debug.LogMessage(LogEventLevel.Information, "Plugins directory found, checking for plugins"); Debug.LogMessage(LogEventLevel.Information, "Plugins directory found, checking for plugins");
@@ -544,11 +514,8 @@ namespace PepperDash.Essentials
/// </summary> /// </summary>
public class LoadedAssembly public class LoadedAssembly
{ {
[JsonProperty("name")]
public string Name { get; private set; } public string Name { get; private set; }
[JsonProperty("version")]
public string Version { get; private set; } public string Version { get; private set; }
[JsonIgnore]
public Assembly Assembly { get; private set; } public Assembly Assembly { get; private set; }
public LoadedAssembly(string name, string version, Assembly assembly) public LoadedAssembly(string name, string version, Assembly assembly)

View File

@@ -130,8 +130,6 @@ namespace PepperDash.Essentials.Core.Presets
var pl = JsonConvert.DeserializeObject<PresetsList>(File.ReadToEnd(_filePath, Encoding.ASCII)); var pl = JsonConvert.DeserializeObject<PresetsList>(File.ReadToEnd(_filePath, Encoding.ASCII));
Name = pl.Name; Name = pl.Name;
PresetsList = pl.Channels; PresetsList = pl.Channels;
Debug.LogMessage(LogEventLevel.Verbose, this, "Loaded {0} presets", PresetsList.Count);
} }
catch (Exception e) catch (Exception e)
{ {

View File

@@ -1,12 +1,11 @@
using Crestron.SimplSharp; using System;
using PepperDash.Core;
using PepperDash.Core.Logging;
using Serilog.Events;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Text;
using System.Threading.Tasks; using Crestron.SimplSharp;
using PepperDash.Core;
using Serilog.Events;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
@@ -18,40 +17,12 @@ namespace PepperDash.Essentials.Core
private List<IEssentialsRoom> _rooms; private List<IEssentialsRoom> _rooms;
public List<IKeyName> Rooms private bool isInAutoMode;
{
get
{
return _rooms.Cast<IKeyName>().ToList();
}
}
private bool _isInAutoMode;
public bool IsInAutoMode
{
get
{
return _isInAutoMode;
}
set
{
if (value == _isInAutoMode)
{
return;
}
_isInAutoMode = value;
IsInAutoModeFeedback.FireUpdate();
}
}
private CTimer _scenarioChangeDebounceTimer; private CTimer _scenarioChangeDebounceTimer;
private int _scenarioChangeDebounceTimeSeconds = 10; // default to 10s private int _scenarioChangeDebounceTimeSeconds = 10; // default to 10s
private Mutex _scenarioChange = new Mutex();
public EssentialsRoomCombiner(string key, EssentialsRoomCombinerPropertiesConfig props) public EssentialsRoomCombiner(string key, EssentialsRoomCombinerPropertiesConfig props)
: base(key) : base(key)
{ {
@@ -65,14 +36,14 @@ namespace PepperDash.Essentials.Core
_scenarioChangeDebounceTimeSeconds = _propertiesConfig.ScenarioChangeDebounceTimeSeconds; _scenarioChangeDebounceTimeSeconds = _propertiesConfig.ScenarioChangeDebounceTimeSeconds;
} }
IsInAutoModeFeedback = new BoolFeedback(() => _isInAutoMode); IsInAutoModeFeedback = new BoolFeedback(() => isInAutoMode);
// default to auto mode // default to auto mode
IsInAutoMode = true; isInAutoMode = true;
if (_propertiesConfig.defaultToManualMode) if (_propertiesConfig.defaultToManualMode)
{ {
IsInAutoMode = false; isInAutoMode = false;
} }
IsInAutoModeFeedback.FireUpdate(); IsInAutoModeFeedback.FireUpdate();
@@ -84,16 +55,8 @@ namespace PepperDash.Essentials.Core
SetupPartitionStateProviders(); SetupPartitionStateProviders();
SetRooms(); SetRooms();
});
if (isInAutoMode)
// Subscribe to the AllDevicesInitialized event
// We need to wait until all devices are initialized in case
// any actions are dependent on 3rd party devices already being
// connected and initialized
DeviceManager.AllDevicesInitialized += (o, a) =>
{
if (IsInAutoMode)
{ {
DetermineRoomCombinationScenario(); DetermineRoomCombinationScenario();
} }
@@ -101,10 +64,10 @@ namespace PepperDash.Essentials.Core
{ {
SetRoomCombinationScenario(_propertiesConfig.defaultScenarioKey); SetRoomCombinationScenario(_propertiesConfig.defaultScenarioKey);
} }
}; });
} }
private void CreateScenarios() void CreateScenarios()
{ {
foreach (var scenarioConfig in _propertiesConfig.Scenarios) foreach (var scenarioConfig in _propertiesConfig.Scenarios)
{ {
@@ -113,29 +76,21 @@ namespace PepperDash.Essentials.Core
} }
} }
private void SetRooms() void SetRooms()
{ {
_rooms = new List<IEssentialsRoom>(); _rooms = new List<IEssentialsRoom>();
foreach (var roomKey in _propertiesConfig.RoomKeys) foreach (var roomKey in _propertiesConfig.RoomKeys)
{ {
var room = DeviceManager.GetDeviceForKey(roomKey); var room = DeviceManager.GetDeviceForKey(roomKey) as IEssentialsRoom;
if (room != null)
if (DeviceManager.GetDeviceForKey(roomKey) is IEssentialsRoom essentialsRoom)
{ {
_rooms.Add(essentialsRoom); _rooms.Add(room);
} }
} }
var rooms = DeviceManager.AllDevices.OfType<IEssentialsRoom>().Cast<Device>();
foreach (var room in rooms)
{
room.Deactivate();
}
} }
private void SetupPartitionStateProviders() void SetupPartitionStateProviders()
{ {
foreach (var pConfig in _propertiesConfig.Partitions) foreach (var pConfig in _propertiesConfig.Partitions)
{ {
@@ -149,18 +104,14 @@ namespace PepperDash.Essentials.Core
} }
} }
private void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e) void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
{ {
StartDebounceTimer(); StartDebounceTimer();
} }
private void StartDebounceTimer() void StartDebounceTimer()
{ {
// default to 500ms for manual mode var time = _scenarioChangeDebounceTimeSeconds * 1000;
var time = 500;
// if in auto mode, debounce the scenario change
if (IsInAutoMode) time = _scenarioChangeDebounceTimeSeconds * 1000;
if (_scenarioChangeDebounceTimer == null) if (_scenarioChangeDebounceTimer == null)
{ {
@@ -175,7 +126,7 @@ namespace PepperDash.Essentials.Core
/// <summary> /// <summary>
/// Determines the current room combination scenario based on the state of the partition sensors /// Determines the current room combination scenario based on the state of the partition sensors
/// </summary> /// </summary>
private void DetermineRoomCombinationScenario() void DetermineRoomCombinationScenario()
{ {
if (_scenarioChangeDebounceTimer != null) if (_scenarioChangeDebounceTimer != null)
{ {
@@ -183,20 +134,14 @@ namespace PepperDash.Essentials.Core
_scenarioChangeDebounceTimer = null; _scenarioChangeDebounceTimer = null;
} }
this.LogInformation("Determining Combination Scenario");
var currentScenario = RoomCombinationScenarios.FirstOrDefault((s) => var currentScenario = RoomCombinationScenarios.FirstOrDefault((s) =>
{ {
this.LogDebug("Checking scenario {scenarioKey}", s.Key);
// iterate the partition states // iterate the partition states
foreach (var partitionState in s.PartitionStates) foreach (var partitionState in s.PartitionStates)
{ {
this.LogDebug("checking PartitionState {partitionStateKey}", partitionState.PartitionKey);
// get the partition by key // get the partition by key
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionState.PartitionKey)); var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionState.PartitionKey));
this.LogDebug("Expected State: {partitionPresent} Actual State: {partitionState}", partitionState.PartitionPresent, partition.PartitionPresentFeedback.BoolValue);
if (partition != null && partitionState.PartitionPresent != partition.PartitionPresentFeedback.BoolValue) if (partition != null && partitionState.PartitionPresent != partition.PartitionPresentFeedback.BoolValue)
{ {
// the partition can't be found or the state doesn't match // the partition can't be found or the state doesn't match
@@ -209,41 +154,10 @@ namespace PepperDash.Essentials.Core
if (currentScenario != null) if (currentScenario != null)
{ {
this.LogInformation("Found combination Scenario {scenarioKey}", currentScenario.Key); CurrentScenario = currentScenario;
ChangeScenario(currentScenario);
} }
} }
private async Task ChangeScenario(IRoomCombinationScenario newScenario)
{
if (newScenario == _currentScenario)
{
return;
}
// Deactivate the old scenario first
if (_currentScenario != null)
{
Debug.LogMessage(LogEventLevel.Information, "Deactivating scenario {currentScenario}", this, _currentScenario.Name);
await _currentScenario.Deactivate();
}
_currentScenario = newScenario;
// Activate the new scenario
if (_currentScenario != null)
{
Debug.LogMessage(LogEventLevel.Debug, $"Current Scenario: {_currentScenario.Name}", this);
await _currentScenario.Activate();
}
RoomCombinationScenarioChanged?.Invoke(this, new EventArgs());
}
#region IEssentialsRoomCombiner Members #region IEssentialsRoomCombiner Members
public event EventHandler<EventArgs> RoomCombinationScenarioChanged; public event EventHandler<EventArgs> RoomCombinationScenarioChanged;
@@ -254,42 +168,53 @@ namespace PepperDash.Essentials.Core
{ {
return _currentScenario; return _currentScenario;
} }
private set
{
if (value != _currentScenario)
{
// Deactivate the old scenario first
if (_currentScenario != null)
{
_currentScenario.Deactivate();
}
_currentScenario = value;
// Activate the new scenario
if (_currentScenario != null)
{
_currentScenario.Activate();
Debug.LogMessage(LogEventLevel.Debug, this, "Current Scenario: {0}", _currentScenario.Name);
}
var handler = RoomCombinationScenarioChanged;
if (handler != null)
{
handler(this, new EventArgs());
}
}
}
} }
public BoolFeedback IsInAutoModeFeedback { get; private set; } public BoolFeedback IsInAutoModeFeedback { get; private set; }
public void SetAutoMode() public void SetAutoMode()
{ {
IsInAutoMode = true; isInAutoMode = true;
IsInAutoModeFeedback.FireUpdate();
foreach (var partition in Partitions)
{
partition.SetAutoMode();
}
DetermineRoomCombinationScenario();
} }
public void SetManualMode() public void SetManualMode()
{ {
IsInAutoMode = false; isInAutoMode = false;
IsInAutoModeFeedback.FireUpdate();
foreach (var partition in Partitions)
{
partition.SetManualMode();
}
} }
public void ToggleMode() public void ToggleMode()
{ {
if (IsInAutoMode) isInAutoMode = !isInAutoMode;
{ IsInAutoModeFeedback.FireUpdate();
SetManualMode();
}
else
{
SetAutoMode();
}
} }
public List<IRoomCombinationScenario> RoomCombinationScenarios { get; private set; } public List<IRoomCombinationScenario> RoomCombinationScenarios { get; private set; }
@@ -298,7 +223,7 @@ namespace PepperDash.Essentials.Core
public void TogglePartitionState(string partitionKey) public void TogglePartitionState(string partitionKey)
{ {
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey)); var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey)) as IPartitionController;
if (partition != null) if (partition != null)
{ {
@@ -308,7 +233,7 @@ namespace PepperDash.Essentials.Core
public void SetRoomCombinationScenario(string scenarioKey) public void SetRoomCombinationScenario(string scenarioKey)
{ {
if (IsInAutoMode) if (isInAutoMode)
{ {
Debug.LogMessage(LogEventLevel.Information, this, "Cannot set room combination scenario when in auto mode. Set to auto mode first."); Debug.LogMessage(LogEventLevel.Information, this, "Cannot set room combination scenario when in auto mode. Set to auto mode first.");
return; return;

View File

@@ -1,7 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Linq;
using Newtonsoft.Json; using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core; using PepperDash.Core;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
@@ -19,21 +21,13 @@ namespace PepperDash.Essentials.Core
/// <summary> /// <summary>
/// The current room combination scenario /// The current room combination scenario
/// </summary> /// </summary>
[JsonProperty("currentScenario")]
IRoomCombinationScenario CurrentScenario { get; } IRoomCombinationScenario CurrentScenario { get; }
/// <summary> /// <summary>
/// When true, indicates the current mode is auto mode /// When true, indicates the current mode is auto mode
/// </summary> /// </summary>
[JsonIgnore]
BoolFeedback IsInAutoModeFeedback {get;} BoolFeedback IsInAutoModeFeedback {get;}
[JsonProperty("isInAutoMode")]
bool IsInAutoMode { get; }
[JsonProperty("rooms")]
List<IKeyName> Rooms { get; }
/// <summary> /// <summary>
/// Sets auto mode /// Sets auto mode
/// </summary> /// </summary>
@@ -52,13 +46,11 @@ namespace PepperDash.Essentials.Core
/// <summary> /// <summary>
/// The available room combinatino scenarios /// The available room combinatino scenarios
/// </summary> /// </summary>
[JsonProperty("roomCombinationScenarios")]
List<IRoomCombinationScenario> RoomCombinationScenarios { get; } List<IRoomCombinationScenario> RoomCombinationScenarios { get; }
/// <summary> /// <summary>
/// The partition /// The partition
/// </summary> /// </summary>
[JsonProperty("partitions")]
List<IPartitionController> Partitions { get; } List<IPartitionController> Partitions { get; }
/// <summary> /// <summary>
@@ -79,32 +71,26 @@ namespace PepperDash.Essentials.Core
/// <summary> /// <summary>
/// When true, indicates that this room combination scenario is active /// When true, indicates that this room combination scenario is active
/// </summary> /// </summary>
[JsonIgnore]
BoolFeedback IsActiveFeedback { get; } BoolFeedback IsActiveFeedback { get; }
[JsonProperty("isActive")]
bool IsActive { get; }
/// <summary> /// <summary>
/// Activates this room combination scenario /// Activates this room combination scenario
/// </summary> /// </summary>
Task Activate(); void Activate();
/// <summary> /// <summary>
/// Deactivates this room combination scenario /// Deactivates this room combination scenario
/// </summary> /// </summary>
Task Deactivate(); void Deactivate();
/// <summary> /// <summary>
/// The state of the partitions that would activate this scenario /// The state of the partitions that would activate this scenario
/// </summary> /// </summary>
[JsonProperty("partitionStates")]
List<PartitionState> PartitionStates { get; } List<PartitionState> PartitionStates { get; }
/// <summary> /// <summary>
/// The mapping of UIs by key to rooms by key /// The mapping of UIs by key to rooms by key
/// </summary> /// </summary>
[JsonProperty("uiMap")]
Dictionary<string, string> UiMap { get; set; } Dictionary<string, string> UiMap { get; set; }
} }

View File

@@ -1,50 +1,35 @@
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Core.Logging; using System;
using Serilog.Events;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using Newtonsoft.Json;
using Serilog.Events;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
/// <summary> /// <summary>
/// Represents a room combination scenario /// Represents a room combination scenario
/// </summary> /// </summary>
public class RoomCombinationScenario : IRoomCombinationScenario, IKeyName public class RoomCombinationScenario: IRoomCombinationScenario
{ {
private RoomCombinationScenarioConfig _config; private RoomCombinationScenarioConfig _config;
[JsonProperty("key")]
public string Key { get; set; } public string Key { get; set; }
[JsonProperty("name")]
public string Name { get; set; } public string Name { get; set; }
[JsonProperty("partitionStates")]
public List<PartitionState> PartitionStates { get; private set; } public List<PartitionState> PartitionStates { get; private set; }
[JsonProperty("uiMap")]
public Dictionary<string, string> UiMap { get; set; } public Dictionary<string, string> UiMap { get; set; }
private bool _isActive; private bool _isActive;
[JsonProperty("isActive")]
public bool IsActive
{
get { return _isActive; }
set
{
if (value == _isActive)
{
return;
}
_isActive = value;
IsActiveFeedback.FireUpdate();
}
}
[JsonIgnore]
public BoolFeedback IsActiveFeedback { get; private set; } public BoolFeedback IsActiveFeedback { get; private set; }
private List<DeviceActionWrapper> activationActions; private List<DeviceActionWrapper> activationActions;
@@ -70,40 +55,36 @@ namespace PepperDash.Essentials.Core
IsActiveFeedback = new BoolFeedback(() => _isActive); IsActiveFeedback = new BoolFeedback(() => _isActive);
} }
public async Task Activate() public void Activate()
{ {
this.LogInformation("Activating Scenario {name} with {activationActionCount} action(s) defined", Name, activationActions.Count); Debug.LogMessage(LogEventLevel.Debug, "Activating Scenario: '{0}' with {1} action(s) defined", Name, activationActions.Count);
List<Task> tasks = new List<Task>();
if (activationActions != null) if (activationActions != null)
{ {
foreach (var action in activationActions) foreach (var action in activationActions)
{ {
this.LogInformation("Running Activation action {@action}", action); DeviceJsonApi.DoDeviceAction(action);
await DeviceJsonApi.DoDeviceActionAsync(action);
} }
} }
IsActive = true; _isActive = true;
IsActiveFeedback.FireUpdate();
} }
public async Task Deactivate() public void Deactivate()
{ {
this.LogInformation("Deactivating Scenario {name} with {deactivationActionCount} action(s) defined", Name, deactivationActions.Count); Debug.LogMessage(LogEventLevel.Debug, "Deactivating Scenario: '{0}' with {1} action(s) defined", Name, deactivationActions.Count);
List<Task> tasks = new List<Task>();
if (deactivationActions != null) if (deactivationActions != null)
{ {
foreach (var action in deactivationActions) foreach (var action in deactivationActions)
{ {
this.LogInformation("Running deactivation action {actionDeviceKey}:{actionMethod}", action.DeviceKey, action.MethodName); DeviceJsonApi.DoDeviceAction(action);
await DeviceJsonApi.DoDeviceActionAsync(action);
} }
} }
IsActive = false; _isActive = false;
IsActiveFeedback.FireUpdate();
} }
} }

View File

@@ -24,7 +24,6 @@ namespace PepperDash.Essentials.Room.Config
//switch on emergency type here. Right now only contact and shutdown //switch on emergency type here. Right now only contact and shutdown
var e = new EssentialsRoomEmergencyContactClosure(room.Key + "-emergency", props.Emergency, room); var e = new EssentialsRoomEmergencyContactClosure(room.Key + "-emergency", props.Emergency, room);
DeviceManager.AddDevice(e); DeviceManager.AddDevice(e);
return e;
} }
return null; return null;
} }
@@ -199,12 +198,6 @@ namespace PepperDash.Essentials.Room.Config
public string SourceListKey { get; set; } public string SourceListKey { get; set; }
[JsonProperty("destinationListKey")] [JsonProperty("destinationListKey")]
public string DestinationListKey { get; set; } public string DestinationListKey { get; set; }
[JsonProperty("audioControlPointListKey")]
public string AudioControlPointListKey { get; set; }
[JsonProperty("cameraListKey")]
public string CameraListKey { get; set; }
[JsonProperty("defaultSourceItem")] [JsonProperty("defaultSourceItem")]
public string DefaultSourceItem { get; set; } public string DefaultSourceItem { get; set; }
/// <summary> /// <summary>

View File

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

View File

@@ -4,16 +4,12 @@ using PepperDash.Essentials.Room.Config;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
public class EssentialsRoomEmergencyContactClosure : EssentialsRoomEmergencyBase, IEssentialsRoomEmergency public class EssentialsRoomEmergencyContactClosure : EssentialsRoomEmergencyBase
{ {
public event EventHandler<EventArgs> EmergencyStateChange;
IEssentialsRoom Room; IEssentialsRoom Room;
string Behavior; string Behavior;
bool TriggerOnClose; bool TriggerOnClose;
public bool InEmergency { get; private set; }
public EssentialsRoomEmergencyContactClosure(string key, EssentialsRoomEmergencyConfig config, IEssentialsRoom room) : public EssentialsRoomEmergencyContactClosure(string key, EssentialsRoomEmergencyConfig config, IEssentialsRoom room) :
base(key) base(key)
{ {
@@ -29,49 +25,14 @@ namespace PepperDash.Essentials.Core
cs.DigitalInputPorts[portNum].StateChange += EsentialsRoomEmergencyContactClosure_StateChange; 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; Behavior = config.Behavior;
TriggerOnClose = config.Trigger.TriggerOnClose; 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) void EsentialsRoomEmergencyContactClosure_StateChange(DigitalInput digitalInput, DigitalInputEventArgs args)
{ {
ContactClosure_StateChange(args.State); if (args.State && TriggerOnClose || !args.State && !TriggerOnClose)
}
void ContactClosure_StateChange(bool portState)
{
if (portState && TriggerOnClose || !portState && !TriggerOnClose)
{
InEmergency = true;
if (EmergencyStateChange != null)
EmergencyStateChange(this, new EventArgs());
RunEmergencyBehavior(); RunEmergencyBehavior();
}
else
{
InEmergency = false;
if (EmergencyStateChange != null)
EmergencyStateChange(this, new EventArgs());
}
} }
/// <summary> /// <summary>
@@ -83,14 +44,4 @@ namespace PepperDash.Essentials.Core
Room.Shutdown(); 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

@@ -59,102 +59,28 @@ namespace PepperDash.Essentials.Core
/// </summary> /// </summary>
public IMobileControlRoomMessenger MobileControlRoomBridge { get; private set; } public IMobileControlRoomMessenger MobileControlRoomBridge { get; private set; }
protected const string _defaultListKey = "default";
/// <summary> /// <summary>
/// The config name of the source list /// The config name of the source list
/// </summary> /// </summary>
/// ///
private string _sourceListKey; protected string _SourceListKey;
public string SourceListKey { public string SourceListKey {
get get
{ {
if(string.IsNullOrEmpty(_sourceListKey)) return _SourceListKey;
{
return _defaultListKey;
}
else
{
return _sourceListKey;
}
} }
protected set private set
{ {
if (value != _sourceListKey) if (value != _SourceListKey)
{ {
_sourceListKey = value; _SourceListKey = value;
} }
} }
} }
private string _destinationListKey; public string DestinationListKey { get; private set; }
public string DestinationListKey
{
get
{
if (string.IsNullOrEmpty(_destinationListKey))
{
return _defaultListKey;
}
else
{
return _destinationListKey;
}
}
protected set
{
if (value != _destinationListKey)
{
_destinationListKey = value;
}
}
}
private string _audioControlPointListKey; protected const string _defaultSourceListKey = "default";
public string AudioControlPointListKey
{
get
{
if (string.IsNullOrEmpty(_audioControlPointListKey))
{
return _defaultListKey;
}
else
{
return _destinationListKey;
}
}
protected set
{
if (value != _audioControlPointListKey)
{
_audioControlPointListKey = value;
}
}
}
private string _cameraListKey;
public string CameraListKey
{
get
{
if (string.IsNullOrEmpty(_cameraListKey))
{
return _defaultListKey;
}
else
{
return _cameraListKey;
}
}
protected set
{
if (value != _cameraListKey)
{
_cameraListKey = value;
}
}
}
/// <summary> /// <summary>
/// Timer used for informing the UIs of a shutdown /// Timer used for informing the UIs of a shutdown
@@ -215,7 +141,6 @@ namespace PepperDash.Essentials.Core
if (!ShutdownPromptTimer.IsRunningFeedback.BoolValue) if (!ShutdownPromptTimer.IsRunningFeedback.BoolValue)
ShutdownType = eShutdownType.None; ShutdownType = eShutdownType.None;
}; };
ShutdownPromptTimer.HasFinished += (o, a) => Shutdown(); // Shutdown is triggered ShutdownPromptTimer.HasFinished += (o, a) => Shutdown(); // Shutdown is triggered
ShutdownPromptSeconds = 60; ShutdownPromptSeconds = 60;
@@ -266,7 +191,7 @@ namespace PepperDash.Essentials.Core
} }
else else
{ {
sourceListKey = _defaultListKey; sourceListKey = _defaultSourceListKey;
} }
} }

View File

@@ -29,10 +29,6 @@ namespace PepperDash.Essentials.Core
string DestinationListKey { get; } string DestinationListKey { get; }
string AudioControlPointListKey { get; }
string CameraListKey { get; }
SecondsCountdownTimer ShutdownPromptTimer { get; } SecondsCountdownTimer ShutdownPromptTimer { get; }
int ShutdownPromptSeconds { get; } int ShutdownPromptSeconds { get; }
int ShutdownVacancySeconds { get; } int ShutdownVacancySeconds { get; }

View File

@@ -40,7 +40,7 @@ namespace PepperDash.Essentials.Core
{ {
void RunRouteAction(string routeKey, string sourceListKey); void RunRouteAction(string routeKey, string sourceListKey);
void RunRouteAction(string routeKey, string sourceListKey, Action successCallback); void RunRouteAction(string routeKey, string sourceListKey, Action successCallback);
} }
/// <summary> /// <summary>
@@ -77,34 +77,6 @@ namespace PepperDash.Essentials.Core
SecondsCountdownTimer ShutdownPromptTimer { get; } SecondsCountdownTimer ShutdownPromptTimer { get; }
void SetShutdownPromptSeconds(int seconds); void SetShutdownPromptSeconds(int seconds);
void StartShutdown(eShutdownType type);
}
/// <summary""'""">
/// Describes a room with a tech password
/// </summary>
public interface ITechPassword
{
event EventHandler<TechPasswordEventArgs> TechPasswordValidateResult;
event EventHandler<EventArgs> TechPasswordChanged;
int TechPasswordLength { get; }
void ValidateTechPassword(string password);
void SetTechPassword(string oldPassword, string newPassword);
}
public class TechPasswordEventArgs : EventArgs
{
public bool IsValid { get; private set; }
public TechPasswordEventArgs(bool isValid)
{
IsValid = isValid;
}
} }
/// <summary> /// <summary>
@@ -159,13 +131,4 @@ namespace PepperDash.Essentials.Core
Core.Privacy.MicrophonePrivacyController MicrophonePrivacy { get; } Core.Privacy.MicrophonePrivacyController MicrophonePrivacy { get; }
} }
public interface IHasAccessoryDevices : IKeyName
{
List<string> AccessoryDeviceKeys { get; }
}
public interface IHasCiscoNavigatorTouchpanel
{
string CiscoNavigatorTouchpanelKey { get; }
}
} }

View File

@@ -8,7 +8,7 @@ using PepperDash.Core;
namespace PepperDash.Essentials.Core.Routing namespace PepperDash.Essentials.Core.Routing
{ {
public class DummyRoutingInputsDevice : Device, IRoutingSource, IRoutingOutputs public class DummyRoutingInputsDevice : Device, IRoutingSource
{ {
/// <summary> /// <summary>
/// A single output port, backplane, audioVideo /// A single output port, backplane, audioVideo

View File

@@ -1,373 +0,0 @@
using PepperDash.Essentials.Core.Queues;
using PepperDash.Essentials.Core.Routing;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Debug = PepperDash.Core.Debug;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Extensions added to any IRoutingInputs classes to provide discovery-based routing
/// on those destinations.
/// </summary>
public static class Extensions
{
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
/// in RouteDescriptorCollection.DefaultCollection
/// </summary>
public static void ReleaseAndMakeRoute(this IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, string destinationPortKey = "", string sourcePortKey = "")
{
// Remove this line before committing!!!!!
var frame = new StackFrame(1, true);
Debug.LogMessage(LogEventLevel.Information, "ReleaseAndMakeRoute Called from {method} with params {destinationKey}:{sourceKey}:{signalType}:{destinationPortKey}:{sourcePortKey}", frame.GetMethod().Name, destination.Key, source.Key, signalType.ToString(), destinationPortKey, sourcePortKey);
var inputPort = string.IsNullOrEmpty(destinationPortKey) ? null : destination.InputPorts.FirstOrDefault(p => p.Key == destinationPortKey);
var outputPort = string.IsNullOrEmpty(sourcePortKey) ? null : source.OutputPorts.FirstOrDefault(p => p.Key == sourcePortKey);
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)
{
Debug.LogMessage(LogEventLevel.Information, "Removing route request for {destination}", null, destinationKey);
var result = RouteRequests.Remove(destinationKey);
var messageTemplate = result ? "Route Request for {destination} removed" : "Route Request for {destination} not found";
Debug.LogMessage(LogEventLevel.Information, messageTemplate, null, destinationKey);
}
/// <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
/// a route is discovered, a new RouteDescriptor is returned. If one or both parts
/// of an audio/video route are discovered a route descriptor is returned. If no route is
/// discovered, then null is returned
/// </summary>
public static (RouteDescriptor, RouteDescriptor) GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort, RoutingOutputPort sourcePort)
{
// if it's a single signal type, find the route
if (!signalType.HasFlag(eRoutingSignalType.AudioVideo))
{
var singleTypeRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, signalType);
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {sourceKey} of type {type}", destination, source.Key, signalType);
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)
{
Debug.LogMessage(LogEventLevel.Verbose, "Route for device: {route}", destination, route.ToString());
}
return (singleTypeRouteDescriptor, null);
}
// otherwise, audioVideo needs to be handled as two steps.
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {sourceKey} of type {type}", destination, source.Key);
var audioRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, eRoutingSignalType.Audio);
var audioSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Audio, 0, audioRouteDescriptor, destinationPort, sourcePort);
if (!audioSuccess)
Debug.LogMessage(LogEventLevel.Debug, "Cannot find audio route to {0}", destination, source.Key);
var videoRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, eRoutingSignalType.Video);
var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, videoRouteDescriptor, destinationPort, sourcePort);
if (!videoSuccess)
Debug.LogMessage(LogEventLevel.Debug, "Cannot find video route to {0}", destination, source.Key);
foreach (var route in audioRouteDescriptor.Routes)
{
Debug.LogMessage(LogEventLevel.Verbose, "Audio route for device: {route}", destination, route.ToString());
}
foreach (var route in videoRouteDescriptor.Routes)
{
Debug.LogMessage(LogEventLevel.Verbose, "Video route for device: {route}", destination, route.ToString());
}
if (!audioSuccess && !videoSuccess)
return (null, null);
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
/// hoping to find the source.
/// </summary>
/// <param name="destination"></param>
/// <param name="source"></param>
/// <param name="destinationPort">The RoutingOutputPort whose link is being checked for a route</param>
/// <param name="alreadyCheckedDevices">Prevents Devices from being twice-checked</param>
/// <param name="signalType">This recursive function should not be called with AudioVideo</param>
/// <param name="cycle">Just an informational counter</param>
/// <param name="routeTable">The RouteDescriptor being populated as the route is discovered</param>
/// <returns>true if source is hit</returns>
private static bool GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source,
RoutingOutputPort outputPortToUse, List<IRoutingInputsOutputs> alreadyCheckedDevices,
eRoutingSignalType signalType, int cycle, RouteDescriptor routeTable, RoutingInputPort destinationPort, RoutingOutputPort sourcePort)
{
cycle++;
Debug.LogMessage(LogEventLevel.Verbose, "GetRouteToSource: {cycle} {sourceKey}:{sourcePortKey}--> {destinationKey}:{destinationPortKey} {type}", null, cycle, source.Key, sourcePort?.Key ?? "auto", destination.Key, destinationPort?.Key ?? "auto", signalType.ToString());
RoutingInputPort goodInputPort = null;
IEnumerable<TieLine> destinationTieLines;
TieLine directTie = null;
if (destinationPort == null)
{
destinationTieLines = TieLineCollection.Default.Where(t =>
t.DestinationPort.ParentDevice.Key == destination.Key && (t.Type == signalType || t.Type.HasFlag(eRoutingSignalType.AudioVideo)));
}
else
{
destinationTieLines = TieLineCollection.Default.Where(t => t.DestinationPort.ParentDevice.Key == destination.Key && t.DestinationPort.Key == destinationPort.Key && (t.Type == signalType || t.Type.HasFlag(eRoutingSignalType.AudioVideo)));
}
// find the TieLine without a port
if (destinationPort == null && sourcePort == null)
{
directTie = destinationTieLines.FirstOrDefault(t => t.DestinationPort.ParentDevice.Key == destination.Key && t.SourcePort.ParentDevice.Key == source.Key);
}
// find a tieLine to a specific destination port without a specific source port
else if (destinationPort != null && sourcePort == null)
{
directTie = destinationTieLines.FirstOrDefault(t => t.DestinationPort.ParentDevice.Key == destination.Key && t.DestinationPort.Key == destinationPort.Key && t.SourcePort.ParentDevice.Key == source.Key);
}
// find a tieline to a specific source port without a specific destination port
else if (destinationPort == null & sourcePort != null)
{
directTie = destinationTieLines.FirstOrDefault(t => t.DestinationPort.ParentDevice.Key == destination.Key && t.SourcePort.ParentDevice.Key == source.Key && t.SourcePort.Key == sourcePort.Key);
}
// find a tieline to a specific source port and destination port
else if (destinationPort != null && sourcePort != null)
{
directTie = destinationTieLines.FirstOrDefault(t => t.DestinationPort.ParentDevice.Key == destination.Key && t.DestinationPort.Key == destinationPort.Key && t.SourcePort.ParentDevice.Key == source.Key && t.SourcePort.Key == sourcePort.Key);
}
if (directTie != null) // Found a tie directly to the source
{
goodInputPort = directTie.DestinationPort;
}
else // no direct-connect. Walk back devices.
{
Debug.LogMessage(LogEventLevel.Verbose, "is not directly connected to {sourceKey}. Walking down tie lines", destination, source.Key);
// No direct tie? Run back out on the inputs' attached devices...
// Only the ones that are routing devices
var midpointTieLines = destinationTieLines.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);
//Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration
if (alreadyCheckedDevices == null)
alreadyCheckedDevices = new List<IRoutingInputsOutputs>();
alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs);
foreach (var tieLine in midpointTieLines)
{
var midpointDevice = tieLine.SourcePort.ParentDevice as IRoutingInputsOutputs;
// Check if this previous device has already been walked
if (alreadyCheckedDevices.Contains(midpointDevice))
{
Debug.LogMessage(LogEventLevel.Verbose, "Skipping input {midpointDeviceKey} on {destinationKey}, this was already checked", destination, midpointDevice.Key, destination.Key);
continue;
}
var midpointOutputPort = tieLine.SourcePort;
Debug.LogMessage(LogEventLevel.Verbose, "Trying to find route on {midpointDeviceKey}", destination, midpointDevice.Key);
// haven't seen this device yet. Do it. Pass the output port to the next
// level to enable switching on success
var upstreamRoutingSuccess = midpointDevice.GetRouteToSource(source, midpointOutputPort,
alreadyCheckedDevices, signalType, cycle, routeTable, null, sourcePort);
if (upstreamRoutingSuccess)
{
Debug.LogMessage(LogEventLevel.Verbose, "Upstream device route found", destination);
Debug.LogMessage(LogEventLevel.Verbose, "Route found on {midpointDeviceKey}", destination, midpointDevice.Key);
Debug.LogMessage(LogEventLevel.Verbose, "TieLine: SourcePort: {SourcePort} DestinationPort: {DestinationPort}", destination, tieLine.SourcePort, tieLine.DestinationPort);
goodInputPort = tieLine.DestinationPort;
break; // Stop looping the inputs in this cycle
}
}
}
if (goodInputPort == null)
{
Debug.LogMessage(LogEventLevel.Verbose, "No route found to {0}", destination, source.Key);
return false;
}
// we have a route on corresponding inputPort. *** Do the route ***
if (destination is IRoutingSink)
{
// it's a sink device
routeTable.Routes.Add(new RouteSwitchDescriptor(goodInputPort));
}
else if (destination is IRouting)
{
routeTable.Routes.Add(new RouteSwitchDescriptor(outputPortToUse, goodInputPort));
}
else // device is merely IRoutingInputOutputs
Debug.LogMessage(LogEventLevel.Verbose, "No routing. Passthrough device", destination);
return true;
}
}
}

View File

@@ -1,30 +0,0 @@
/* Unmerged change from project 'PepperDash.Essentials.Core (net6)'
Before:
namespace PepperDash.Essentials.Core.Routing.Interfaces
After:
using PepperDash;
using PepperDash.Essentials;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Routing;
using PepperDash.Essentials.Core.Routing;
using PepperDash.Essentials.Core.Routing.Interfaces
*/
namespace PepperDash.Essentials.Core
{
/// <summary>
/// The handler type for a Room's SourceInfoChange
/// </summary>
public delegate void SourceInfoChangeHandler(SourceListItem info, ChangeType type);
//*******************************************************************************************
// Interfaces
/// <summary>
/// For rooms with a single presentation source, change event
/// </summary>
public interface IHasCurrentSourceInfoChange
{
string CurrentSourceInfoKey { get; set; }
SourceListItem CurrentSourceInfo { get; set; }
event SourceInfoChangeHandler CurrentSourceChange;
}
}

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace PepperDash.Essentials.Core.Routing namespace PepperDash.Essentials.Core.Routing
{ {
public interface IVideoSync : IKeyed public interface IVideoSync: IKeyed
{ {
bool VideoSyncDetected { get; } bool VideoSyncDetected { get; }

View File

@@ -1,10 +0,0 @@
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines a receiver that has internal routing (DM-RMC-4K-Z-SCALER-C)
/// </summary>
public interface IRmcRouting : IRoutingNumeric
{
IntFeedback AudioVideoSourceNumericFeedback { get; }
}
}

View File

@@ -1,9 +0,0 @@
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines an IRmcRouting with a feedback event
/// </summary>
public interface IRmcRoutingWithFeedback : IRmcRouting
{
}
}

View File

@@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines a midpoint device as have internal routing. Any devices in the middle of the
/// signal chain, that do switching, must implement this for routing to work otherwise
/// the routing algorithm will treat the IRoutingInputsOutputs device as a passthrough
/// device.
/// </summary>
public interface IRouting : IRoutingInputsOutputs
{
void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType);
}
/*public interface IRouting<TInputSelector,TOutputSelector> : IRoutingInputsOutputs
{
void ExecuteSwitch(TInputSelector inputSelector, TOutputSelector outputSelector, eRoutingSignalType signalType);
}*/
}

View File

@@ -1,16 +0,0 @@
using System;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines an event structure for reporting output route data
/// </summary>
public interface IRoutingFeedback : IKeyName
{
event EventHandler<RoutingNumericEventArgs> NumericSwitchChange;
//void OnSwitchChange(RoutingNumericEventArgs e);
}
}

View File

@@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DM;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
public interface IRoutingHasVideoInputSyncFeedbacks
{
FeedbackCollection<BoolFeedback> VideoInputSyncFeedbacks { get; }
}
}

View File

@@ -1,18 +0,0 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines a class that has a collection of RoutingInputPorts
/// </summary>
public interface IRoutingInputs : IKeyed
{
RoutingPortCollection<RoutingInputPort> InputPorts { get; }
}
/* public interface IRoutingInputs<TSelector> : IKeyed
{
RoutingPortCollection<RoutingInputPort<TSelector>, TSelector> InputPorts { get; }
}*/
}

View File

@@ -0,0 +1,426 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DM;
using PepperDash.Core;
using Serilog.Events;
namespace PepperDash.Essentials.Core
{
public class RouteRequest
{
public IRoutingSink Destination {get; set;}
public IRoutingOutputs Source {get; set;}
public eRoutingSignalType SignalType {get; set;}
public void HandleCooldown(object sender, FeedbackEventArgs args)
{
var coolingDevice = sender as IWarmingCooling;
if(args.BoolValue == false)
{
Destination.ReleaseAndMakeRoute(Source, SignalType);
if(sender == null) return;
coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown;
}
}
}
/// <summary>
/// Extensions added to any IRoutingInputs classes to provide discovery-based routing
/// on those destinations.
/// </summary>
public static class IRoutingInputsExtensions
{
private static Dictionary<string, RouteRequest> RouteRequests = new Dictionary<string, RouteRequest>();
/// <summary>
/// Gets any existing RouteDescriptor for a destination, clears it using ReleaseRoute
/// and then attempts a new Route and if sucessful, stores that RouteDescriptor
/// in RouteDescriptorCollection.DefaultCollection
/// </summary>
public static void ReleaseAndMakeRoute(this IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType)
{
var routeRequest = new RouteRequest {
Destination = destination,
Source = source,
SignalType = signalType
};
var coolingDevice = destination as IWarmingCooling;
RouteRequest existingRouteRequest;
//We already have a route request for this device, and it's a cooling device and is cooling
if (RouteRequests.TryGetValue(destination.Key, out existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
{
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
RouteRequests[destination.Key] = routeRequest;
Debug.LogMessage(LogEventLevel.Verbose, "******************************************************** Device: {0} is cooling down and already has a routing request stored. Storing new route request to route to source key: {1}", destination.Key, routeRequest.Source.Key);
return;
}
//New Request
if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
{
coolingDevice.IsCoolingDownFeedback.OutputChange -= routeRequest.HandleCooldown;
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
RouteRequests.Add(destination.Key, routeRequest);
Debug.LogMessage(LogEventLevel.Verbose, "******************************************************** Device: {0} is cooling down. Storing route request to route to source key: {1}", destination.Key, routeRequest.Source.Key);
return;
}
if (RouteRequests.ContainsKey(destination.Key) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == false)
{
RouteRequests.Remove(destination.Key);
Debug.LogMessage(LogEventLevel.Verbose, "******************************************************** Device: {0} is NOT cooling down. Removing stored route request and routing to source key: {1}", destination.Key, routeRequest.Source.Key);
}
destination.ReleaseRoute();
RunRouteRequest(routeRequest);
}
public static void RunRouteRequest(RouteRequest request)
{
if (request.Source == null) return;
var newRoute = request.Destination.GetRouteToSource(request.Source, request.SignalType);
if (newRoute == null) return;
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(newRoute);
Debug.LogMessage(LogEventLevel.Verbose, request.Destination, "Executing full route");
newRoute.ExecuteRoutes();
}
/// <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 IRoutingSink destination)
{
RouteRequest existingRequest;
if (RouteRequests.TryGetValue(destination.Key, out existingRequest) && destination is IWarmingCooling)
{
var coolingDevice = destination as IWarmingCooling;
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRequest.HandleCooldown;
}
RouteRequests.Remove(destination.Key);
var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination);
if (current != null)
{
Debug.LogMessage(LogEventLevel.Debug, destination, "Releasing current route: {0}", 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
/// a route is discovered, a new RouteDescriptor is returned. If one or both parts
/// of an audio/video route are discovered a route descriptor is returned. If no route is
/// discovered, then null is returned
/// </summary>
public static RouteDescriptor GetRouteToSource(this IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType)
{
var routeDescr = new RouteDescriptor(source, destination, signalType);
// if it's a single signal type, find the route
if ((signalType & (eRoutingSignalType.Audio & eRoutingSignalType.Video)) == (eRoutingSignalType.Audio & eRoutingSignalType.Video))
{
Debug.LogMessage(LogEventLevel.Debug, destination, "Attempting to build source route from {0}", source.Key);
if (!destination.GetRouteToSource(source, null, null, signalType, 0, routeDescr))
routeDescr = null;
}
// otherwise, audioVideo needs to be handled as two steps.
else
{
Debug.LogMessage(LogEventLevel.Debug, destination, "Attempting to build audio and video routes from {0}", source.Key);
var audioSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Audio, 0, routeDescr);
if (!audioSuccess)
Debug.LogMessage(LogEventLevel.Debug, destination, "Cannot find audio route to {0}", source.Key);
var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, routeDescr);
if (!videoSuccess)
Debug.LogMessage(LogEventLevel.Debug, destination, "Cannot find video route to {0}", source.Key);
if (!audioSuccess && !videoSuccess)
routeDescr = null;
}
//Debug.LogMessage(LogEventLevel.Debug, destination, "Route{0} discovered", routeDescr == null ? " NOT" : "");
return routeDescr;
}
/// <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
/// hoping to find the source.
/// </summary>
/// <param name="destination"></param>
/// <param name="source"></param>
/// <param name="outputPortToUse">The RoutingOutputPort whose link is being checked for a route</param>
/// <param name="alreadyCheckedDevices">Prevents Devices from being twice-checked</param>
/// <param name="signalType">This recursive function should not be called with AudioVideo</param>
/// <param name="cycle">Just an informational counter</param>
/// <param name="routeTable">The RouteDescriptor being populated as the route is discovered</param>
/// <returns>true if source is hit</returns>
static bool GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source,
RoutingOutputPort outputPortToUse, List<IRoutingInputsOutputs> alreadyCheckedDevices,
eRoutingSignalType signalType, int cycle, RouteDescriptor routeTable)
{
cycle++;
Debug.LogMessage(LogEventLevel.Verbose, "GetRouteToSource: {0} {1}--> {2}", cycle, source.Key, destination.Key);
RoutingInputPort goodInputPort = null;
var destDevInputTies = TieLineCollection.Default.Where(t =>
t.DestinationPort.ParentDevice == destination && (t.Type == signalType || (t.Type & (eRoutingSignalType.Audio | eRoutingSignalType.Video)) == (eRoutingSignalType.Audio | eRoutingSignalType.Video)));
// find a direct tie
var directTie = destDevInputTies.FirstOrDefault(
t => t.DestinationPort.ParentDevice == destination
&& t.SourcePort.ParentDevice == source);
if (directTie != null) // Found a tie directly to the source
{
goodInputPort = directTie.DestinationPort;
}
else // no direct-connect. Walk back devices.
{
Debug.LogMessage(LogEventLevel.Verbose, destination, "is not directly connected to {0}. Walking down tie lines", source.Key);
// No direct tie? Run back out on the inputs' attached devices...
// Only the ones that are routing devices
var attachedMidpoints = destDevInputTies.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);
//Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration
if (alreadyCheckedDevices == null)
alreadyCheckedDevices = new List<IRoutingInputsOutputs>();
alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs);
foreach (var inputTieToTry in attachedMidpoints)
{
var upstreamDeviceOutputPort = inputTieToTry.SourcePort;
var upstreamRoutingDevice = upstreamDeviceOutputPort.ParentDevice as IRoutingInputsOutputs;
Debug.LogMessage(LogEventLevel.Verbose, destination, "Trying to find route on {0}", upstreamRoutingDevice.Key);
// Check if this previous device has already been walked
if (alreadyCheckedDevices.Contains(upstreamRoutingDevice))
{
Debug.LogMessage(LogEventLevel.Verbose, destination, "Skipping input {0} on {1}, this was already checked", upstreamRoutingDevice.Key, destination.Key);
continue;
}
// haven't seen this device yet. Do it. Pass the output port to the next
// level to enable switching on success
var upstreamRoutingSuccess = upstreamRoutingDevice.GetRouteToSource(source, upstreamDeviceOutputPort,
alreadyCheckedDevices, signalType, cycle, routeTable);
if (upstreamRoutingSuccess)
{
Debug.LogMessage(LogEventLevel.Verbose, destination, "Upstream device route found");
goodInputPort = inputTieToTry.DestinationPort;
break; // Stop looping the inputs in this cycle
}
}
}
// we have a route on corresponding inputPort. *** Do the route ***
if (goodInputPort != null)
{
//Debug.LogMessage(LogEventLevel.Verbose, destination, "adding RouteDescriptor");
if (outputPortToUse == null)
{
// it's a sink device
routeTable.Routes.Add(new RouteSwitchDescriptor(goodInputPort));
}
else if (destination is IRouting)
{
routeTable.Routes.Add(new RouteSwitchDescriptor (outputPortToUse, goodInputPort));
}
else // device is merely IRoutingInputOutputs
Debug.LogMessage(LogEventLevel.Verbose, destination, " No routing. Passthrough device");
//Debug.LogMessage(LogEventLevel.Verbose, destination, "Exiting cycle {0}", cycle);
return true;
}
Debug.LogMessage(LogEventLevel.Verbose, destination, "No route found to {0}", source.Key);
return false;
}
}
// MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE
/// <summary>
/// A collection of RouteDescriptors - typically the static DefaultCollection is used
/// </summary>
public class RouteDescriptorCollection
{
public static RouteDescriptorCollection DefaultCollection
{
get
{
if (_DefaultCollection == null)
_DefaultCollection = new RouteDescriptorCollection();
return _DefaultCollection;
}
}
static RouteDescriptorCollection _DefaultCollection;
List<RouteDescriptor> RouteDescriptors = new List<RouteDescriptor>();
/// <summary>
/// Adds a RouteDescriptor to the list. If an existing RouteDescriptor for the
/// destination exists already, it will not be added - in order to preserve
/// proper route releasing.
/// </summary>
/// <param name="descriptor"></param>
public void AddRouteDescriptor(RouteDescriptor descriptor)
{
if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination))
{
Debug.LogMessage(LogEventLevel.Debug, descriptor.Destination,
"Route to [{0}] already exists in global routes table", descriptor.Source.Key);
return;
}
RouteDescriptors.Add(descriptor);
}
/// <summary>
/// Gets the RouteDescriptor for a destination
/// </summary>
/// <returns>null if no RouteDescriptor for a destination exists</returns>
public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs destination)
{
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
}
/// <summary>
/// Returns the RouteDescriptor for a given destination AND removes it from collection.
/// Returns null if no route with the provided destination exists.
/// </summary>
public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination)
{
var descr = GetRouteDescriptorForDestination(destination);
if (descr != null)
RouteDescriptors.Remove(descr);
return descr;
}
}
/// <summary>
/// Represents an collection of individual route steps between Source and Destination
/// </summary>
public class RouteDescriptor
{
public IRoutingInputs Destination { 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)
{
Destination = destination;
Source = source;
SignalType = signalType;
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}", route.ToString());
if (route.SwitchingDevice is IRoutingSink)
{
var device = route.SwitchingDevice as IRoutingSinkWithSwitching;
if (device == null)
continue;
device.ExecuteSwitch(route.InputPort.Selector);
}
else if (route.SwitchingDevice is IRouting)
{
(route.SwitchingDevice as IRouting).ExecuteSwitch(route.InputPort.Selector, route.OutputPort.Selector, SignalType);
route.OutputPort.InUseTracker.AddUser(Destination, "destination-" + SignalType);
Debug.LogMessage(LogEventLevel.Verbose, "Output port {0} routing. Count={1}", 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()
{
foreach (var route in Routes)
{
if (route.SwitchingDevice is IRouting)
{
// Pull the route from the port. Whatever is watching the output's in use tracker is
// responsible for responding appropriately.
route.OutputPort.InUseTracker.RemoveUser(Destination, "destination-" + SignalType);
Debug.LogMessage(LogEventLevel.Verbose, "Port {0} releasing. Count={1}", route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
}
}
}
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 individual link for a route
/// </summary>
public class RouteSwitchDescriptor
{
public IRoutingInputs SwitchingDevice { get { return InputPort.ParentDevice; } }
public RoutingOutputPort OutputPort { get; set; }
public RoutingInputPort InputPort { get; set; }
public RouteSwitchDescriptor(RoutingInputPort inputPort)
{
InputPort = inputPort;
}
public RouteSwitchDescriptor(RoutingOutputPort outputPort, RoutingInputPort inputPort)
{
InputPort = inputPort;
OutputPort = outputPort;
}
public override string ToString()
{
if(SwitchingDevice is IRouting)
return string.Format("{0} switches output '{1}' to input '{2}'", SwitchingDevice.Key, OutputPort.Selector, InputPort.Selector);
else
return string.Format("{0} switches to input '{1}'", SwitchingDevice.Key, InputPort.Selector);
}
}
}

View File

@@ -1,16 +0,0 @@
namespace PepperDash.Essentials.Core
{
/// <summary>
/// For devices like RMCs, baluns, other devices with no switching.
/// </summary>
public interface IRoutingInputsOutputs : IRoutingInputs, IRoutingOutputs
{
}
/* /// <summary>
/// For devices like RMCs, baluns, other devices with no switching.
/// </summary>
public interface IRoutingInputsOutputs<TInputSelector, TOutputSelector> : IRoutingInputs<TInputSelector>, IRoutingOutputs<TOutputSelector>
{
}*/
}

View File

@@ -1,7 +0,0 @@
namespace PepperDash.Essentials.Core
{
public interface IRoutingNumeric : IRouting
{
void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type);
}
}

View File

@@ -1,9 +0,0 @@
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines an IRoutingNumeric with a feedback event
/// </summary>
public interface IRoutingNumericWithFeedback : IRoutingNumeric, IRoutingFeedback
{
}
}

View File

@@ -1,19 +0,0 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines a class that has a collection of RoutingOutputPorts
/// </summary>
public interface IRoutingOutputs : IKeyed
{
RoutingPortCollection<RoutingOutputPort> OutputPorts { get; }
}
/* public interface IRoutingOutputs<TSelector> : IKeyed
{
RoutingPortCollection<RoutingOutputPort<TSelector>, TSelector> OutputPorts { get; }
}*/
}

View File

@@ -1,23 +0,0 @@
namespace PepperDash.Essentials.Core
{
/// <summary>
/// For fixed-source endpoint devices
/// </summary>
public interface IRoutingSink : IRoutingInputs, IHasCurrentSourceInfoChange
{
}
public interface IRoutingSinkWithInputPort :IRoutingSink
{
RoutingInputPort CurrentInputPort { get; }
}
/*/// <summary>
/// For fixed-source endpoint devices
/// </summary>
public interface IRoutingSink<TSelector> : IRoutingInputs<TSelector>, IHasCurrentSourceInfoChange
{
void UpdateRouteRequest<TOutputSelector>(RouteRequest<TSelector, TOutputSelector> request);
RouteRequest<TSelector, TOutputSelector> GetRouteRequest<TOutputSelector>();
}*/
}

View File

@@ -1,25 +0,0 @@
using PepperDash.Essentials.Core.Routing;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// For fixed-source endpoint devices
/// </summary>
public interface IRoutingSinkWithFeedback : IRoutingSinkWithSwitching
{
}
/* /// <summary>
/// For fixed-source endpoint devices
/// </summary>
public interface IRoutingSinkWithFeedback<TSelector> : IRoutingSinkWithSwitching<TSelector>
{
RouteSwitchDescriptor CurrentRoute { get; }
event EventHandler InputChanged;
}*/
}

View File

@@ -1,27 +0,0 @@
using System;
namespace PepperDash.Essentials.Core
{
public delegate void InputChangedEventHandler(IRoutingSinkWithSwitching destination, RoutingInputPort currentPort);
/// <summary>
/// Endpoint device like a display, that selects inputs
/// </summary>
public interface IRoutingSinkWithSwitching : IRoutingSink
{
void ExecuteSwitch(object inputSelector);
}
public interface IRoutingSinkWithSwitchingWithInputPort:IRoutingSinkWithSwitching, IRoutingSinkWithInputPort
{
event InputChangedEventHandler InputChanged;
}
/* /// <summary>
/// Endpoint device like a display, that selects inputs
/// </summary>
public interface IRoutingSinkWithSwitching<TSelector> : IRoutingSink<TSelector>
{
void ExecuteSwitch(TSelector inputSelector);
}*/
}

View File

@@ -1,9 +0,0 @@
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines an IRoutingOutputs devices as being a source - the start of the chain
/// </summary>
public interface IRoutingSource : IRoutingOutputs
{
}
}

View File

@@ -1,12 +0,0 @@
namespace PepperDash.Essentials.Core
{
public interface IRoutingWithClear : IRouting
{
/// <summary>
/// Clears a route to an output, however a device needs to do that
/// </summary>
/// <param name="outputSelector">Output to clear</param>
/// <param name="signalType">signal type to clear</param>
void ClearRoute(object outputSelector, eRoutingSignalType signalType);
}
}

View File

@@ -1,16 +0,0 @@
using System.Collections.Generic;
using System;
namespace PepperDash.Essentials.Core
{
public delegate void RouteChangedEventHandler(IRoutingWithFeedback midpoint, RouteSwitchDescriptor newRoute);
/// <summary>
/// Defines an IRouting with a feedback event
/// </summary>
public interface IRoutingWithFeedback : IRouting
{
List<RouteSwitchDescriptor> CurrentRoutes { get; }
event RouteChangedEventHandler RouteChanged;
}
}

View File

@@ -1,8 +0,0 @@
namespace PepperDash.Essentials.Core
{
public interface ITxRouting : IRoutingNumeric
{
IntFeedback VideoSourceNumericFeedback { get; }
IntFeedback AudioSourceNumericFeedback { get; }
}
}

View File

@@ -1,9 +0,0 @@
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines an IRmcRouting with a feedback event
/// </summary>
public interface ITxRoutingWithFeedback : ITxRouting
{
}
}

View File

@@ -1,170 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Crestron.SimplSharpPro;
using PepperDash.Core;
using Serilog.Events;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Represents an collection of individual route steps between Source and Destination
/// </summary>
public class RouteDescriptor
{
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 RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType) : this(source, destination, null, signalType)
{
}
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, RoutingInputPort inputPort, eRoutingSignalType signalType)
{
Destination = destination;
Source = source;
SignalType = signalType;
InputPort = inputPort;
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());
if (route.SwitchingDevice is IRoutingSinkWithSwitching sink)
{
sink.ExecuteSwitch(route.InputPort.Selector);
continue;
}
if (route.SwitchingDevice is IRouting switchingDevice)
{
switchingDevice.ExecuteSwitch(route.InputPort.Selector, route.OutputPort.Selector, SignalType);
route.OutputPort.InUseTracker.AddUser(Destination, "destination-" + SignalType);
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()
{
foreach (var route in Routes.Where(r => r.SwitchingDevice is IRouting))
{
if (route.SwitchingDevice is IRouting switchingDevice)
{
if (route.OutputPort == null)
{
continue;
}
if (route.OutputPort.InUseTracker != null)
{
route.OutputPort.InUseTracker.RemoveUser(Destination, "destination-" + SignalType);
Debug.LogMessage(LogEventLevel.Verbose, "Port {0} releasing. Count={1}", null, route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
}
else
{
Debug.LogMessage(LogEventLevel.Error, "InUseTracker is null for OutputPort {0}", null, route.OutputPort.Key);
}
}
}
}
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
/// </summary>
public class RouteDescriptor<TInputSelector, TOutputSelector>
{
public IRoutingInputs<TInputSelector> Destination { get; private set; }
public IRoutingOutputs<TOutputSelector> Source { get; private set; }
public eRoutingSignalType SignalType { get; private set; }
public List<RouteSwitchDescriptor<TInputSelector, TOutputSelector>> Routes { get; private set; }
public RouteDescriptor(IRoutingOutputs<TOutputSelector> source, IRoutingInputs<TInputSelector> destination, eRoutingSignalType signalType)
{
Destination = destination;
Source = source;
SignalType = signalType;
Routes = new List<RouteSwitchDescriptor<TInputSelector, TOutputSelector>>();
}
/// <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<TInputSelector> sink)
{
sink.ExecuteSwitch(route.InputPort.Selector);
continue;
}
if (route.SwitchingDevice is IRouting switchingDevice)
{
switchingDevice.ExecuteSwitch(route.InputPort.Selector, route.OutputPort.Selector, SignalType);
route.OutputPort.InUseTracker.AddUser(Destination, "destination-" + SignalType);
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()
{
foreach (var route in Routes)
{
if (route.SwitchingDevice is IRouting<TInputSelector, TOutputSelector>)
{
// Pull the route from the port. Whatever is watching the output's in use tracker is
// responsible for responding appropriately.
route.OutputPort.InUseTracker.RemoveUser(Destination, "destination-" + SignalType);
Debug.LogMessage(LogEventLevel.Verbose, "Port {0} releasing. Count={1}", null, route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
}
}
}
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));
}
}*/
}

View File

@@ -1,143 +0,0 @@
using PepperDash.Core;
using Serilog.Events;
using System.Collections.Generic;
using System.Linq;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// A collection of RouteDescriptors - typically the static DefaultCollection is used
/// </summary>
public class RouteDescriptorCollection
{
public static RouteDescriptorCollection DefaultCollection
{
get
{
if (_DefaultCollection == null)
_DefaultCollection = new RouteDescriptorCollection();
return _DefaultCollection;
}
}
private static RouteDescriptorCollection _DefaultCollection;
private readonly List<RouteDescriptor> RouteDescriptors = new List<RouteDescriptor>();
/// <summary>
/// Adds a RouteDescriptor to the list. If an existing RouteDescriptor for the
/// destination exists already, it will not be added - in order to preserve
/// proper route releasing.
/// </summary>
/// <param name="descriptor"></param>
public void AddRouteDescriptor(RouteDescriptor descriptor)
{
if (descriptor == null)
{
return;
}
if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination)
&& 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);
return;
}
RouteDescriptors.Add(descriptor);
}
/// <summary>
/// Gets the RouteDescriptor for a destination
/// </summary>
/// <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);
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);
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination && rd.InputPort != null && rd.InputPort.Key == inputPortKey);
}
/// <summary>
/// Returns the RouteDescriptor for a given destination AND removes it from collection.
/// Returns null if no route with the provided destination exists.
/// </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);
var descr = string.IsNullOrEmpty(inputPortKey)
? GetRouteDescriptorForDestination(destination)
: GetRouteDescriptorForDestinationAndInputPort(destination, inputPortKey);
if (descr != null)
RouteDescriptors.Remove(descr);
Debug.LogMessage(LogEventLevel.Information, "Found route descriptor {routeDescriptor}", destination, descr);
return descr;
}
}
/*/// <summary>
/// A collection of RouteDescriptors - typically the static DefaultCollection is used
/// </summary>
public class RouteDescriptorCollection<TInputSelector, TOutputSelector>
{
public static RouteDescriptorCollection<TInputSelector, TOutputSelector> DefaultCollection
{
get
{
if (_DefaultCollection == null)
_DefaultCollection = new RouteDescriptorCollection<TInputSelector, TOutputSelector>();
return _DefaultCollection;
}
}
private static RouteDescriptorCollection<TInputSelector, TOutputSelector> _DefaultCollection;
private readonly List<RouteDescriptor> RouteDescriptors = new List<RouteDescriptor>();
/// <summary>
/// Adds a RouteDescriptor to the list. If an existing RouteDescriptor for the
/// destination exists already, it will not be added - in order to preserve
/// proper route releasing.
/// </summary>
/// <param name="descriptor"></param>
public void AddRouteDescriptor(RouteDescriptor descriptor)
{
if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination))
{
Debug.LogMessage(LogEventLevel.Debug, descriptor.Destination,
"Route to [{0}] already exists in global routes table", descriptor.Source.Key);
return;
}
RouteDescriptors.Add(descriptor);
}
/// <summary>
/// Gets the RouteDescriptor for a destination
/// </summary>
/// <returns>null if no RouteDescriptor for a destination exists</returns>
public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs<TInputSelector> destination)
{
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
}
/// <summary>
/// Returns the RouteDescriptor for a given destination AND removes it from collection.
/// Returns null if no route with the provided destination exists.
/// </summary>
public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs<TInputSelector> destination)
{
var descr = GetRouteDescriptorForDestination(destination);
if (descr != null)
RouteDescriptors.Remove(descr);
return descr;
}
}*/
}

View File

@@ -1,47 +0,0 @@
using PepperDash.Core;
using Serilog.Events;
using System;
namespace PepperDash.Essentials.Core
{
public class RouteRequest
{
public RoutingInputPort DestinationPort { get; set; }
public RoutingOutputPort SourcePort { get; set; }
public IRoutingInputs Destination { get; set; }
public IRoutingOutputs Source { get; set; }
public eRoutingSignalType SignalType { get; set; }
public void HandleCooldown(object sender, FeedbackEventArgs args)
{
try
{
Debug.LogMessage(LogEventLevel.Information, "Handling cooldown route request: {destination}:{destinationPort} -> {source}:{sourcePort} {type}", null, Destination?.Key ?? "empty destination", DestinationPort?.Key ?? "no destination port", Source?.Key ?? "empty source", SourcePort?.Key ?? "empty source port", SignalType.ToString());
if (args.BoolValue == true)
{
return;
}
Debug.LogMessage(LogEventLevel.Information, "Cooldown complete. Making route from {destination} to {source}", Destination?.Key, Source?.Key);
Destination.ReleaseAndMakeRoute(Source, SignalType, DestinationPort?.Key ?? string.Empty, SourcePort?.Key ?? string.Empty);
if (sender is IWarmingCooling coolingDevice)
{
Debug.LogMessage(LogEventLevel.Debug, "Unsubscribing from cooling feedback for {destination}", null, Destination.Key);
coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown;
}
} catch(Exception ex)
{
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

@@ -1,60 +0,0 @@
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Represents an individual link for a route
/// </summary>
public class RouteSwitchDescriptor
{
public IRoutingInputs SwitchingDevice { get { return InputPort?.ParentDevice; } }
public RoutingOutputPort OutputPort { get; set; }
public RoutingInputPort InputPort { get; set; }
public RouteSwitchDescriptor(RoutingInputPort inputPort)
{
InputPort = inputPort;
}
public RouteSwitchDescriptor(RoutingOutputPort outputPort, RoutingInputPort inputPort)
{
InputPort = inputPort;
OutputPort = outputPort;
}
public override string ToString()
{
if (SwitchingDevice is IRouting)
return $"{(SwitchingDevice != null ? SwitchingDevice.Key : "No Device")} switches output {(OutputPort != null ? OutputPort.Key : "No output port")} to input {(InputPort != null ? InputPort.Key : "No input port")}";
else
return $"{(SwitchingDevice != null ? SwitchingDevice.Key : "No Device")} switches to input {(InputPort != null ? InputPort.Key : "No input port")}";
}
}
/*/// <summary>
/// Represents an individual link for a route
/// </summary>
public class RouteSwitchDescriptor<TInputSelector, TOutputSelector>
{
public IRoutingInputs<TInputSelector> SwitchingDevice { get { return InputPort.ParentDevice; } }
public RoutingOutputPort<TOutputSelector> OutputPort { get; set; }
public RoutingInputPort<TInputSelector> InputPort { get; set; }
public RouteSwitchDescriptor(RoutingInputPort<TInputSelector> inputPort)
{
InputPort = inputPort;
}
public RouteSwitchDescriptor(RoutingOutputPort<TOutputSelector> outputPort, RoutingInputPort<TInputSelector> inputPort)
{
InputPort = inputPort;
OutputPort = outputPort;
}
public override string ToString()
{
if (SwitchingDevice is IRouting)
return string.Format("{0} switches output '{1}' to input '{2}'", SwitchingDevice.Key, OutputPort.Selector, InputPort.Selector);
else
return string.Format("{0} switches to input '{1}'", SwitchingDevice.Key, InputPort.Selector);
}
}*/
}

View File

@@ -1,271 +0,0 @@
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
using System;
using System.Linq;
namespace PepperDash.Essentials.Core.Routing
{
public class RoutingFeedbackManager:EssentialsDevice
{
public RoutingFeedbackManager(string key, string name): base(key, name)
{
AddPreActivationAction(SubscribeForMidpointFeedback);
AddPreActivationAction(SubscribeForSinkFeedback);
}
private void SubscribeForMidpointFeedback()
{
var midpointDevices = DeviceManager.AllDevices.OfType<IRoutingWithFeedback>();
foreach (var device in midpointDevices)
{
device.RouteChanged += HandleMidpointUpdate;
}
}
private void SubscribeForSinkFeedback()
{
var sinkDevices = DeviceManager.AllDevices.OfType<IRoutingSinkWithSwitchingWithInputPort>();
foreach (var device in sinkDevices)
{
device.InputChanged += HandleSinkUpdate;
}
}
private void HandleMidpointUpdate(IRoutingWithFeedback midpoint, RouteSwitchDescriptor newRoute)
{
try
{
var devices = DeviceManager.AllDevices.OfType<IRoutingSinkWithSwitchingWithInputPort>();
foreach (var device in devices)
{
UpdateDestination(device, device.CurrentInputPort);
}
}
catch (Exception ex)
{
Debug.LogMessage(ex, "Error handling midpoint update from {midpointKey}:{Exception}", this, midpoint.Key, ex);
}
}
private void HandleSinkUpdate(IRoutingSinkWithSwitching sender, RoutingInputPort currentInputPort)
{
try
{
UpdateDestination(sender, currentInputPort);
}
catch (Exception ex)
{
Debug.LogMessage(ex, "Error handling Sink update from {senderKey}:{Exception}", this, sender.Key, ex);
}
}
private void UpdateDestination(IRoutingSinkWithSwitching destination, RoutingInputPort inputPort)
{
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Updating destination {destination} with inputPort {inputPort}", this,destination?.Key, inputPort?.Key);
if(inputPort == null)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "Destination {destination} has not reported an input port yet", this,destination.Key);
return;
}
TieLine firstTieLine;
try
{
var tieLines = TieLineCollection.Default;
firstTieLine = tieLines.FirstOrDefault(tl => tl.DestinationPort.Key == inputPort.Key && tl.DestinationPort.ParentDevice.Key == inputPort.ParentDevice.Key);
if (firstTieLine == null)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No tieline found for inputPort {inputPort}. Clearing current source", this, inputPort);
var tempSourceListItem = new SourceListItem
{
SourceKey = "$transient",
Name = inputPort.Key,
};
destination.CurrentSourceInfo = tempSourceListItem; ;
destination.CurrentSourceInfoKey = "$transient";
return;
}
} catch (Exception ex)
{
Debug.LogMessage(ex, "Error getting first tieline: {Exception}", this, ex);
return;
}
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Getting source for first TieLine {tieLine}", this, firstTieLine);
TieLine sourceTieLine;
try
{
sourceTieLine = GetRootTieLine(firstTieLine);
if (sourceTieLine == null)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No route found to source for inputPort {inputPort}. Clearing current source", this, inputPort);
var tempSourceListItem = new SourceListItem
{
SourceKey = "$transient",
Name = "None",
};
destination.CurrentSourceInfo = tempSourceListItem;
destination.CurrentSourceInfoKey = string.Empty;
return;
}
} catch(Exception ex)
{
Debug.LogMessage(ex, "Error getting sourceTieLine: {Exception}", this, ex);
return;
}
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found root TieLine {tieLine}", this, sourceTieLine);
// Does not handle combinable scenarios or other scenarios where a display might be part of multiple rooms yet.
var room = DeviceManager.AllDevices.OfType<IEssentialsRoom>().FirstOrDefault((r) => {
if(r is IHasMultipleDisplays roomMultipleDisplays)
{
return roomMultipleDisplays.Displays.Any(d => d.Value.Key == destination.Key);
}
if(r is IHasDefaultDisplay roomDefaultDisplay)
{
return roomDefaultDisplay.DefaultDisplay.Key == destination.Key;
}
return false;
});
if(room == null)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "No room found for display {destination}", this, destination.Key);
return;
}
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found room {room} for destination {destination}", this, room.Key, destination.Key);
var sourceList = ConfigReader.ConfigObject.GetSourceListForKey(room.SourceListKey);
if (sourceList == null)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "No source list found for source list key {key}. Unable to find source for tieLine {sourceTieLine}", this, room.SourceListKey, sourceTieLine);
return;
}
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found sourceList for room {room}", this, room.Key);
var sourceListItem = sourceList.FirstOrDefault(sli => {
//// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose,
// "SourceListItem {sourceListItem}:{sourceKey} tieLine sourceport device key {sourcePortDeviceKey}",
// this,
// sli.Key,
// sli.Value.SourceKey,
// sourceTieLine.SourcePort.ParentDevice.Key);
return sli.Value.SourceKey.Equals(sourceTieLine.SourcePort.ParentDevice.Key,StringComparison.InvariantCultureIgnoreCase);
});
var source = sourceListItem.Value;
var sourceKey = sourceListItem.Key;
if (source == null)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No source found for device {key}. Creating transient source for {destination}", this, sourceTieLine.SourcePort.ParentDevice.Key, destination);
var tempSourceListItem = new SourceListItem
{
SourceKey = "$transient",
Name = sourceTieLine.SourcePort.Key,
};
destination.CurrentSourceInfoKey = "$transient";
destination.CurrentSourceInfo = tempSourceListItem;
return;
}
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Got Source {@source} with key {sourceKey}", this, source, sourceKey);
destination.CurrentSourceInfoKey = sourceKey;
destination.CurrentSourceInfo = source;
}
private TieLine GetRootTieLine(TieLine tieLine)
{
TieLine nextTieLine = null;
try
{
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "**Following tieLine {tieLine}**", this, tieLine);
if (tieLine.SourcePort.ParentDevice is IRoutingWithFeedback midpoint)
{
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "TieLine Source device {sourceDevice} is midpoint", this, midpoint);
if(midpoint.CurrentRoutes == null || midpoint.CurrentRoutes.Count == 0)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "Midpoint {midpointKey} has no routes",this, midpoint.Key);
return null;
}
var currentRoute = midpoint.CurrentRoutes.FirstOrDefault(route => {
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Checking {route} against {tieLine}", this, route, tieLine);
return route.OutputPort != null && route.InputPort != null && route.OutputPort?.Key == tieLine.SourcePort.Key && route.OutputPort?.ParentDevice.Key == tieLine.SourcePort.ParentDevice.Key;
});
if (currentRoute == null)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No route through midpoint {midpoint} for outputPort {outputPort}", this, midpoint.Key, tieLine.SourcePort);
return null;
}
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found currentRoute {currentRoute} through {midpoint}", this, currentRoute, midpoint);
nextTieLine = TieLineCollection.Default.FirstOrDefault(tl => {
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Checking {route} against {tieLine}", tl.DestinationPort.Key, currentRoute.InputPort.Key);
return tl.DestinationPort.Key == currentRoute.InputPort.Key && tl.DestinationPort.ParentDevice.Key == currentRoute.InputPort.ParentDevice.Key; });
if (nextTieLine != null)
{
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found next tieLine {tieLine}. Walking the chain", this, nextTieLine);
return GetRootTieLine(nextTieLine);
}
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found root tieLine {tieLine}", this,nextTieLine);
return nextTieLine;
}
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "TieLIne Source Device {sourceDeviceKey} is IRoutingSource: {isIRoutingSource}", this, tieLine.SourcePort.ParentDevice.Key, tieLine.SourcePort.ParentDevice is IRoutingSource);
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "TieLine Source Device interfaces: {typeFullName}:{interfaces}", this, tieLine.SourcePort.ParentDevice.GetType().FullName, tieLine.SourcePort.ParentDevice.GetType().GetInterfaces().Select(i => i.Name));
if (tieLine.SourcePort.ParentDevice is IRoutingSource || tieLine.SourcePort.ParentDevice is IRoutingOutputs) //end of the chain
{
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found root: {tieLine}", this, tieLine);
return tieLine;
}
nextTieLine = TieLineCollection.Default.FirstOrDefault(tl => tl.DestinationPort.Key == tieLine.SourcePort.Key && tl.DestinationPort.ParentDevice.Key == tieLine.SourcePort.ParentDevice.Key );
if (nextTieLine != null)
{
return GetRootTieLine(nextTieLine);
}
} catch (Exception ex)
{
Debug.LogMessage(ex, "Error walking tieLines: {Exception}", this, ex);
return null;
}
return null;
}
}
}

View File

@@ -1,82 +0,0 @@
using System;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Basic RoutingInput with no statuses.
/// </summary>
public class RoutingInputPort : RoutingPort
{
/// <summary>
/// The IRoutingInputs object this lives on
/// </summary>
public IRoutingInputs ParentDevice { get; private set; }
/// <summary>
/// Constructor for a basic RoutingInputPort
/// </summary>
/// <param name="selector">An object used to refer to this port in the IRouting device's ExecuteSwitch method.
/// May be string, number, whatever</param>
/// <param name="parent">The IRoutingInputs object this lives on</param>
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
object selector, IRoutingInputs parent)
: this (key, type, connType, selector, parent, false)
{
}
/// <summary>
/// Constructor for a virtual routing input port that lives inside a device. For example
/// the ports that link a DM card to a DM matrix bus
/// </summary>
/// <param name="isInternal">true for internal ports</param>
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
object selector, IRoutingInputs parent, bool isInternal)
: base(key, type, connType, selector, isInternal)
{
if (parent == null)
throw new ArgumentNullException(nameof(parent));
ParentDevice = parent;
}
public override string ToString()
{
return $"{ParentDevice.Key}|{Key}|{Type}|{ConnectionType}";
}
}
/*/// <summary>
/// Basic RoutingInput with no statuses.
/// </summary>
public class RoutingInputPort<TSelector> : RoutingPort<TSelector>
{
/// <summary>
/// The IRoutingInputs object this lives on
/// </summary>
public IRoutingInputs<TSelector> ParentDevice { get; private set; }
/// <summary>
/// Constructor for a basic RoutingInputPort
/// </summary>
/// <param name="selector">An object used to refer to this port in the IRouting device's ExecuteSwitch method.
/// May be string, number, whatever</param>
/// <param name="parent">The IRoutingInputs object this lives on</param>
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
TSelector selector, IRoutingInputs<TSelector> parent)
: this(key, type, connType, selector, parent, false)
{
}
/// <summary>
/// Constructor for a virtual routing input port that lives inside a device. For example
/// the ports that link a DM card to a DM matrix bus
/// </summary>
/// <param name="isInternal">true for internal ports</param>
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
TSelector selector, IRoutingInputs<TSelector> parent, bool isInternal)
: base(key, type, connType, selector, isInternal)
{
ParentDevice = parent ?? throw new ArgumentNullException(nameof(parent));
}
}*/
}

View File

@@ -1,30 +0,0 @@
namespace PepperDash.Essentials.Core
{
/// <summary>
/// A RoutingInputPort for devices like DM-TX and DM input cards.
/// Will provide video statistics on connected signals
/// </summary>
public class RoutingInputPortWithVideoStatuses : RoutingInputPort
{
/// <summary>
/// Video statuses attached to this port
/// </summary>
public VideoStatusOutputs VideoStatus { get; private set; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="selector">An object used to refer to this port in the IRouting device's ExecuteSwitch method.
/// May be string, number, whatever</param>
/// <param name="parent">The IRoutingInputs object this lives on</param>
/// <param name="funcs">A VideoStatusFuncsWrapper used to assign the callback funcs that will get
/// the values for the various stats</param>
public RoutingInputPortWithVideoStatuses(string key,
eRoutingSignalType type, eRoutingPortConnectionType connType, object selector,
IRoutingInputs parent, VideoStatusFuncsWrapper funcs) :
base(key, type, connType, selector, parent)
{
VideoStatus = new VideoStatusOutputs(funcs);
}
}
}

View File

@@ -0,0 +1,203 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DM;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// The handler type for a Room's SourceInfoChange
/// </summary>
public delegate void SourceInfoChangeHandler(/*EssentialsRoomBase room,*/ SourceListItem info, ChangeType type);
//*******************************************************************************************
// Interfaces
/// <summary>
/// For rooms with a single presentation source, change event
/// </summary>
public interface IHasCurrentSourceInfoChange
{
string CurrentSourceInfoKey { get; set; }
SourceListItem CurrentSourceInfo { get; set; }
event SourceInfoChangeHandler CurrentSourceChange;
}
/// <summary>
/// Defines a class that has a collection of RoutingInputPorts
/// </summary>
public interface IRoutingInputs : IKeyed
{
RoutingPortCollection<RoutingInputPort> InputPorts { get; }
}
/// <summary>
/// Defines a class that has a collection of RoutingOutputPorts
/// </summary>
public interface IRoutingOutputs : IKeyed
{
RoutingPortCollection<RoutingOutputPort> OutputPorts { get; }
}
/// <summary>
/// For fixed-source endpoint devices
/// </summary>
public interface IRoutingSink : IRoutingInputs, IHasCurrentSourceInfoChange
{
}
/// <summary>
/// Endpoint device like a display, that selects inputs
/// </summary>
public interface IRoutingSinkWithSwitching : IRoutingSink
{
//void ClearRoute();
void ExecuteSwitch(object inputSelector);
}
/// <summary>
/// For devices like RMCs, baluns, other devices with no switching.
/// </summary>
public interface IRoutingInputsOutputs : IRoutingInputs, IRoutingOutputs
{
}
/// <summary>
/// Defines a midpoint device as have internal routing. Any devices in the middle of the
/// signal chain, that do switching, must implement this for routing to work otherwise
/// the routing algorithm will treat the IRoutingInputsOutputs device as a passthrough
/// device.
/// </summary>
public interface IRouting : IRoutingInputsOutputs
{
void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType);
}
public interface IRoutingWithClear : IRouting
{
/// <summary>
/// Clears a route to an output, however a device needs to do that
/// </summary>
/// <param name="outputSelector">Output to clear</param>
/// <param name="signalType">signal type to clear</param>
void ClearRoute(object outputSelector, eRoutingSignalType signalType);
}
public interface IRoutingNumeric : IRouting
{
void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type);
}
public interface ITxRouting : IRoutingNumeric
{
IntFeedback VideoSourceNumericFeedback { get; }
IntFeedback AudioSourceNumericFeedback { get; }
}
/// <summary>
/// Defines a receiver that has internal routing (DM-RMC-4K-Z-SCALER-C)
/// </summary>
public interface IRmcRouting : IRoutingNumeric
{
IntFeedback AudioVideoSourceNumericFeedback { get; }
}
/// <summary>
/// Defines an IRmcRouting with a feedback event
/// </summary>
public interface ITxRoutingWithFeedback : ITxRouting
{
}
/// <summary>
/// Defines an IRmcRouting with a feedback event
/// </summary>
public interface IRmcRoutingWithFeedback : IRmcRouting
{
}
/// <summary>
/// Defines an IRoutingOutputs devices as being a source - the start of the chain
/// </summary>
public interface IRoutingSource : IRoutingOutputs
{
}
/// <summary>
/// Defines an event structure for reporting output route data
/// </summary>
public interface IRoutingFeedback : IKeyName
{
event EventHandler<RoutingNumericEventArgs> NumericSwitchChange;
//void OnSwitchChange(RoutingNumericEventArgs e);
}
/// <summary>
/// Defines an IRoutingNumeric with a feedback event
/// </summary>
public interface IRoutingNumericWithFeedback : IRoutingNumeric, IRoutingFeedback
{
}
/// <summary>
/// Defines an IRouting with a feedback event
/// </summary>
public interface IRoutingWithFeedback : IRouting, IRoutingFeedback
{
}
public class RoutingNumericEventArgs : EventArgs
{
public uint? Output { get; set; }
public uint? Input { get; set; }
public eRoutingSignalType SigType { get; set; }
public RoutingInputPort InputPort { get; set; }
public RoutingOutputPort OutputPort { get; set; }
public RoutingNumericEventArgs(uint output, uint input, eRoutingSignalType sigType) : this(output, input, null, null, sigType)
{
}
public RoutingNumericEventArgs(RoutingOutputPort outputPort, RoutingInputPort inputPort,
eRoutingSignalType sigType)
: this(null, null, outputPort, inputPort, sigType)
{
}
public RoutingNumericEventArgs()
: this(null, null, null, null, 0)
{
}
public RoutingNumericEventArgs(uint? output, uint? input, RoutingOutputPort outputPort,
RoutingInputPort inputPort, eRoutingSignalType sigType)
{
OutputPort = outputPort;
InputPort = inputPort;
Output = output;
Input = input;
SigType = sigType;
}
}
public interface IRoutingHasVideoInputSyncFeedbacks
{
FeedbackCollection<BoolFeedback> VideoInputSyncFeedbacks { get; }
}
}

View File

@@ -1,43 +0,0 @@
using System;
namespace PepperDash.Essentials.Core
{
public class RoutingNumericEventArgs : EventArgs
{
public uint? Output { get; set; }
public uint? Input { get; set; }
public eRoutingSignalType SigType { get; set; }
public RoutingInputPort InputPort { get; set; }
public RoutingOutputPort OutputPort { get; set; }
public RoutingNumericEventArgs(uint output, uint input, eRoutingSignalType sigType) : this(output, input, null, null, sigType)
{
}
public RoutingNumericEventArgs(RoutingOutputPort outputPort, RoutingInputPort inputPort,
eRoutingSignalType sigType)
: this(null, null, outputPort, inputPort, sigType)
{
}
public RoutingNumericEventArgs()
: this(null, null, null, null, 0)
{
}
public RoutingNumericEventArgs(uint? output, uint? input, RoutingOutputPort outputPort,
RoutingInputPort inputPort, eRoutingSignalType sigType)
{
OutputPort = outputPort;
InputPort = inputPort;
Output = output;
Input = input;
SigType = sigType;
}
}
}

View File

@@ -1,75 +0,0 @@
using System;
namespace PepperDash.Essentials.Core
{
public class RoutingOutputPort : RoutingPort
{
/// <summary>
/// The IRoutingOutputs object this port lives on
/// </summary>
public IRoutingOutputs ParentDevice { get; private set; }
public InUseTracking InUseTracker { get; private set; }
/// <summary>
/// </summary>
/// <param name="selector">An object used to refer to this port in the IRouting device's ExecuteSwitch method.
/// May be string, number, whatever</param>
/// <param name="parent">The IRoutingOutputs object this port lives on</param>
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
object selector, IRoutingOutputs parent)
: this(key, type, connType, selector, parent, false)
{
}
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
object selector, IRoutingOutputs parent, bool isInternal)
: base(key, type, connType, selector, isInternal)
{
ParentDevice = parent ?? throw new ArgumentNullException(nameof(parent));
InUseTracker = new InUseTracking();
}
public override string ToString()
{
return $"{ParentDevice.Key}|{Key}|{Type}|{ConnectionType}";
}
}
/*public class RoutingOutputPort<TSelector> : RoutingPort<TSelector>
{
/// <summary>
/// The IRoutingOutputs object this port lives on
/// </summary>
public IRoutingOutputs ParentDevice { get; private set; }
public InUseTracking InUseTracker { get; private set; }
/// <summary>
/// </summary>
/// <param name="selector">An object used to refer to this port in the IRouting device's ExecuteSwitch method.
/// May be string, number, whatever</param>
/// <param name="parent">The IRoutingOutputs object this port lives on</param>
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
TSelector selector, IRoutingOutputs parent)
: this(key, type, connType, selector, parent, false)
{
}
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
TSelector selector, IRoutingOutputs parent, bool isInternal)
: base(key, type, connType, selector, isInternal)
{
ParentDevice = parent ?? throw new ArgumentNullException(nameof(parent));
InUseTracker = new InUseTracking();
}
public override string ToString()
{
return ParentDevice.Key + ":" + Key;
}
}*/
}

View File

@@ -1,12 +1,15 @@
using PepperDash.Core; using System;
using System.Collections.Generic;
using PepperDash.Core;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
/// <summary> /// <summary>
/// Base class for RoutingInput and Output ports /// Base class for RoutingInput and Output ports
/// </summary> /// </summary>
public abstract class RoutingPort : IKeyed public abstract class RoutingPort : IKeyed
{ {
public string Key { get; private set; } public string Key { get; private set; }
public eRoutingSignalType Type { get; private set; } public eRoutingSignalType Type { get; private set; }
@@ -23,26 +26,183 @@ namespace PepperDash.Essentials.Core
ConnectionType = connType; ConnectionType = connType;
Selector = selector; Selector = selector;
IsInternal = isInternal; IsInternal = isInternal;
} }
} }
/*public abstract class RoutingPort<TSelector>:IKeyed [Flags]
{ public enum eRoutingSignalType
public string Key { get; private set; } {
public eRoutingSignalType Type { get; private set; } Audio = 1,
public eRoutingPortConnectionType ConnectionType { get; private set; } Video = 2,
public readonly TSelector Selector; AudioVideo = Audio | Video,
public bool IsInternal { get; private set; } UsbOutput = 8,
public object FeedbackMatchObject { get; set; } UsbInput = 16,
public object Port { get; set; } SecondaryAudio = 32
}
public RoutingPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, TSelector selector, bool isInternal) public enum eRoutingPortConnectionType
{ {
Key = key; None, BackplaneOnly, DisplayPort, Dvi, Hdmi, Rgb, Vga, LineAudio, DigitalAudio, Sdi,
Type = type; Composite, Component, DmCat, DmMmFiber, DmSmFiber, Speaker, Streaming, UsbC, HdBaseT
ConnectionType = connType; }
Selector = selector;
IsInternal = isInternal; /// <summary>
} /// Basic RoutingInput with no statuses.
}*/ /// </summary>
public class RoutingInputPort : RoutingPort
{
/// <summary>
/// The IRoutingInputs object this lives on
/// </summary>
public IRoutingInputs ParentDevice { get; private set; }
/// <summary>
/// Constructor for a basic RoutingInputPort
/// </summary>
/// <param name="selector">An object used to refer to this port in the IRouting device's ExecuteSwitch method.
/// May be string, number, whatever</param>
/// <param name="parent">The IRoutingInputs object this lives on</param>
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
object selector, IRoutingInputs parent)
: this (key, type, connType, selector, parent, false)
{
}
/// <summary>
/// Constructor for a virtual routing input port that lives inside a device. For example
/// the ports that link a DM card to a DM matrix bus
/// </summary>
/// <param name="isInternal">true for internal ports</param>
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
object selector, IRoutingInputs parent, bool isInternal)
: base(key, type, connType, selector, isInternal)
{
if (parent == null)
throw new ArgumentNullException("parent");
ParentDevice = parent;
}
///// <summary>
///// Static method to get a named port from a named device
///// </summary>
///// <returns>Returns null if device or port doesn't exist</returns>
//public static RoutingInputPort GetDevicePort(string deviceKey, string portKey)
//{
// var sourceDev = DeviceManager.GetDeviceForKey(deviceKey) as IRoutingInputs;
// if (sourceDev == null)
// return null;
// return sourceDev.InputPorts[portKey];
//}
///// <summary>
///// Static method to get a named port from a card in a named ICardPortsDevice device
///// Uses ICardPortsDevice.GetChildInputPort
///// </summary>
///// <param name="cardKey">'input-N'</param>
///// <returns>null if device, card or port doesn't exist</returns>
//public static RoutingInputPort GetDeviceCardPort(string deviceKey, string cardKey, string portKey)
//{
// var sourceDev = DeviceManager.GetDeviceForKey(deviceKey) as ICardPortsDevice;
// if (sourceDev == null)
// return null;
// return sourceDev.GetChildInputPort(cardKey, portKey);
//}
}
/// <summary>
/// A RoutingInputPort for devices like DM-TX and DM input cards.
/// Will provide video statistics on connected signals
/// </summary>
public class RoutingInputPortWithVideoStatuses : RoutingInputPort
{
/// <summary>
/// Video statuses attached to this port
/// </summary>
public VideoStatusOutputs VideoStatus { get; private set; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="selector">An object used to refer to this port in the IRouting device's ExecuteSwitch method.
/// May be string, number, whatever</param>
/// <param name="parent">The IRoutingInputs object this lives on</param>
/// <param name="funcs">A VideoStatusFuncsWrapper used to assign the callback funcs that will get
/// the values for the various stats</param>
public RoutingInputPortWithVideoStatuses(string key,
eRoutingSignalType type, eRoutingPortConnectionType connType, object selector,
IRoutingInputs parent, VideoStatusFuncsWrapper funcs) :
base(key, type, connType, selector, parent)
{
VideoStatus = new VideoStatusOutputs(funcs);
}
}
public class RoutingOutputPort : RoutingPort
{
/// <summary>
/// The IRoutingOutputs object this port lives on
/// </summary>
public IRoutingOutputs ParentDevice { get; private set; }
public InUseTracking InUseTracker { get; private set; }
/// <summary>
/// </summary>
/// <param name="selector">An object used to refer to this port in the IRouting device's ExecuteSwitch method.
/// May be string, number, whatever</param>
/// <param name="parent">The IRoutingOutputs object this port lives on</param>
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
object selector, IRoutingOutputs parent)
: this(key, type, connType, selector, parent, false)
{
}
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
object selector, IRoutingOutputs parent, bool isInternal)
: base(key, type, connType, selector, isInternal)
{
if (parent == null)
throw new ArgumentNullException("parent");
ParentDevice = parent;
InUseTracker = new InUseTracking();
}
public override string ToString()
{
return ParentDevice.Key + ":" + Key;
}
///// <summary>
///// Static method to get a named port from a named device
///// </summary>
///// <returns>Returns null if device or port doesn't exist</returns>
//public static RoutingOutputPort GetDevicePort(string deviceKey, string portKey)
//{
// var sourceDev = DeviceManager.GetDeviceForKey(deviceKey) as IRoutingOutputs;
// if (sourceDev == null)
// return null;
// var port = sourceDev.OutputPorts[portKey];
// if (port == null)
// Debug.LogMessage(LogEventLevel.Information, "WARNING: Device '{0}' does does not contain output port '{1}'", deviceKey, portKey);
// return port;
//}
///// <summary>
///// Static method to get a named port from a card in a named ICardPortsDevice device
///// Uses ICardPortsDevice.GetChildOutputPort on that device
///// </summary>
///// <param name="cardKey">'input-N' or 'output-N'</param>
///// <returns>null if device, card or port doesn't exist</returns>
//public static RoutingOutputPort GetDeviceCardPort(string deviceKey, string cardKey, string portKey)
//{
// var sourceDev = DeviceManager.GetDeviceForKey(deviceKey) as ICardPortsDevice;
// if (sourceDev == null)
// return null;
// var port = sourceDev.GetChildOutputPort(cardKey, portKey);
//}
}
} }

View File

@@ -23,21 +23,4 @@ namespace PepperDash.Essentials.Core
} }
} }
} }
/* /// <summary>
/// Basically a List , with an indexer to find ports by key name
/// </summary>
public class RoutingPortCollection<T, TSelector> : List<T> where T : RoutingPort<TSelector>
{
/// <summary>
/// Case-insensitive port lookup linked to ports' keys
/// </summary>
public T this[string key]
{
get
{
return this.FirstOrDefault(i => i.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
}
}
}*/
} }

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using Crestron.SimplSharp; using Crestron.SimplSharp;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core.Routing
{ {
/// <summary> /// <summary>
/// These should correspond directly with the portNames var in the config tool. /// These should correspond directly with the portNames var in the config tool.
@@ -239,13 +239,5 @@ namespace PepperDash.Essentials.Core
/// HdBaseTOut /// HdBaseTOut
/// </summary> /// </summary>
public const string HdBaseTOut = "hdBaseTOut"; public const string HdBaseTOut = "hdBaseTOut";
/// <summary>
/// SdiIn
/// </summary>
public const string SdiIn = "sdiIn";
/// <summary>
/// SdiOut
/// </summary>
public const string SdiOut = "sdiOut";
} }
} }

View File

@@ -1,107 +1,111 @@
using Newtonsoft.Json; using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DM;
using PepperDash.Core;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
public class TieLine public class TieLine
{ {
public RoutingOutputPort SourcePort { get; private set; } public RoutingOutputPort SourcePort { get; private set; }
public RoutingInputPort DestinationPort { get; private set; } public RoutingInputPort DestinationPort { get; private set; }
//public int InUseCount { get { return DestinationUsingThis.Count; } } //public int InUseCount { get { return DestinationUsingThis.Count; } }
/// <summary> /// <summary>
/// Gets the type of this tie line. Will either be the type of the desination port /// Gets the type of this tie line. Will either be the type of the desination port
/// or the type of OverrideType when it is set. /// or the type of OverrideType when it is set.
/// </summary> /// </summary>
public eRoutingSignalType Type public eRoutingSignalType Type
{ {
get get
{ {
if (OverrideType.HasValue) return OverrideType.Value; if (OverrideType.HasValue) return OverrideType.Value;
return DestinationPort.Type; return DestinationPort.Type;
} }
} }
/// <summary> /// <summary>
/// Use this to override the Type property for the destination port. For example, /// Use this to override the Type property for the destination port. For example,
/// when the tie line is type AudioVideo, and the signal flow should be limited to /// when the tie line is type AudioVideo, and the signal flow should be limited to
/// Audio-only or Video only, changing this type will alter the signal paths /// Audio-only or Video only, changing this type will alter the signal paths
/// available to the routing algorithm without affecting the actual Type /// available to the routing algorithm without affecting the actual Type
/// of the destination port. /// of the destination port.
/// </summary> /// </summary>
public eRoutingSignalType? OverrideType { get; set; } public eRoutingSignalType? OverrideType { get; set; }
//List<IRoutingInputs> DestinationUsingThis = new List<IRoutingInputs>(); //List<IRoutingInputs> DestinationUsingThis = new List<IRoutingInputs>();
/// <summary> /// <summary>
/// For tie lines that represent internal links, like from cards to the matrix in a DM. /// For tie lines that represent internal links, like from cards to the matrix in a DM.
/// This property is true if SourcePort and DestinationPort IsInternal /// This property is true if SourcePort and DestinationPort IsInternal
/// property are both true /// property are both true
/// </summary> /// </summary>
public bool IsInternal { get { return SourcePort.IsInternal && DestinationPort.IsInternal; } } public bool IsInternal { get { return SourcePort.IsInternal && DestinationPort.IsInternal; } }
public bool TypeMismatch { get { return SourcePort.Type != DestinationPort.Type; } } public bool TypeMismatch { get { return SourcePort.Type != DestinationPort.Type; } }
public bool ConnectionTypeMismatch { get { return SourcePort.ConnectionType != DestinationPort.ConnectionType; } } public bool ConnectionTypeMismatch { get { return SourcePort.ConnectionType != DestinationPort.ConnectionType; } }
public string TypeMismatchNote { get; set; } public string TypeMismatchNote { get; set; }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="sourcePort"></param> /// <param name="sourcePort"></param>
/// <param name="destinationPort"></param> /// <param name="destinationPort"></param>
public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort) public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort)
{ {
if (sourcePort == null || destinationPort == null) if (sourcePort == null || destinationPort == null)
throw new ArgumentNullException("source or destination port"); throw new ArgumentNullException("source or destination port");
SourcePort = sourcePort; SourcePort = sourcePort;
DestinationPort = destinationPort; DestinationPort = destinationPort;
} }
/// <summary> /// <summary>
/// Creates a tie line with an overriding Type. See help for OverrideType property for info /// Creates a tie line with an overriding Type. See help for OverrideType property for info
/// </summary> /// </summary>
/// <param name="overrideType">The signal type to limit the link to. Overrides DestinationPort.Type</param> /// <param name="overrideType">The signal type to limit the link to. Overrides DestinationPort.Type</param>
public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort, eRoutingSignalType overrideType) : public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort, eRoutingSignalType overrideType) :
this(sourcePort, destinationPort) this(sourcePort, destinationPort)
{ {
OverrideType = overrideType; OverrideType = overrideType;
} }
/// <summary> /// <summary>
/// Will link up video status from supporting inputs to connected outputs /// Will link up video status from supporting inputs to connected outputs
/// </summary> /// </summary>
public void Activate() public void Activate()
{ {
// Now does nothing // Now does nothing
} }
public void Deactivate() public void Deactivate()
{ {
// Now does nothing // Now does nothing
} }
public override string ToString() public override string ToString()
{ {
return string.Format("Tie line: {0}:{1} --> {2}:{3} {4}", SourcePort.ParentDevice.Key, SourcePort.Key, return string.Format("Tie line: [{0}]{1} --> [{2}]{3}", SourcePort.ParentDevice.Key, SourcePort.Key,
DestinationPort.ParentDevice.Key, DestinationPort.Key, Type.ToString()); DestinationPort.ParentDevice.Key, DestinationPort.Key);
} }
} }
//******************************************************************************** //********************************************************************************
public class TieLineCollection : List<TieLine> public class TieLineCollection : List<TieLine>
{ {
public static TieLineCollection Default public static TieLineCollection Default
{ {
get get
{ {
if (_Default == null) if (_Default == null)
_Default = new TieLineCollection(); _Default = new TieLineCollection();
return _Default; return _Default;
} }
} }
static TieLineCollection _Default;
[JsonIgnore] }
private static TieLineCollection _Default;
}
} }

View File

@@ -30,7 +30,7 @@ namespace PepperDash.Essentials.Core.Config
/// <returns>null if config data does not match ports, cards or devices</returns> /// <returns>null if config data does not match ports, cards or devices</returns>
public TieLine GetTieLine() public TieLine GetTieLine()
{ {
Debug.LogMessage(LogEventLevel.Information, "Build TieLine: {0}",null, this); Debug.LogMessage(LogEventLevel.Information, "Build TieLine: {0}", this);
// Get the source device // Get the source device
var sourceDev = DeviceManager.GetDeviceForKey(SourceKey) as IRoutingOutputs; var sourceDev = DeviceManager.GetDeviceForKey(SourceKey) as IRoutingOutputs;
if (sourceDev == null) if (sourceDev == null)
@@ -48,29 +48,68 @@ namespace PepperDash.Essentials.Core.Config
} }
//Get the source port //Get the source port
var sourceOutputPort = sourceDev.OutputPorts[SourcePort]; RoutingOutputPort sourceOutputPort = null;
//// If it's a card-based device, get the card and then the source port
//if (sourceDev is ICardPortsDevice)
//{
// if (SourceCard == null)
// {
// LogError("Card missing from source device config");
// return null;
// }
// sourceOutputPort = (sourceDev as ICardPortsDevice).GetChildOutputPort(SourceCard, SourcePort);
// if (sourceOutputPort == null)
// {
// LogError("Source card does not contain port");
// return null;
// }
//}
//// otherwise it's a normal port device, get the source port
//else
//{
sourceOutputPort = sourceDev.OutputPorts[SourcePort];
if (sourceOutputPort == null)
{
LogError("Source does not contain port");
return null;
}
//}
if (sourceOutputPort == null)
{
LogError("Source does not contain port");
return null;
}
//Get the Destination port //Get the Destination port
var destinationInputPort = destDev.InputPorts[DestinationPort]; RoutingInputPort destinationInputPort = null;
//// If it's a card-based device, get the card and then the Destination port
if (destinationInputPort == null) //if (destDev is ICardPortsDevice)
//{
// if (DestinationCard == null)
// {
// LogError("Card missing from destination device config");
// return null;
// }
// destinationInputPort = (destDev as ICardPortsDevice).GetChildInputPort(DestinationCard, DestinationPort);
// if (destinationInputPort == null)
// {
// LogError("Destination card does not contain port");
// return null;
// }
//}
//// otherwise it's a normal port device, get the Destination port
//else
//{
destinationInputPort = destDev.InputPorts[DestinationPort];
if (destinationInputPort == null)
{ {
LogError("Destination does not contain port"); LogError("Destination does not contain port");
return null; return null;
} }
//}
return new TieLine(sourceOutputPort, destinationInputPort); return new TieLine(sourceOutputPort, destinationInputPort);
} }
void LogError(string msg) void LogError(string msg)
{ {
Debug.LogMessage(LogEventLevel.Error, "WARNING: Cannot create tie line: {message}:\r {tieLineConfig}",null, msg, this); Debug.LogMessage(LogEventLevel.Debug, "WARNING: Cannot create tie line: {0}:\r {1}", msg, this);
} }
public override string ToString() public override string ToString()

View File

@@ -1,8 +0,0 @@
namespace PepperDash.Essentials.Core
{
public enum eRoutingPortConnectionType
{
None, BackplaneOnly, DisplayPort, Dvi, Hdmi, Rgb, Vga, LineAudio, DigitalAudio, Sdi,
Composite, Component, DmCat, DmMmFiber, DmSmFiber, Speaker, Streaming, UsbC, HdBaseT
}
}

Some files were not shown because too many files have changed in this diff Show More