mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-02-16 21:24:54 +00:00
Compare commits
176 Commits
2.0.0-alph
...
feature-2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afc37f5426 | ||
|
|
4ed5bb7ada | ||
|
|
4360e7f992 | ||
|
|
f926db418d | ||
|
|
0944be2a70 | ||
|
|
bacc0a4f57 | ||
|
|
1d49ea67ad | ||
|
|
35018b3572 | ||
|
|
237fff5398 | ||
|
|
b2eab21fbd | ||
|
|
f2545fb1cf | ||
|
|
27072e3475 | ||
|
|
e4755ed9df | ||
|
|
316867caf8 | ||
|
|
d8fd774324 | ||
|
|
e0058d8cfe | ||
|
|
a055d06bc6 | ||
|
|
66cb592c70 | ||
|
|
d53a5607e2 | ||
|
|
34f59f1410 | ||
|
|
261779d4c4 | ||
|
|
30d5e2b081 | ||
|
|
5516ed16c3 | ||
|
|
8108b9dfdb | ||
|
|
fb4f1482c7 | ||
|
|
54dcb5de08 | ||
|
|
4ef481375c | ||
|
|
d8a88b2a07 | ||
|
|
cc724ddf19 | ||
|
|
e29e800d9d | ||
|
|
134e8ba02e | ||
|
|
a83ba444d3 | ||
|
|
f4c5e6fbeb | ||
|
|
35d7994cc8 | ||
|
|
c3e9d654c9 | ||
|
|
f68b1e9e49 | ||
|
|
cd81b8af73 | ||
|
|
cd52c245a6 | ||
|
|
0b60f53d0e | ||
|
|
ffed2dea8a | ||
|
|
936dce2046 | ||
|
|
b33704eabe | ||
|
|
aca6fe9af5 | ||
|
|
332faaa9cc | ||
|
|
4449077a39 | ||
|
|
86ba9e0f16 | ||
|
|
db2d8a213d | ||
|
|
590e16298c | ||
|
|
1a11e9019c | ||
|
|
0e16dff90c | ||
|
|
d11827bc7b | ||
|
|
631dd2b00d | ||
|
|
639cd2abfb | ||
|
|
f04f70495f | ||
|
|
fa38e8a9a8 | ||
|
|
f351c036ed | ||
|
|
0a7da79356 | ||
|
|
82ebf45921 | ||
|
|
d0dbe986f3 | ||
|
|
aa503f3b29 | ||
|
|
90251d92df | ||
|
|
778af651d0 | ||
|
|
2e31719a84 | ||
|
|
a96fb21898 | ||
|
|
3f45372e1e | ||
|
|
60dbaf547d | ||
|
|
941e537d53 | ||
|
|
f7c5e18af8 | ||
|
|
ab73bbf979 | ||
|
|
1fb1947158 | ||
|
|
e374f7b50f | ||
|
|
7a263a644a | ||
|
|
97bd30e9c9 | ||
|
|
63c653b21f | ||
|
|
c56841d95b | ||
|
|
64d6df70b0 | ||
|
|
d970d806c9 | ||
|
|
5a9b876d80 | ||
|
|
fb60683af6 | ||
|
|
b63996b9e6 | ||
|
|
bc217a2008 | ||
|
|
fec6b0d385 | ||
|
|
f3b4c0aa02 | ||
|
|
a3351812cd | ||
|
|
71815eff17 | ||
|
|
c7f4bf1fb2 | ||
|
|
bec3ab8e73 | ||
|
|
c499d2a2eb | ||
|
|
d9721a362e | ||
|
|
5fb6f3e117 | ||
|
|
c2fb44a662 | ||
|
|
0f9bddf4dd | ||
|
|
ddc2491664 | ||
|
|
9a6209f50a | ||
|
|
49852d25e8 | ||
|
|
ba4ca20936 | ||
|
|
c50726f813 | ||
|
|
0aafe8a62e | ||
|
|
71005940ac | ||
|
|
e5e79316a6 | ||
|
|
5aa1f85df5 | ||
|
|
7bac65002d | ||
|
|
ed0141a536 | ||
|
|
25ebcdfb5d | ||
|
|
b326ccf6c3 | ||
|
|
3a56e47c48 | ||
|
|
171bd6b1ec | ||
|
|
e61fd7777a | ||
|
|
027bdd5bf4 | ||
|
|
b876b8123d | ||
|
|
6e05653c6c | ||
|
|
16d32bc720 | ||
|
|
0bef5d4b77 | ||
|
|
a3c1572a77 | ||
|
|
cc06c7bfd8 | ||
|
|
00a7b25026 | ||
|
|
edc916c9d3 | ||
|
|
c755ecb16c | ||
|
|
a8c36ba243 | ||
|
|
f630d3f410 | ||
|
|
35e0662b27 | ||
|
|
effefc939c | ||
|
|
5afdc2effa | ||
|
|
448cc273ec | ||
|
|
0a2aaa693f | ||
|
|
1ebee58ad6 | ||
|
|
3c5fe88e5a | ||
|
|
8255328be1 | ||
|
|
4bf026601f | ||
|
|
888f1b3888 | ||
|
|
19bd5723c8 | ||
|
|
e3e7add5b9 | ||
|
|
dd66de0463 | ||
|
|
3823943cd9 | ||
|
|
577e111f26 | ||
|
|
528fff569d | ||
|
|
06a6b1caa2 | ||
|
|
621d848418 | ||
|
|
2f9038a501 | ||
|
|
983b18d25a | ||
|
|
048004d441 | ||
|
|
e7e448f02c | ||
|
|
2e61d8d709 | ||
|
|
d8d2c5b340 | ||
|
|
7e736ae519 | ||
|
|
0067e11d3d | ||
|
|
7942c91f73 | ||
|
|
e1638762a1 | ||
|
|
3566400379 | ||
|
|
dde85f39a2 | ||
|
|
735433f660 | ||
|
|
734149960b | ||
|
|
cb16f2a505 | ||
|
|
a95d44e405 | ||
|
|
cb9eb5dafa | ||
|
|
eb955aa014 | ||
|
|
2d9ffca78e | ||
|
|
98f1a09c25 | ||
|
|
a11ad421f0 | ||
|
|
8878ff7ddd | ||
|
|
7e4b5f984f | ||
|
|
64ab315142 | ||
|
|
c47a93f4d0 | ||
|
|
5a55a701d6 | ||
|
|
01862ab9aa | ||
|
|
8ec6fa785e | ||
|
|
e37c675da1 | ||
|
|
3ee8cb7ea3 | ||
|
|
2b6f79b68f | ||
|
|
65369606a4 | ||
|
|
e9954b3081 | ||
|
|
e1b50649fd | ||
|
|
5e69ea1947 | ||
|
|
fd1b92a6c0 | ||
|
|
06d806687d | ||
|
|
d8e2f8cd51 |
52
.github/workflows/docker.yml
vendored
52
.github/workflows/docker.yml
vendored
@@ -66,6 +66,55 @@ 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: |
|
||||||
@@ -74,9 +123,6 @@ jobs:
|
|||||||
# Create the release on the source repo
|
# Create the release on the source repo
|
||||||
- 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)'
|
||||||
|
|||||||
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -37,6 +37,8 @@ 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
1
.gitignore
vendored
@@ -391,3 +391,4 @@ 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
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
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.
|
||||||
|
|||||||
@@ -1,6 +1,19 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="$(PackageOutputPath)\$(AssemblyName)\*.cpz" Condition="$(ProjectType) == 'Program'">
|
<!-- Include CPZ files from multiple possible locations -->
|
||||||
|
<None Include="$(TargetDir)*.cpz" Condition="$(ProjectType) == 'Program'">
|
||||||
|
<Pack>true</Pack>
|
||||||
|
<PackagePath>build;</PackagePath>
|
||||||
|
</None>
|
||||||
|
<None Include="$(OutputPath)*.cpz" Condition="$(ProjectType) == 'Program'">
|
||||||
|
<Pack>true</Pack>
|
||||||
|
<PackagePath>build;</PackagePath>
|
||||||
|
</None>
|
||||||
|
<None Include="$(MSBuildProjectDirectory)\bin\$(Configuration)\**\*.cpz" Condition="$(ProjectType) == 'Program'">
|
||||||
|
<Pack>true</Pack>
|
||||||
|
<PackagePath>build;</PackagePath>
|
||||||
|
</None>
|
||||||
|
<None Include="$(PackageOutputPath)\build\*.cpz" Condition="$(ProjectType) == 'Program'">
|
||||||
<Pack>true</Pack>
|
<Pack>true</Pack>
|
||||||
<PackagePath>build;</PackagePath>
|
<PackagePath>build;</PackagePath>
|
||||||
</None>
|
</None>
|
||||||
@@ -10,17 +23,94 @@
|
|||||||
</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)"></Message>
|
<Message Text="Creating CPLZ $(TargetDir)" Importance="high" />
|
||||||
|
<Message Text="PackageOutputPath: $(PackageOutputPath)" Importance="high" />
|
||||||
|
<Message Text="AssemblyName: $(AssemblyName)" Importance="high" />
|
||||||
|
<Message Text="TargetName: $(TargetName)" Importance="high" />
|
||||||
|
<Message Text="Version: $(Version)" Importance="high" />
|
||||||
|
<Message Text="TargetFramework: $(TargetFramework)" Importance="high" />
|
||||||
<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="Copy CPZ NET6" AfterTargets="SimplSharpPostProcess" Condition="($(ProjectType) == 'Program' And ( '$(TargetFramework)' == 'net6.0' ) Or ( '$(TargetFramework)' == 'net8.0' ))">
|
<Target Name="Debug Variables" BeforeTargets="Build">
|
||||||
<Message Text="Copying CPZ"></Message>
|
<Message Text="================ Debug Variables ================" Importance="high" />
|
||||||
<Move SourceFiles="$(TargetDir)$(TargetName).cpz" DestinationFiles="$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cpz" />
|
<Message Text="ProjectType: '$(ProjectType)'" Importance="high" />
|
||||||
|
<Message Text="TargetFramework: '$(TargetFramework)'" Importance="high" />
|
||||||
|
<Message Text="TargetDir: '$(TargetDir)'" Importance="high" />
|
||||||
|
<Message Text="===============================================" Importance="high" />
|
||||||
</Target>
|
</Target>
|
||||||
<Target Name="Copy CPZ NET47" AfterTargets="SimplSharpPostProcess47" Condition="($(ProjectType) == 'Program' And ( '$(TargetFramework)' != 'net6.0' ) And ( '$(TargetFramework)' != 'net8.0' ))">
|
<Target Name="Copy CPZ NET472"
|
||||||
<Message Text="Copying CPZ"></Message>
|
AfterTargets="Build"
|
||||||
<Move SourceFiles="$(TargetDir)$(TargetName).cpz" DestinationFiles="$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cpz" />
|
DependsOnTargets="BuildDependencies"
|
||||||
|
Condition="'$(ProjectType)' == 'Program' And '$(TargetFramework)' == 'net472'">
|
||||||
|
<Message Text="========================================" Importance="high" />
|
||||||
|
<Message Text="Starting CPZ Build Process for NET472" Importance="high" />
|
||||||
|
<Message Text="ProjectType: '$(ProjectType)'" Importance="high" />
|
||||||
|
<Message Text="TargetFramework: '$(TargetFramework)'" Importance="high" />
|
||||||
|
<Message Text="========================================" Importance="high" />
|
||||||
|
|
||||||
|
<!-- Create output directory -->
|
||||||
|
<MakeDir Directories="$(PackageOutputPath)\$(AssemblyName)"
|
||||||
|
Condition="!Exists('$(PackageOutputPath)\$(AssemblyName)')" />
|
||||||
|
|
||||||
|
<!-- Copy the CPZ file -->
|
||||||
|
<Copy SourceFiles="$(TargetDir)$(TargetName).cpz"
|
||||||
|
DestinationFiles="$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cpz"
|
||||||
|
Condition="Exists('$(TargetDir)$(TargetName).cpz')" />
|
||||||
|
|
||||||
|
<Message Text="CPZ Build completed for NET472"
|
||||||
|
Condition="Exists('$(PackageOutputPath)\$(AssemblyName)\$(TargetName).$(Version).$(TargetFramework).cpz')"
|
||||||
|
Importance="high" />
|
||||||
|
</Target>
|
||||||
|
<Target Name="Copy CPZ NET6"
|
||||||
|
AfterTargets="Build"
|
||||||
|
DependsOnTargets="BuildDependencies"
|
||||||
|
Condition="'$(ProjectType)' == 'Program' And ('$(TargetFramework)' == 'net6.0' Or '$(TargetFramework)' == 'net8.0')">
|
||||||
|
<Message Text="========================================" Importance="high" />
|
||||||
|
<Message Text="Starting CPZ Build Process" Importance="high" />
|
||||||
|
<Message Text="ProjectType: '$(ProjectType)'" Importance="high" />
|
||||||
|
<Message Text="TargetFramework: '$(TargetFramework)'" Importance="high" />
|
||||||
|
<Message Text="MSBuildProjectDirectory: '$(MSBuildProjectDirectory)'" Importance="high" />
|
||||||
|
<Message Text="PATH: '$(PATH)'" Importance="high" />
|
||||||
|
<Message Text="========================================" Importance="high" />
|
||||||
|
|
||||||
|
<!-- Check for SimplSharp compiler -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<SimplSharpCompilerPath>$(HOME)/.crestron/SimplSharpPro/SimplSharpCompiler</SimplSharpCompilerPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Warning Text="SimplSharpCompiler not found at: $(SimplSharpCompilerPath)"
|
||||||
|
Condition="!Exists('$(SimplSharpCompilerPath)')" />
|
||||||
|
|
||||||
|
<!-- Run the SimplSharp compiler to create CPZ -->
|
||||||
|
<Exec Command=""$(SimplSharpCompilerPath)" "$(MSBuildProjectDirectory)" "$(TargetDir)""
|
||||||
|
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>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Crestron.SimplSharp.Reflection;
|
using System.Reflection;
|
||||||
using Crestron.SimplSharpPro;
|
using Crestron.SimplSharpPro;
|
||||||
using Crestron.SimplSharpPro.DeviceSupport;
|
using Crestron.SimplSharpPro.DeviceSupport;
|
||||||
using Crestron.SimplSharpPro.EthernetCommunication;
|
using Crestron.SimplSharpPro.EthernetCommunication;
|
||||||
@@ -18,81 +18,6 @@ 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>
|
||||||
@@ -168,19 +93,15 @@ 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 (!typeof(IBridgeAdvanced).IsAssignableFrom(device.GetType().GetCType()))
|
if (device is IBridgeAdvanced bridge)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Information, this,
|
bridge.LinkToApi(Eisc, d.JoinStart, d.JoinMapKey, this);
|
||||||
"{0} is not compatible with this bridge type. Please use 'eiscapi' instead, or updae the device.",
|
|
||||||
device.Key);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var bridge = device as IBridgeAdvanced;
|
Debug.LogMessage(LogEventLevel.Information, this,
|
||||||
if (bridge != null)
|
"{0} is not compatible with this bridge type. Please use 'eiscapi' instead, or updae the device.",
|
||||||
{
|
device.Key);
|
||||||
bridge.LinkToApi(Eisc, d.JoinStart, d.JoinMapKey, this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,11 +170,11 @@ namespace PepperDash.Essentials.Core.Bridges
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void PrintJoinMaps()
|
public virtual void PrintJoinMaps()
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Information, this, "Join Maps for EISC IPID: {0}", Eisc.ID.ToString("X"));
|
CrestronConsole.ConsoleCommandResponse("Join Maps for EISC IPID: {0}\r\n", Eisc.ID.ToString("X"));
|
||||||
|
|
||||||
foreach (var joinMap in JoinMaps)
|
foreach (var joinMap in JoinMaps)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Join map for device '{0}':", joinMap.Key);
|
CrestronConsole.ConsoleCommandResponse("Join map for device '{0}':", joinMap.Key);
|
||||||
joinMap.Value.PrintJoinMapInfo();
|
joinMap.Value.PrintJoinMapInfo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
66
src/PepperDash.Essentials.Core/Bridges/BridgeHelper.cs
Normal file
66
src/PepperDash.Essentials.Core/Bridges/BridgeHelper.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -93,6 +93,7 @@ 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)
|
||||||
@@ -101,6 +102,7 @@ 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)
|
||||||
@@ -108,7 +110,10 @@ 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()
|
||||||
|
|||||||
@@ -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; } }
|
||||||
|
|||||||
@@ -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, controlConfig);
|
comm = new ComPortController(deviceConfig.Key + "-com", GetComPort, controlConfig.ComParams.Value, 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];
|
return dev.ComPorts[config.ControlPortNumber.Value];
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -202,17 +202,20 @@ namespace PepperDash.Essentials.Core
|
|||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class EssentialsControlPropertiesConfig :
|
public class EssentialsControlPropertiesConfig :
|
||||||
PepperDash.Core.ControlPropertiesConfig
|
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
|
||||||
@@ -228,11 +231,13 @@ 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
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
using System;
|
using Crestron.SimplSharp.Net.Http;
|
||||||
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;
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core
|
namespace PepperDash.Essentials.Core
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
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>();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ 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
|
||||||
{
|
{
|
||||||
@@ -23,7 +24,13 @@ 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; }
|
||||||
@@ -37,6 +44,8 @@ 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>();
|
||||||
}
|
}
|
||||||
@@ -46,7 +55,7 @@ namespace PepperDash.Essentials.Core.Config
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<string, SourceListItem> GetSourceListForKey(string key)
|
public Dictionary<string, SourceListItem> GetSourceListForKey(string key)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(key) || !SourceLists.ContainsKey(key))
|
if (SourceLists == null || string.IsNullOrEmpty(key) || !SourceLists.ContainsKey(key))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return SourceLists[key];
|
return SourceLists[key];
|
||||||
@@ -55,11 +64,11 @@ 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 item to retrieve</param>
|
/// <param name="key">key of the list to retrieve</param>
|
||||||
/// <returns>DestinationListItem if the key exists, null otherwise</returns>
|
/// <returns>DestinationList if the key exists, null otherwise</returns>
|
||||||
public Dictionary<string, DestinationListItem> GetDestinationListForKey(string key)
|
public Dictionary<string, DestinationListItem> GetDestinationListForKey(string key)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(key) || !DestinationLists.ContainsKey(key))
|
if (DestinationLists == null || string.IsNullOrEmpty(key) || !DestinationLists.ContainsKey(key))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -67,6 +76,30 @@ namespace PepperDash.Essentials.Core.Config
|
|||||||
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
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -31,9 +31,17 @@ 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\/(.*)\/#.*");
|
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*");
|
||||||
string uuid = result.Groups[1].Value;
|
string uuid = result.Groups[1].Value;
|
||||||
return uuid;
|
return uuid;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/.*");
|
||||||
|
string uuid = result.Groups[1].Value;
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,9 +53,17 @@ 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\/(.*)\/#.*");
|
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*");
|
||||||
string uuid = result.Groups[1].Value;
|
string uuid = result.Groups[1].Value;
|
||||||
return uuid;
|
return uuid;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/system-templates\/(.*)\/system-template-versions\/(.*)\/.*");
|
||||||
|
string uuid = result.Groups[2].Value;
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
public class GenericVersiportDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput, IPartitionStateProvider
|
||||||
{
|
{
|
||||||
public Versiport InputPort { get; private set; }
|
public Versiport InputPort { get; private set; }
|
||||||
|
|
||||||
@@ -35,10 +35,15 @@ 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(() =>
|
||||||
{
|
{
|
||||||
@@ -52,7 +57,8 @@ 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);
|
||||||
|
|
||||||
@@ -65,7 +71,10 @@ 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,10 +15,22 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
|
|||||||
/// <example>
|
/// <example>
|
||||||
/// See MockDisplay for example implemntation
|
/// See MockDisplay for example implemntation
|
||||||
/// </example>
|
/// </example>
|
||||||
public interface IHasInputs<TKey, TSelector>: IKeyName
|
[Obsolete("Use IHasInputs<T> instead. Will be removed for 2.0 release")]
|
||||||
|
public interface IHasInputs<T, TSelector>: IKeyName
|
||||||
{
|
{
|
||||||
ISelectableItems<TKey> Inputs { get; }
|
ISelectableItems<T> 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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
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; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,6 +44,9 @@ 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>
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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")]
|
||||||
string CurrentItem { get; set; }
|
TKey CurrentItem { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
76
src/PepperDash.Essentials.Core/Devices/CameraListItem.cs
Normal file
76
src/PepperDash.Essentials.Core/Devices/CameraListItem.cs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,13 @@
|
|||||||
|
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.Text;
|
using System.Reflection;
|
||||||
using Crestron.SimplSharp;
|
using System.Threading.Tasks;
|
||||||
using Crestron.SimplSharp.Reflection;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
using PepperDash.Core;
|
|
||||||
using Serilog.Events;
|
|
||||||
|
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core
|
namespace PepperDash.Essentials.Core
|
||||||
{
|
{
|
||||||
@@ -35,7 +31,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
|
|
||||||
DoDeviceAction(action);
|
DoDeviceAction(action);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
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]}");
|
||||||
}
|
}
|
||||||
@@ -63,7 +59,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
action.Params = new object[0];
|
action.Params = new object[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
CType t = obj.GetType();
|
Type 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();
|
||||||
@@ -82,7 +78,19 @@ namespace PepperDash.Essentials.Core
|
|||||||
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();
|
||||||
|
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Calling method {methodName} on device {deviceKey}", null, method.Name, action.DeviceKey);
|
||||||
method.Invoke(obj, convertedParams);
|
method.Invoke(obj, convertedParams);
|
||||||
|
}
|
||||||
|
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,
|
CrestronConsole.ConsoleCommandResponse("Method {0} successfully called on device {1}", method.Name,
|
||||||
action.DeviceKey);
|
action.DeviceKey);
|
||||||
@@ -90,7 +98,71 @@ namespace PepperDash.Essentials.Core
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
CrestronConsole.ConsoleCommandResponse("Unable to call method with name {0}. {1}", action.MethodName,
|
CrestronConsole.ConsoleCommandResponse("Unable to call method with name {0}. {1}", action.MethodName,
|
||||||
ex.Message);}
|
ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task DoDeviceActionAsync(DeviceActionWrapper action)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
//no params, so setting action.Params to empty array
|
||||||
|
action.Params = new object[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
Type t = obj.GetType();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (method == null)
|
||||||
|
{
|
||||||
|
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)
|
private static object ConvertType(object value, Type conversionType)
|
||||||
@@ -121,7 +193,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
if (obj == null)
|
if (obj == null)
|
||||||
return "{ \"error\":\"No Device\"}";
|
return "{ \"error\":\"No Device\"}";
|
||||||
|
|
||||||
CType t = obj.GetType();
|
Type 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
|
||||||
var props = t.GetProperties().Select(p => new PropertyNameType(p, obj));
|
var props = t.GetProperties().Select(p => new PropertyNameType(p, obj));
|
||||||
return JsonConvert.SerializeObject(props, Formatting.Indented);
|
return JsonConvert.SerializeObject(props, Formatting.Indented);
|
||||||
@@ -136,10 +208,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().GetCType().GetProperty(propertyName).GetValue(dev, null);
|
object prop = dev.GetType().GetProperty(propertyName).GetValue(dev, null);
|
||||||
|
|
||||||
// var prop = t.GetProperty(propertyName);
|
// var prop = t.GetProperty(propertyName);
|
||||||
if (prop != null)
|
if (prop != null)
|
||||||
@@ -165,7 +237,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
return "{ \"error\":\"No Device\"}";
|
return "{ \"error\":\"No Device\"}";
|
||||||
|
|
||||||
// Package up method names using helper objects
|
// Package up method names using helper objects
|
||||||
CType t = obj.GetType();
|
Type 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));
|
||||||
@@ -179,7 +251,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
return "{ \"error\":\"No Device\"}";
|
return "{ \"error\":\"No Device\"}";
|
||||||
|
|
||||||
// Package up method names using helper objects
|
// Package up method names using helper objects
|
||||||
CType t = obj.GetType();
|
Type 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())
|
||||||
@@ -226,7 +298,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
Debug.LogMessage(LogEventLevel.Information, dev, " Checking for collection '{0}', index '{1}'", objName, indexStr);
|
Debug.LogMessage(LogEventLevel.Information, dev, " Checking for collection '{0}', index '{1}'", objName, indexStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
CType oType = obj.GetType();
|
Type oType = obj.GetType();
|
||||||
var prop = oType.GetProperty(objName);
|
var prop = oType.GetProperty(objName);
|
||||||
if (prop == null)
|
if (prop == null)
|
||||||
{
|
{
|
||||||
@@ -256,7 +328,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
obj = indexedPropInfo.GetValue(collection, new object[] { properParam });
|
obj = indexedPropInfo.GetValue(collection, new object[] { properParam });
|
||||||
}
|
}
|
||||||
// if the index is bad, catch it here.
|
// if the index is bad, catch it here.
|
||||||
catch (Crestron.SimplSharp.Reflection.TargetInvocationException e)
|
catch (TargetInvocationException e)
|
||||||
{
|
{
|
||||||
if (e.InnerException is ArgumentOutOfRangeException)
|
if (e.InnerException is ArgumentOutOfRangeException)
|
||||||
Debug.LogMessage(LogEventLevel.Information, " Index Out of range");
|
Debug.LogMessage(LogEventLevel.Information, " Index Out of range");
|
||||||
@@ -287,7 +359,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
//if (obj == null)
|
//if (obj == null)
|
||||||
// return "{\"error\":\"No object found\"}";
|
// return "{\"error\":\"No object found\"}";
|
||||||
|
|
||||||
//CType t = obj.GetType();
|
//Type 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
|
||||||
@@ -307,13 +379,15 @@ namespace PepperDash.Essentials.Core
|
|||||||
|
|
||||||
public class PropertyNameType
|
public class PropertyNameType
|
||||||
{
|
{
|
||||||
object Parent;
|
private object Parent;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public PropertyInfo PropInfo { get; private set; }
|
public PropertyInfo PropInfo { get; private set; }
|
||||||
public string Name { get { return PropInfo.Name; } }
|
public string Name { get { return PropInfo.Name; } }
|
||||||
public string Type { get { return PropInfo.PropertyType.Name; } }
|
public string Type { get { return PropInfo.PropertyType.Name; } }
|
||||||
public string Value { get
|
public string Value
|
||||||
|
{
|
||||||
|
get
|
||||||
{
|
{
|
||||||
if (PropInfo.CanRead)
|
if (PropInfo.CanRead)
|
||||||
{
|
{
|
||||||
@@ -328,7 +402,8 @@ namespace PepperDash.Essentials.Core
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
return null;
|
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; } }
|
||||||
@@ -347,10 +422,14 @@ namespace PepperDash.Essentials.Core
|
|||||||
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 { get {
|
public IEnumerable<NameType> Params
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
return MethodInfo.GetParameters().Select(p =>
|
return MethodInfo.GetParameters().Select(p =>
|
||||||
new NameType { Name = p.Name, Type = p.ParameterType.Name });
|
new NameType { Name = p.Name, Type = p.ParameterType.Name });
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public MethodNameParams(MethodInfo info)
|
public MethodNameParams(MethodInfo info)
|
||||||
{
|
{
|
||||||
@@ -365,7 +444,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.All)]
|
[AttributeUsage(AttributeTargets.All)]
|
||||||
public class ApiAttribute : CAttribute
|
public class ApiAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
using System;
|
using Crestron.SimplSharp;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
using Crestron.SimplSharpPro;
|
using Crestron.SimplSharpPro;
|
||||||
|
|
||||||
using PepperDash.Core;
|
using PepperDash.Core;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core
|
namespace PepperDash.Essentials.Core
|
||||||
@@ -16,13 +14,15 @@ namespace PepperDash.Essentials.Core
|
|||||||
{
|
{
|
||||||
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; } }
|
//public static List<Device> Devices { get { return _Devices; } }
|
||||||
//static List<Device> _Devices = new List<Device>();
|
//static List<Device> _Devices = new List<Device>();
|
||||||
|
|
||||||
static readonly Dictionary<string, IKeyed> Devices = new Dictionary<string, IKeyed>(StringComparer.OrdinalIgnoreCase);
|
private static readonly Dictionary<string, IKeyed> Devices = new Dictionary<string, IKeyed>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a copy of all the devices in a list
|
/// Returns a copy of all the devices in a list
|
||||||
@@ -64,7 +64,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
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
|
||||||
@@ -124,6 +124,18 @@ namespace PepperDash.Essentials.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
{
|
{
|
||||||
var handler = AllDevicesActivated;
|
var handler = AllDevicesActivated;
|
||||||
@@ -142,6 +154,15 @@ namespace PepperDash.Essentials.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void OnAllDevicesInitialized()
|
||||||
|
{
|
||||||
|
var handler = AllDevicesInitialized;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(null, new EventArgs());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calls activate on all Device class items
|
/// Calls activate on all Device class items
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -166,7 +187,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
// var dev = GetDeviceForKey(devKey);
|
// var dev = GetDeviceForKey(devKey);
|
||||||
// if(dev != null)
|
// if(dev != null)
|
||||||
// {
|
// {
|
||||||
// var type = dev.GetType().GetCType();
|
// var type = dev.GetType().GetType();
|
||||||
// var methods = type.GetMethods(BindingFlags.Public|BindingFlags.Instance);
|
// var methods = type.GetMethods(BindingFlags.Public|BindingFlags.Instance);
|
||||||
// var sb = new StringBuilder();
|
// var sb = new StringBuilder();
|
||||||
// sb.AppendLine(string.Format("{2} methods on [{0}] ({1}):", dev.Key, type.Name, methods.Length));
|
// sb.AppendLine(string.Format("{2} methods on [{0}] ({1}):", dev.Key, type.Name, methods.Length));
|
||||||
@@ -203,8 +224,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
Debug.LogMessage(LogEventLevel.Information, "Device '{0}' not found", devKey);
|
Debug.LogMessage(LogEventLevel.Information, "Device '{0}' not found", devKey);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var statusDev = dev as IHasFeedback;
|
if (!(dev is IHasFeedback statusDev))
|
||||||
if (statusDev == null)
|
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Device '{0}' does not have visible feedbacks", devKey);
|
Debug.LogMessage(LogEventLevel.Information, "Device '{0}' does not have visible feedbacks", devKey);
|
||||||
return;
|
return;
|
||||||
@@ -225,13 +245,11 @@ namespace PepperDash.Essentials.Core
|
|||||||
|
|
||||||
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>())
|
||||||
{
|
{
|
||||||
sb.Append(string.Format("{0}: {1}\r", dev,
|
CrestronConsole.ConsoleCommandResponse($"{dev}: {dev.CommunicationMonitor.Status}{Environment.NewLine}");
|
||||||
dev.CommunicationMonitor.Status));
|
|
||||||
}
|
}
|
||||||
CrestronConsole.ConsoleCommandResponse(sb.ToString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -268,6 +286,9 @@ namespace PepperDash.Essentials.Core
|
|||||||
Devices.Add(newDev.Key, newDev);
|
Devices.Add(newDev.Key, newDev);
|
||||||
//if (!(_Devices.Contains(newDev)))
|
//if (!(_Devices.Contains(newDev)))
|
||||||
// _Devices.Add(newDev);
|
// _Devices.Add(newDev);
|
||||||
|
|
||||||
|
if (newDev is EssentialsDevice essentialsDev)
|
||||||
|
essentialsDev.Initialized += DeviceManager_Initialized;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -367,8 +388,7 @@ 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);
|
||||||
|
|
||||||
var com = GetDeviceForKey(match.Groups[1].Value) as ComPortController;
|
if (!(GetDeviceForKey(match.Groups[1].Value) is ComPortController com))
|
||||||
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;
|
||||||
@@ -423,16 +443,15 @@ 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 (device == null)
|
if (!(GetDeviceForKey(deviceKey) is IStreamDebugging device))
|
||||||
{
|
{
|
||||||
CrestronConsole.ConsoleCommandResponse("Unable to get device with key: {0}", deviceKey);
|
CrestronConsole.ConsoleCommandResponse("Unable to get device with key: {0}", deviceKey);
|
||||||
return;
|
return;
|
||||||
@@ -479,9 +498,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
{
|
{
|
||||||
foreach (var device in AllDevices)
|
foreach (var device in AllDevices)
|
||||||
{
|
{
|
||||||
var streamDevice = device as IStreamDebugging;
|
if (device is IStreamDebugging streamDevice)
|
||||||
|
|
||||||
if (streamDevice != null)
|
|
||||||
{
|
{
|
||||||
streamDevice.StreamDebugging.SetDebuggingWithDefaultTimeout(eStreamDebuggingSetting.Off);
|
streamDevice.StreamDebugging.SetDebuggingWithDefaultTimeout(eStreamDebuggingSetting.Off);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 Crestron.SimplSharp.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
using PepperDash.Core;
|
using PepperDash.Core;
|
||||||
using PepperDash.Essentials.Core.Config;
|
using PepperDash.Essentials.Core.Config;
|
||||||
@@ -17,6 +17,24 @@ 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)
|
||||||
{
|
{
|
||||||
@@ -41,6 +59,8 @@ namespace PepperDash.Essentials.Core
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Initialize();
|
Initialize();
|
||||||
|
|
||||||
|
IsInitialized = true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
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; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
12
src/PepperDash.Essentials.Core/Devices/IDspPresets.cs
Normal file
12
src/PepperDash.Essentials.Core/Devices/IDspPresets.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using PepperDash.Core;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
public interface IDspPresets
|
||||||
|
{
|
||||||
|
Dictionary<string, IKeyName> Presets { get; }
|
||||||
|
|
||||||
|
void RecallPreset(string key);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
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;
|
||||||
@@ -23,7 +21,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
{
|
{
|
||||||
public static void DumpFeedbacksToConsole(this IHasFeedback source, bool getCurrentStates)
|
public static void DumpFeedbacksToConsole(this IHasFeedback source, bool getCurrentStates)
|
||||||
{
|
{
|
||||||
CType t = source.GetType();
|
Type 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));
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@ 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;
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ 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;
|
||||||
|
|
||||||
@@ -70,7 +69,7 @@ namespace PepperDash.Essentials.Core.Devices
|
|||||||
{
|
{
|
||||||
public LaptopFactory()
|
public LaptopFactory()
|
||||||
{
|
{
|
||||||
TypeNames = new List<string>() { "laptop" };
|
TypeNames = new List<string>() { "deprecated" };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override EssentialsDevice BuildDevice(DeviceConfig dc)
|
public override EssentialsDevice BuildDevice(DeviceConfig dc)
|
||||||
|
|||||||
45
src/PepperDash.Essentials.Core/Devices/PresetListItem.cs
Normal file
45
src/PepperDash.Essentials.Core/Devices/PresetListItem.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,7 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
using Crestron.SimplSharp.CrestronIO;
|
|
||||||
using Crestron.SimplSharpPro;
|
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using PepperDash.Core;
|
using PepperDash.Core;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core
|
namespace PepperDash.Essentials.Core
|
||||||
{
|
{
|
||||||
@@ -42,7 +34,8 @@ namespace PepperDash.Essentials.Core
|
|||||||
return _SourceDevice;
|
return _SourceDevice;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Device _SourceDevice;
|
|
||||||
|
private Device _SourceDevice;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets either the source's Name or this AlternateName property, if
|
/// Gets either the source's Name or this AlternateName property, if
|
||||||
@@ -144,12 +137,27 @@ namespace PepperDash.Essentials.Core
|
|||||||
[JsonProperty("isAudioSource")]
|
[JsonProperty("isAudioSource")]
|
||||||
public bool IsAudioSource { get; set; }
|
public bool IsAudioSource { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hide source on UI when Avanced Sharing is enabled
|
||||||
|
/// </summary>
|
||||||
|
[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 SourceListItem()
|
||||||
{
|
{
|
||||||
Icon = "Blank";
|
Icon = "Blank";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{SourceKey}:{Name}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SourceRouteListItem
|
public class SourceRouteListItem
|
||||||
@@ -157,9 +165,15 @@ namespace PepperDash.Essentials.Core
|
|||||||
[JsonProperty("sourceKey")]
|
[JsonProperty("sourceKey")]
|
||||||
public string SourceKey { get; set; }
|
public string SourceKey { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("sourcePortKey")]
|
||||||
|
public string SourcePortKey { get; set; }
|
||||||
|
|
||||||
[JsonProperty("destinationKey")]
|
[JsonProperty("destinationKey")]
|
||||||
public string DestinationKey { get; set; }
|
public string DestinationKey { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("destinationPortKey")]
|
||||||
|
public string DestinationPortKey { get; set; }
|
||||||
|
|
||||||
[JsonProperty("type")]
|
[JsonProperty("type")]
|
||||||
public eRoutingSignalType Type { get; set; }
|
public eRoutingSignalType Type { get; set; }
|
||||||
}
|
}
|
||||||
@@ -172,7 +186,12 @@ namespace PepperDash.Essentials.Core
|
|||||||
defaultDisplay,
|
defaultDisplay,
|
||||||
leftDisplay,
|
leftDisplay,
|
||||||
rightDisplay,
|
rightDisplay,
|
||||||
|
centerDisplay,
|
||||||
programAudio,
|
programAudio,
|
||||||
codecContent
|
codecContent,
|
||||||
|
frontLeftDisplay,
|
||||||
|
frontRightDisplay,
|
||||||
|
rearLeftDisplay,
|
||||||
|
rearRightDisplay,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,6 @@ 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
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
|
|
||||||
|
|
||||||
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
|
||||||
@@ -20,6 +17,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
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
|
||||||
@@ -96,6 +94,8 @@ 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,
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -1,238 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Crestron.SimplSharp.Reflection;
|
using System.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 CType CType { get; set; }
|
public Type Type { 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()
|
||||||
{
|
{
|
||||||
CType = null;
|
Type = null;
|
||||||
Description = "Not Available";
|
Description = "Not Available";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var factory = (IDeviceFactory)Crestron.SimplSharp.Reflection.Activator.CreateInstance(type);
|
var factory = (IDeviceFactory)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, CType cType, Func<DeviceConfig, IKeyed> method)
|
public static void AddFactoryForType(string typeName, string description, Type Type, 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() { CType = cType, Description = description, FactoryMethod = method };
|
var wrapper = new DeviceFactoryWrapper() { Type = Type, Description = description, FactoryMethod = method };
|
||||||
DeviceFactory.FactoryMethods.Add(typeName, wrapper);
|
DeviceFactory.FactoryMethods.Add(typeName, wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,18 +149,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Error, "Exception occurred while creating device {0}: {1}", dc.Key, ex.Message);
|
Debug.LogMessage(ex, "Exception occurred while creating device {0}: {1}", null, 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,17 +169,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 cType = "Not Specified by Plugin";
|
var Type = "Not Specified by Plugin";
|
||||||
|
|
||||||
if (type.Value.CType != null)
|
if (type.Value.Type != null)
|
||||||
{
|
{
|
||||||
cType = type.Value.CType.FullName;
|
Type = type.Value.Type.FullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
CrestronConsole.ConsoleCommandResponse(
|
CrestronConsole.ConsoleCommandResponse(
|
||||||
@"Type: '{0}'
|
@"Type: '{0}'
|
||||||
CType: '{1}'
|
Type: '{1}'
|
||||||
Description: {2}{3}", type.Key, cType, description, CrestronEnvironment.NewLine);
|
Description: {2}{3}", type.Key, Type, description, CrestronEnvironment.NewLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,4 @@
|
|||||||
using System;
|
namespace PepperDash.Essentials.Core
|
||||||
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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
using Crestron.SimplSharp.Reflection;
|
using System.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)Crestron.SimplSharp.Reflection.Activator.CreateInstance(extension);
|
var factory = (IProcessorExtensionDeviceFactory)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, CType cType, Func<DeviceConfig, IKeyed> method)
|
public static void AddFactoryForType(string extensionName, string description, Type Type, 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() { CType = cType, Description = description, FactoryMethod = method };
|
var wrapper = new DeviceFactoryWrapper() { Type = Type, Description = description, FactoryMethod = method };
|
||||||
ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, wrapper);
|
ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
|
|
||||||
|
|
||||||
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;
|
||||||
@@ -14,6 +10,10 @@ 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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ 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;
|
||||||
|
|||||||
@@ -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 Crestron.SimplSharp.Reflection;
|
using System.Reflection;
|
||||||
using Crestron.SimplSharp.CrestronIO;
|
using Crestron.SimplSharp.CrestronIO;
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
|
|
||||||
@@ -109,17 +109,18 @@ namespace PepperDash.Essentials.Core
|
|||||||
protected void AddJoins(Type type)
|
protected void AddJoins(Type type)
|
||||||
{
|
{
|
||||||
var fields =
|
var fields =
|
||||||
type.GetCType()
|
type.GetFields(BindingFlags.Public | BindingFlags.Instance)
|
||||||
.GetFields(BindingFlags.Public | BindingFlags.Instance)
|
.Where(f => f.IsDefined(typeof (JoinNameAttribute), true)).ToList();
|
||||||
.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);
|
||||||
|
|
||||||
var value = field.GetValue(childClass) as JoinDataComplete; //this here is JoinMapBaseAdvanced, not the child class. JoinMapBaseAdvanced has no fields.
|
//this here is JoinMapBaseAdvanced, not the child class. JoinMapBaseAdvanced has no fields.
|
||||||
|
|
||||||
if (value == null)
|
if (!(field.GetValue(childClass) is JoinDataComplete value))
|
||||||
{
|
{
|
||||||
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;
|
||||||
@@ -129,7 +130,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);
|
||||||
}
|
}
|
||||||
@@ -155,29 +156,37 @@ namespace PepperDash.Essentials.Core
|
|||||||
{
|
{
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
// Get the joins of each type and print them
|
var lineEnding = "\r\n";
|
||||||
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 =
|
|
||||||
Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Digital) == eJoinType.Digital)
|
|
||||||
.ToDictionary(j => j.Key, j => j.Value);
|
|
||||||
var digitalSb = AppendJoinList(GetSortedJoins(digitals));
|
|
||||||
digitalSb.AppendLine("## Analogs");
|
|
||||||
digitalSb.AppendLine();
|
|
||||||
|
|
||||||
var analogs =
|
var digitals =
|
||||||
Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Analog) == eJoinType.Analog)
|
Joins.Where(j => j.Value.Metadata.JoinType.HasFlag(eJoinType.Digital))
|
||||||
|
.ToDictionary(j => j.Key, j => j.Value);
|
||||||
|
|
||||||
|
var analogs = Joins.Where(j => j.Value.Metadata.JoinType.HasFlag(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 & eJoinType.Serial) == eJoinType.Serial)
|
Joins.Where(j => j.Value.Metadata.JoinType.HasFlag(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);
|
||||||
@@ -202,7 +211,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))
|
||||||
{
|
{
|
||||||
@@ -230,7 +239,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} |";
|
const string stringFormatter = "| {0} | {1} | {2} | {3} | {4} |\r\n";
|
||||||
const int joinNumberLen = 11;
|
const int joinNumberLen = 11;
|
||||||
const int joinSpanLen = 9;
|
const int joinSpanLen = 9;
|
||||||
const int typeLen = 19;
|
const int typeLen = 19;
|
||||||
@@ -238,25 +247,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.AppendLine(String.Format(stringFormatter,
|
sb.Append(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.AppendLine(String.Format(stringFormatter,
|
sb.Append(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.AppendLine(join.Value.GetMarkdownFormattedData(stringFormatter, descriptionLen));
|
sb.Append(join.Value.GetMarkdownFormattedData(stringFormatter, descriptionLen));
|
||||||
}
|
}
|
||||||
sb.AppendLine();
|
sb.Append("\r\n");
|
||||||
return sb;
|
return sb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,10 +420,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";
|
||||||
@@ -430,13 +439,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"],
|
||||||
@@ -454,8 +463,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"],
|
||||||
@@ -501,7 +510,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)CAttribute.GetCustomAttribute(memberInfo, typeof(JoinNameAttribute));
|
var attribute = (JoinNameAttribute)Attribute.GetCustomAttribute(memberInfo, typeof(JoinNameAttribute));
|
||||||
|
|
||||||
if (attribute == null) return name;
|
if (attribute == null) return name;
|
||||||
|
|
||||||
@@ -514,13 +523,13 @@ namespace PepperDash.Essentials.Core
|
|||||||
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.All)]
|
[AttributeUsage(AttributeTargets.All)]
|
||||||
public class JoinNameAttribute : CAttribute
|
public class JoinNameAttribute : Attribute
|
||||||
{
|
{
|
||||||
private string _Name;
|
private string _Name;
|
||||||
|
|
||||||
public JoinNameAttribute(string name)
|
public JoinNameAttribute(string name)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, "Setting Attribute Name: {0}", name);
|
Debug.LogMessage(LogEventLevel.Verbose, "Setting Attribute Name: {0}",null, name);
|
||||||
_Name = name;
|
_Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ namespace PepperDash.Essentials.Core.Monitoring
|
|||||||
_uptimePollTimer = null;
|
_uptimePollTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PollUptime(object obj)
|
public void PollUptime(object obj)
|
||||||
{
|
{
|
||||||
var consoleResponse = string.Empty;
|
var consoleResponse = string.Empty;
|
||||||
|
|
||||||
@@ -142,17 +142,20 @@ namespace PepperDash.Essentials.Core.Monitoring
|
|||||||
_uptime = uptimeRaw.Substring(forIndex + 4);
|
_uptime = uptimeRaw.Substring(forIndex + 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ProcessorReboot()
|
public 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ProgramReset(uint index)
|
public 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;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
using System;
|
using PepperDash.Core;
|
||||||
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
|
||||||
{
|
{
|
||||||
@@ -17,9 +14,36 @@ namespace PepperDash.Essentials.Core
|
|||||||
{
|
{
|
||||||
private IPartitionStateProvider _partitionSensor;
|
private IPartitionStateProvider _partitionSensor;
|
||||||
|
|
||||||
private bool isInAutoMode;
|
public bool IsInAutoMode { get; private set; }
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
@@ -50,11 +74,11 @@ namespace PepperDash.Essentials.Core
|
|||||||
PartitionPresentFeedback.FireUpdate();
|
PartitionPresentFeedback.FireUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
|
private void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
|
||||||
{
|
{
|
||||||
if (isInAutoMode)
|
if (IsInAutoMode)
|
||||||
{
|
{
|
||||||
PartitionPresentFeedback.FireUpdate();
|
PartitionPresent = e.BoolValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +88,9 @@ namespace PepperDash.Essentials.Core
|
|||||||
|
|
||||||
public void SetAutoMode()
|
public void SetAutoMode()
|
||||||
{
|
{
|
||||||
isInAutoMode = true;
|
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Setting {Key} to Auto Mode", this);
|
||||||
|
|
||||||
|
IsInAutoMode = true;
|
||||||
if (PartitionPresentFeedback != null)
|
if (PartitionPresentFeedback != null)
|
||||||
{
|
{
|
||||||
PartitionPresentFeedback.SetValueFunc(() => _partitionSensor.PartitionPresentFeedback.BoolValue);
|
PartitionPresentFeedback.SetValueFunc(() => _partitionSensor.PartitionPresentFeedback.BoolValue);
|
||||||
@@ -76,52 +102,64 @@ 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()
|
||||||
{
|
{
|
||||||
isInAutoMode = false;
|
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Setting {Key} to Manual Mode", this);
|
||||||
|
|
||||||
|
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()
|
||||||
{
|
{
|
||||||
if (!isInAutoMode)
|
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Toggling Partition State for {Key}", this);
|
||||||
|
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"IsInAutoMode: {IsInAutoMode}", this);
|
||||||
|
|
||||||
|
if (!IsInAutoMode)
|
||||||
{
|
{
|
||||||
partitionPresent = !partitionPresent;
|
PartitionPresent = !PartitionPresent;
|
||||||
PartitionPresentFeedback.FireUpdate();
|
PartitionPresentFeedback.FireUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
using Newtonsoft.Json;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
|
|
||||||
using PepperDash.Core;
|
using PepperDash.Core;
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core
|
namespace PepperDash.Essentials.Core
|
||||||
@@ -13,7 +9,11 @@ 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,8 +21,12 @@ 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();
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
<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>
|
||||||
@@ -23,7 +26,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.20.66" />
|
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.20.66" />
|
||||||
<PackageReference Include="PepperDashCore" Version="2.0.0-beta-415" />
|
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-451" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="Crestron\CrestronGenericBaseDevice.cs.orig" />
|
<None Include="Crestron\CrestronGenericBaseDevice.cs.orig" />
|
||||||
|
|||||||
@@ -1,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 Crestron.SimplSharp.Reflection;
|
using System.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,24 +27,31 @@ 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>
|
||||||
@@ -59,7 +66,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)
|
foreach (var fi in assemblyFiles.Where(fi => fi.Name.Contains("Essentials") || fi.Name.Contains("PepperDash")))
|
||||||
{
|
{
|
||||||
string version = string.Empty;
|
string version = string.Empty;
|
||||||
Assembly assembly = null;
|
Assembly assembly = null;
|
||||||
@@ -69,6 +76,7 @@ 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"):
|
||||||
@@ -81,9 +89,12 @@ namespace PepperDash.Essentials
|
|||||||
version = Global.AssemblyVersion;
|
version = Global.AssemblyVersion;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ("PepperDash_Core.dll"):
|
case ("PepperDashCore.dll"):
|
||||||
{
|
{
|
||||||
version = PepperDash.Core.Debug.PepperDashCoreVersion;
|
Debug.LogMessage(LogEventLevel.Verbose, "Found PepperDash_Core.dll");
|
||||||
|
version = Debug.PepperDashCoreVersion;
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "PepperDash_Core Version: {0}", version);
|
||||||
|
PepperDashCoreAssembly = new LoadedAssembly(fi.Name, version, assembly);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,6 +129,8 @@ namespace PepperDash.Essentials
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <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);
|
//Debug.LogMessage(LogEventLevel.Verbose, "Attempting to load {0}", filePath);
|
||||||
var assembly = Assembly.LoadFrom(filePath);
|
var assembly = Assembly.LoadFrom(filePath);
|
||||||
@@ -136,6 +149,11 @@ namespace PepperDash.Essentials
|
|||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
} catch(Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogMessage(ex, "Error loading assembly from {path}", null, filePath);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +162,7 @@ namespace PepperDash.Essentials
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="assembly"></param>
|
/// <param name="assembly"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
static string GetAssemblyVersion(Assembly assembly)
|
public 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)
|
||||||
@@ -190,12 +208,19 @@ 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("Loaded Assemblies:" + CrestronEnvironment.NewLine);
|
CrestronConsole.ConsoleCommandResponse("PepperDash Core Version: {0}" + CrestronEnvironment.NewLine, PepperDashCoreAssembly.Version);
|
||||||
foreach (var assembly in LoadedAssemblies)
|
CrestronConsole.ConsoleCommandResponse("Essentials Plugin Versions:" + CrestronEnvironment.NewLine);
|
||||||
|
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
|
||||||
@@ -354,7 +379,7 @@ namespace PepperDash.Essentials
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var assy = loadedAssembly.Assembly;
|
var assy = loadedAssembly.Assembly;
|
||||||
CType[] types = {};
|
Type[] types = {};
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
types = assy.GetTypes();
|
types = assy.GetTypes();
|
||||||
@@ -375,7 +400,7 @@ namespace PepperDash.Essentials
|
|||||||
if (typeof (IPluginDeviceFactory).IsAssignableFrom(type) && !type.IsAbstract)
|
if (typeof (IPluginDeviceFactory).IsAssignableFrom(type) && !type.IsAbstract)
|
||||||
{
|
{
|
||||||
var plugin =
|
var plugin =
|
||||||
(IPluginDeviceFactory) Crestron.SimplSharp.Reflection.Activator.CreateInstance(type);
|
(IPluginDeviceFactory)Activator.CreateInstance(type);
|
||||||
LoadCustomPlugin(plugin, loadedAssembly);
|
LoadCustomPlugin(plugin, loadedAssembly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -432,6 +457,9 @@ 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>
|
||||||
@@ -439,7 +467,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(CType type, MethodInfo loadPlugin, LoadedAssembly loadedAssembly)
|
static void LoadCustomLegacyPlugin(Type 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);
|
||||||
|
|
||||||
@@ -486,6 +514,8 @@ 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");
|
||||||
@@ -514,8 +544,11 @@ 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)
|
||||||
|
|||||||
@@ -130,6 +130,8 @@ 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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using System;
|
using Crestron.SimplSharp;
|
||||||
|
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.Text;
|
using System.Threading;
|
||||||
using Crestron.SimplSharp;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using PepperDash.Core;
|
|
||||||
using Serilog.Events;
|
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core
|
namespace PepperDash.Essentials.Core
|
||||||
{
|
{
|
||||||
@@ -17,12 +18,40 @@ namespace PepperDash.Essentials.Core
|
|||||||
|
|
||||||
private List<IEssentialsRoom> _rooms;
|
private List<IEssentialsRoom> _rooms;
|
||||||
|
|
||||||
private bool isInAutoMode;
|
public List<IKeyName> Rooms
|
||||||
|
{
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
@@ -36,14 +65,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();
|
||||||
@@ -55,8 +84,16 @@ 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();
|
||||||
}
|
}
|
||||||
@@ -64,10 +101,10 @@ namespace PepperDash.Essentials.Core
|
|||||||
{
|
{
|
||||||
SetRoomCombinationScenario(_propertiesConfig.defaultScenarioKey);
|
SetRoomCombinationScenario(_propertiesConfig.defaultScenarioKey);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateScenarios()
|
private void CreateScenarios()
|
||||||
{
|
{
|
||||||
foreach (var scenarioConfig in _propertiesConfig.Scenarios)
|
foreach (var scenarioConfig in _propertiesConfig.Scenarios)
|
||||||
{
|
{
|
||||||
@@ -76,21 +113,29 @@ namespace PepperDash.Essentials.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetRooms()
|
private 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) as IEssentialsRoom;
|
var room = DeviceManager.GetDeviceForKey(roomKey);
|
||||||
if (room != null)
|
|
||||||
|
if (DeviceManager.GetDeviceForKey(roomKey) is IEssentialsRoom essentialsRoom)
|
||||||
{
|
{
|
||||||
_rooms.Add(room);
|
_rooms.Add(essentialsRoom);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupPartitionStateProviders()
|
var rooms = DeviceManager.AllDevices.OfType<IEssentialsRoom>().Cast<Device>();
|
||||||
|
|
||||||
|
foreach (var room in rooms)
|
||||||
|
{
|
||||||
|
room.Deactivate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupPartitionStateProviders()
|
||||||
{
|
{
|
||||||
foreach (var pConfig in _propertiesConfig.Partitions)
|
foreach (var pConfig in _propertiesConfig.Partitions)
|
||||||
{
|
{
|
||||||
@@ -104,14 +149,18 @@ namespace PepperDash.Essentials.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
|
private void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
|
||||||
{
|
{
|
||||||
StartDebounceTimer();
|
StartDebounceTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StartDebounceTimer()
|
private void StartDebounceTimer()
|
||||||
{
|
{
|
||||||
var time = _scenarioChangeDebounceTimeSeconds * 1000;
|
// default to 500ms for manual mode
|
||||||
|
var time = 500;
|
||||||
|
|
||||||
|
// if in auto mode, debounce the scenario change
|
||||||
|
if (IsInAutoMode) time = _scenarioChangeDebounceTimeSeconds * 1000;
|
||||||
|
|
||||||
if (_scenarioChangeDebounceTimer == null)
|
if (_scenarioChangeDebounceTimer == null)
|
||||||
{
|
{
|
||||||
@@ -126,7 +175,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>
|
||||||
void DetermineRoomCombinationScenario()
|
private void DetermineRoomCombinationScenario()
|
||||||
{
|
{
|
||||||
if (_scenarioChangeDebounceTimer != null)
|
if (_scenarioChangeDebounceTimer != null)
|
||||||
{
|
{
|
||||||
@@ -134,14 +183,20 @@ 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
|
||||||
@@ -154,10 +209,41 @@ namespace PepperDash.Essentials.Core
|
|||||||
|
|
||||||
if (currentScenario != null)
|
if (currentScenario != null)
|
||||||
{
|
{
|
||||||
CurrentScenario = currentScenario;
|
this.LogInformation("Found combination Scenario {scenarioKey}", currentScenario.Key);
|
||||||
|
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;
|
||||||
@@ -168,53 +254,42 @@ 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()
|
||||||
{
|
{
|
||||||
isInAutoMode = !isInAutoMode;
|
if (IsInAutoMode)
|
||||||
IsInAutoModeFeedback.FireUpdate();
|
{
|
||||||
|
SetManualMode();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetAutoMode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<IRoomCombinationScenario> RoomCombinationScenarios { get; private set; }
|
public List<IRoomCombinationScenario> RoomCombinationScenarios { get; private set; }
|
||||||
@@ -223,7 +298,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
|
|
||||||
public void TogglePartitionState(string partitionKey)
|
public void TogglePartitionState(string partitionKey)
|
||||||
{
|
{
|
||||||
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey)) as IPartitionController;
|
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey));
|
||||||
|
|
||||||
if (partition != null)
|
if (partition != null)
|
||||||
{
|
{
|
||||||
@@ -233,7 +308,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;
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Threading.Tasks;
|
||||||
using System.Text;
|
using Newtonsoft.Json;
|
||||||
using Crestron.SimplSharp;
|
|
||||||
|
|
||||||
using PepperDash.Core;
|
using PepperDash.Core;
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core
|
namespace PepperDash.Essentials.Core
|
||||||
@@ -21,13 +19,21 @@ 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>
|
||||||
@@ -46,11 +52,13 @@ 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>
|
||||||
@@ -71,26 +79,32 @@ 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>
|
||||||
void Activate();
|
Task Activate();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deactivates this room combination scenario
|
/// Deactivates this room combination scenario
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Deactivate();
|
Task 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; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,50 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
|
|
||||||
using PepperDash.Core;
|
using PepperDash.Core;
|
||||||
|
using PepperDash.Core.Logging;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
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
|
public class RoomCombinationScenario : IRoomCombinationScenario, IKeyName
|
||||||
{
|
{
|
||||||
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;
|
||||||
@@ -55,36 +70,40 @@ namespace PepperDash.Essentials.Core
|
|||||||
IsActiveFeedback = new BoolFeedback(() => _isActive);
|
IsActiveFeedback = new BoolFeedback(() => _isActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Activate()
|
public async Task Activate()
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Debug, "Activating Scenario: '{0}' with {1} action(s) defined", Name, activationActions.Count);
|
this.LogInformation("Activating Scenario {name} with {activationActionCount} 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)
|
||||||
{
|
{
|
||||||
DeviceJsonApi.DoDeviceAction(action);
|
this.LogInformation("Running Activation action {@action}", action);
|
||||||
|
await DeviceJsonApi.DoDeviceActionAsync(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_isActive = true;
|
IsActive = true;
|
||||||
IsActiveFeedback.FireUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Deactivate()
|
public async Task Deactivate()
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Debug, "Deactivating Scenario: '{0}' with {1} action(s) defined", Name, deactivationActions.Count);
|
this.LogInformation("Deactivating Scenario {name} with {deactivationActionCount} 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)
|
||||||
{
|
{
|
||||||
DeviceJsonApi.DoDeviceAction(action);
|
this.LogInformation("Running deactivation action {actionDeviceKey}:{actionMethod}", action.DeviceKey, action.MethodName);
|
||||||
|
await DeviceJsonApi.DoDeviceActionAsync(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_isActive = false;
|
IsActive = false;
|
||||||
IsActiveFeedback.FireUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -198,6 +199,12 @@ 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>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
public class EssentialsRoomEmergencyTriggerConfig
|
public class EssentialsRoomEmergencyTriggerConfig
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// contact,
|
/// contact,versiport
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -4,12 +4,16 @@ using PepperDash.Essentials.Room.Config;
|
|||||||
|
|
||||||
namespace PepperDash.Essentials.Core
|
namespace PepperDash.Essentials.Core
|
||||||
{
|
{
|
||||||
public class EssentialsRoomEmergencyContactClosure : EssentialsRoomEmergencyBase
|
public class EssentialsRoomEmergencyContactClosure : EssentialsRoomEmergencyBase, IEssentialsRoomEmergency
|
||||||
{
|
{
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
@@ -25,15 +29,50 @@ 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)
|
||||||
{
|
{
|
||||||
if (args.State && TriggerOnClose || !args.State && !TriggerOnClose)
|
ContactClosure_StateChange(args.State);
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
///
|
///
|
||||||
@@ -44,4 +83,14 @@ 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; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -59,28 +59,102 @@ 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>
|
||||||
///
|
///
|
||||||
protected string _SourceListKey;
|
private string _sourceListKey;
|
||||||
public string SourceListKey {
|
public string SourceListKey {
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return _SourceListKey;
|
if(string.IsNullOrEmpty(_sourceListKey))
|
||||||
|
{
|
||||||
|
return _defaultListKey;
|
||||||
}
|
}
|
||||||
private set
|
else
|
||||||
{
|
{
|
||||||
if (value != _SourceListKey)
|
return _sourceListKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected set
|
||||||
{
|
{
|
||||||
_SourceListKey = value;
|
if (value != _sourceListKey)
|
||||||
|
{
|
||||||
|
_sourceListKey = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string DestinationListKey { get; private set; }
|
private string _destinationListKey;
|
||||||
|
public string DestinationListKey
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_destinationListKey))
|
||||||
|
{
|
||||||
|
return _defaultListKey;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return _destinationListKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
if (value != _destinationListKey)
|
||||||
|
{
|
||||||
|
_destinationListKey = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected const string _defaultSourceListKey = "default";
|
private string _audioControlPointListKey;
|
||||||
|
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
|
||||||
@@ -141,6 +215,7 @@ 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;
|
||||||
@@ -191,7 +266,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sourceListKey = _defaultSourceListKey;
|
sourceListKey = _defaultListKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ 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; }
|
||||||
|
|||||||
@@ -159,4 +159,13 @@ 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; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ using PepperDash.Core;
|
|||||||
|
|
||||||
namespace PepperDash.Essentials.Core.Routing
|
namespace PepperDash.Essentials.Core.Routing
|
||||||
{
|
{
|
||||||
public class DummyRoutingInputsDevice : Device, IRoutingSource
|
public class DummyRoutingInputsDevice : Device, IRoutingSource, IRoutingOutputs
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A single output port, backplane, audioVideo
|
/// A single output port, backplane, audioVideo
|
||||||
|
|||||||
373
src/PepperDash.Essentials.Core/Routing/Extensions.cs
Normal file
373
src/PepperDash.Essentials.Core/Routing/Extensions.cs
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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; }
|
||||||
|
|
||||||
|
|||||||
10
src/PepperDash.Essentials.Core/Routing/IRmcRouting.cs
Normal file
10
src/PepperDash.Essentials.Core/Routing/IRmcRouting.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an IRmcRouting with a feedback event
|
||||||
|
/// </summary>
|
||||||
|
public interface IRmcRoutingWithFeedback : IRmcRouting
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/PepperDash.Essentials.Core/Routing/IRouting.cs
Normal file
21
src/PepperDash.Essentials.Core/Routing/IRouting.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
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);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
16
src/PepperDash.Essentials.Core/Routing/IRoutingFeedback.cs
Normal file
16
src/PepperDash.Essentials.Core/Routing/IRoutingFeedback.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/PepperDash.Essentials.Core/Routing/IRoutingInputs.cs
Normal file
18
src/PepperDash.Essentials.Core/Routing/IRoutingInputs.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
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; }
|
||||||
|
}*/
|
||||||
|
}
|
||||||
@@ -1,426 +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;
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
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>
|
||||||
|
{
|
||||||
|
}*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
public interface IRoutingNumeric : IRouting
|
||||||
|
{
|
||||||
|
void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an IRoutingNumeric with a feedback event
|
||||||
|
/// </summary>
|
||||||
|
public interface IRoutingNumericWithFeedback : IRoutingNumeric, IRoutingFeedback
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/PepperDash.Essentials.Core/Routing/IRoutingOutputs.cs
Normal file
19
src/PepperDash.Essentials.Core/Routing/IRoutingOutputs.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
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; }
|
||||||
|
}*/
|
||||||
|
}
|
||||||
23
src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs
Normal file
23
src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
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>();
|
||||||
|
}*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
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;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
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);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
9
src/PepperDash.Essentials.Core/Routing/IRoutingSource.cs
Normal file
9
src/PepperDash.Essentials.Core/Routing/IRoutingSource.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an IRoutingOutputs devices as being a source - the start of the chain
|
||||||
|
/// </summary>
|
||||||
|
public interface IRoutingSource : IRoutingOutputs
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/PepperDash.Essentials.Core/Routing/IRoutingWithClear.cs
Normal file
12
src/PepperDash.Essentials.Core/Routing/IRoutingWithClear.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/PepperDash.Essentials.Core/Routing/ITxRouting.cs
Normal file
8
src/PepperDash.Essentials.Core/Routing/ITxRouting.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
public interface ITxRouting : IRoutingNumeric
|
||||||
|
{
|
||||||
|
IntFeedback VideoSourceNumericFeedback { get; }
|
||||||
|
IntFeedback AudioSourceNumericFeedback { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an IRmcRouting with a feedback event
|
||||||
|
/// </summary>
|
||||||
|
public interface ITxRoutingWithFeedback : ITxRouting
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
170
src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs
Normal file
170
src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
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));
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
47
src/PepperDash.Essentials.Core/Routing/RouteRequest.cs
Normal file
47
src/PepperDash.Essentials.Core/Routing/RouteRequest.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
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"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
using PepperDash.Core;
|
||||||
|
using PepperDash.Essentials.Core.Queues;
|
||||||
|
using System;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core.Routing
|
||||||
|
{
|
||||||
|
public class RouteRequestQueueItem : IQueueMessage
|
||||||
|
{
|
||||||
|
private readonly Action<RouteRequest> action;
|
||||||
|
private readonly RouteRequest routeRequest;
|
||||||
|
|
||||||
|
public RouteRequestQueueItem(Action<RouteRequest> routeAction, RouteRequest request)
|
||||||
|
{
|
||||||
|
action = routeAction;
|
||||||
|
routeRequest = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispatch()
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Information, "Dispatching route request {routeRequest}", null, routeRequest);
|
||||||
|
action(routeRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReleaseRouteQueueItem : IQueueMessage
|
||||||
|
{
|
||||||
|
private readonly Action<IRoutingInputs, string> action;
|
||||||
|
private readonly IRoutingInputs destination;
|
||||||
|
private readonly string inputPortKey;
|
||||||
|
|
||||||
|
public ReleaseRouteQueueItem(Action<IRoutingInputs, string> action, IRoutingInputs destination, string inputPortKey)
|
||||||
|
{
|
||||||
|
this.action = action;
|
||||||
|
this.destination = destination;
|
||||||
|
this.inputPortKey = inputPortKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispatch()
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Information, "Dispatching release route request for {destination}:{inputPortKey}", null, destination?.Key ?? "no destination", string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
|
||||||
|
action(destination, inputPortKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
271
src/PepperDash.Essentials.Core/Routing/RoutingFeedbackManager.cs
Normal file
271
src/PepperDash.Essentials.Core/Routing/RoutingFeedbackManager.cs
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
82
src/PepperDash.Essentials.Core/Routing/RoutingInputPort.cs
Normal file
82
src/PepperDash.Essentials.Core/Routing/RoutingInputPort.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
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));
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,203 +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
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <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; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
src/PepperDash.Essentials.Core/Routing/RoutingOutputPort.cs
Normal file
75
src/PepperDash.Essentials.Core/Routing/RoutingOutputPort.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
using System;
|
using PepperDash.Core;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
using PepperDash.Core;
|
|
||||||
|
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core
|
namespace PepperDash.Essentials.Core
|
||||||
@@ -29,180 +26,23 @@ namespace PepperDash.Essentials.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
/*public abstract class RoutingPort<TSelector>:IKeyed
|
||||||
public enum eRoutingSignalType
|
|
||||||
{
|
{
|
||||||
Audio = 1,
|
public string Key { get; private set; }
|
||||||
Video = 2,
|
public eRoutingSignalType Type { get; private set; }
|
||||||
AudioVideo = Audio | Video,
|
public eRoutingPortConnectionType ConnectionType { get; private set; }
|
||||||
UsbOutput = 8,
|
public readonly TSelector Selector;
|
||||||
UsbInput = 16,
|
public bool IsInternal { get; private set; }
|
||||||
SecondaryAudio = 32
|
public object FeedbackMatchObject { get; set; }
|
||||||
}
|
public object Port { get; set; }
|
||||||
|
|
||||||
public enum eRoutingPortConnectionType
|
public RoutingPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, TSelector selector, bool isInternal)
|
||||||
{
|
{
|
||||||
None, BackplaneOnly, DisplayPort, Dvi, Hdmi, Rgb, Vga, LineAudio, DigitalAudio, Sdi,
|
Key = key;
|
||||||
Composite, Component, DmCat, DmMmFiber, DmSmFiber, Speaker, Streaming, UsbC, HdBaseT
|
Type = type;
|
||||||
}
|
ConnectionType = connType;
|
||||||
|
Selector = selector;
|
||||||
/// <summary>
|
IsInternal = isInternal;
|
||||||
/// 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);
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
@@ -23,4 +23,21 @@ 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core.Routing
|
namespace PepperDash.Essentials.Core
|
||||||
{
|
{
|
||||||
/// <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,5 +239,13 @@ namespace PepperDash.Essentials.Core.Routing
|
|||||||
/// 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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,6 @@
|
|||||||
using System;
|
using Newtonsoft.Json;
|
||||||
|
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
|
||||||
{
|
{
|
||||||
@@ -88,8 +82,8 @@ namespace PepperDash.Essentials.Core
|
|||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return string.Format("Tie line: [{0}]{1} --> [{2}]{3}", SourcePort.ParentDevice.Key, SourcePort.Key,
|
return string.Format("Tie line: {0}:{1} --> {2}:{3} {4}", SourcePort.ParentDevice.Key, SourcePort.Key,
|
||||||
DestinationPort.ParentDevice.Key, DestinationPort.Key);
|
DestinationPort.ParentDevice.Key, DestinationPort.Key, Type.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +100,8 @@ namespace PepperDash.Essentials.Core
|
|||||||
return _Default;
|
return _Default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static TieLineCollection _Default;
|
|
||||||
|
[JsonIgnore]
|
||||||
|
private static TieLineCollection _Default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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}", this);
|
Debug.LogMessage(LogEventLevel.Information, "Build TieLine: {0}",null, 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,68 +48,29 @@ namespace PepperDash.Essentials.Core.Config
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Get the source port
|
//Get the source port
|
||||||
RoutingOutputPort sourceOutputPort = null;
|
var sourceOutputPort = sourceDev.OutputPorts[SourcePort];
|
||||||
//// 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)
|
if (sourceOutputPort == null)
|
||||||
{
|
{
|
||||||
LogError("Source does not contain port");
|
LogError("Source does not contain port");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
//Get the Destination port
|
//Get the Destination port
|
||||||
RoutingInputPort destinationInputPort = null;
|
var destinationInputPort = destDev.InputPorts[DestinationPort];
|
||||||
//// If it's a card-based device, get the card and then the Destination port
|
|
||||||
//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)
|
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.Debug, "WARNING: Cannot create tie line: {0}:\r {1}", msg, this);
|
Debug.LogMessage(LogEventLevel.Error, "WARNING: Cannot create tie line: {message}:\r {tieLineConfig}",null, msg, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
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
Reference in New Issue
Block a user