diff --git a/.github/workflows/EssentialsPlugins-builds-4-series-caller.yml b/.github/workflows/EssentialsPlugins-builds-4-series-caller.yml
index 121e6cf9..d5990e99 100644
--- a/.github/workflows/EssentialsPlugins-builds-4-series-caller.yml
+++ b/.github/workflows/EssentialsPlugins-builds-4-series-caller.yml
@@ -6,13 +6,9 @@ on:
- '**'
jobs:
- runTests:
- uses: PepperDash/workflow-templates/.github/workflows/essentialsplugins-run-tests.yml@main
- secrets: inherit
getVersion:
uses: PepperDash/workflow-templates/.github/workflows/essentialsplugins-getversion.yml@main
secrets: inherit
- needs: runTests
build-4Series:
uses: PepperDash/workflow-templates/.github/workflows/essentialsplugins-4Series-builds.yml@main
secrets: inherit
diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml
index 35752ff7..42dd64c5 100644
--- a/.github/workflows/publish-docs.yml
+++ b/.github/workflows/publish-docs.yml
@@ -26,9 +26,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@v4
- name: Dotnet Setup
- uses: actions/setup-dotnet@v5
+ uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.x
diff --git a/PepperDash.Essentials.4Series.sln b/PepperDash.Essentials.4Series.sln
index a39555e8..7423c50a 100644
--- a/PepperDash.Essentials.4Series.sln
+++ b/PepperDash.Essentials.4Series.sln
@@ -36,171 +36,49 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{02EA681E-C
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Core", "src\PepperDash.Core\PepperDash.Core.csproj", "{E5336563-1194-501E-BC4A-79AD9283EF90}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Core.Tests", "src\PepperDash.Core.Tests\PepperDash.Core.Tests.csproj", "{680BA287-E61F-4B8D-BD7A-84C2504F5F9C}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Essentials.Core.Tests", "src\PepperDash.Essentials.Core.Tests\PepperDash.Essentials.Core.Tests.csproj", "{F508E0BA-E885-424F-9D4C-359CF0011DEF}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Essentials.Tests", "src\PepperDash.Essentials.Tests\PepperDash.Essentials.Tests.csproj", "{841EE676-7784-4456-8F76-3697C6D432A6}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug 4.7.2|Any CPU = Debug 4.7.2|Any CPU
- Debug 4.7.2|x64 = Debug 4.7.2|x64
- Debug 4.7.2|x86 = Debug 4.7.2|x86
Debug|Any CPU = Debug|Any CPU
- Debug|x64 = Debug|x64
- Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
- Release|x64 = Release|x64
- Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{53E204B7-97DD-441D-A96C-721DF014DF82}.Debug 4.7.2|Any CPU.ActiveCfg = Debug 4.7.2|Any CPU
{53E204B7-97DD-441D-A96C-721DF014DF82}.Debug 4.7.2|Any CPU.Build.0 = Debug 4.7.2|Any CPU
- {53E204B7-97DD-441D-A96C-721DF014DF82}.Debug 4.7.2|x64.ActiveCfg = Debug 4.7.2|Any CPU
- {53E204B7-97DD-441D-A96C-721DF014DF82}.Debug 4.7.2|x64.Build.0 = Debug 4.7.2|Any CPU
- {53E204B7-97DD-441D-A96C-721DF014DF82}.Debug 4.7.2|x86.ActiveCfg = Debug 4.7.2|Any CPU
- {53E204B7-97DD-441D-A96C-721DF014DF82}.Debug 4.7.2|x86.Build.0 = Debug 4.7.2|Any CPU
{53E204B7-97DD-441D-A96C-721DF014DF82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{53E204B7-97DD-441D-A96C-721DF014DF82}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {53E204B7-97DD-441D-A96C-721DF014DF82}.Debug|x64.ActiveCfg = Debug|Any CPU
- {53E204B7-97DD-441D-A96C-721DF014DF82}.Debug|x64.Build.0 = Debug|Any CPU
- {53E204B7-97DD-441D-A96C-721DF014DF82}.Debug|x86.ActiveCfg = Debug|Any CPU
- {53E204B7-97DD-441D-A96C-721DF014DF82}.Debug|x86.Build.0 = Debug|Any CPU
{53E204B7-97DD-441D-A96C-721DF014DF82}.Release|Any CPU.ActiveCfg = Release|Any CPU
{53E204B7-97DD-441D-A96C-721DF014DF82}.Release|Any CPU.Build.0 = Release|Any CPU
- {53E204B7-97DD-441D-A96C-721DF014DF82}.Release|x64.ActiveCfg = Release|Any CPU
- {53E204B7-97DD-441D-A96C-721DF014DF82}.Release|x64.Build.0 = Release|Any CPU
- {53E204B7-97DD-441D-A96C-721DF014DF82}.Release|x86.ActiveCfg = Release|Any CPU
- {53E204B7-97DD-441D-A96C-721DF014DF82}.Release|x86.Build.0 = Release|Any CPU
{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug 4.7.2|Any CPU.ActiveCfg = Debug 4.7.2|Any CPU
{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug 4.7.2|Any CPU.Build.0 = Debug 4.7.2|Any CPU
- {CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug 4.7.2|x64.ActiveCfg = Debug 4.7.2|Any CPU
- {CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug 4.7.2|x64.Build.0 = Debug 4.7.2|Any CPU
- {CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug 4.7.2|x86.ActiveCfg = Debug 4.7.2|Any CPU
- {CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug 4.7.2|x86.Build.0 = Debug 4.7.2|Any CPU
{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug|x64.ActiveCfg = Debug|Any CPU
- {CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug|x64.Build.0 = Debug|Any CPU
- {CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug|x86.ActiveCfg = Debug|Any CPU
- {CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug|x86.Build.0 = Debug|Any CPU
{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Release|Any CPU.Build.0 = Release|Any CPU
- {CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Release|x64.ActiveCfg = Release|Any CPU
- {CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Release|x64.Build.0 = Release|Any CPU
- {CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Release|x86.ActiveCfg = Release|Any CPU
- {CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Release|x86.Build.0 = Release|Any CPU
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug 4.7.2|Any CPU.ActiveCfg = Debug 4.7.2|Any CPU
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug 4.7.2|Any CPU.Build.0 = Debug 4.7.2|Any CPU
- {3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug 4.7.2|x64.ActiveCfg = Debug 4.7.2|Any CPU
- {3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug 4.7.2|x64.Build.0 = Debug 4.7.2|Any CPU
- {3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug 4.7.2|x86.ActiveCfg = Debug 4.7.2|Any CPU
- {3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug 4.7.2|x86.Build.0 = Debug 4.7.2|Any CPU
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug|x64.ActiveCfg = Debug|Any CPU
- {3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug|x64.Build.0 = Debug|Any CPU
- {3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug|x86.ActiveCfg = Debug|Any CPU
- {3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug|x86.Build.0 = Debug|Any CPU
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Release|Any CPU.Build.0 = Release|Any CPU
- {3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Release|x64.ActiveCfg = Release|Any CPU
- {3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Release|x64.Build.0 = Release|Any CPU
- {3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Release|x86.ActiveCfg = Release|Any CPU
- {3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Release|x86.Build.0 = Release|Any CPU
{F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug 4.7.2|Any CPU.ActiveCfg = Debug|Any CPU
{F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug 4.7.2|Any CPU.Build.0 = Debug|Any CPU
- {F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug 4.7.2|x64.ActiveCfg = Debug 4.7.2|Any CPU
- {F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug 4.7.2|x64.Build.0 = Debug 4.7.2|Any CPU
- {F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug 4.7.2|x86.ActiveCfg = Debug 4.7.2|Any CPU
- {F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug 4.7.2|x86.Build.0 = Debug 4.7.2|Any CPU
{F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug|x64.ActiveCfg = Debug|Any CPU
- {F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug|x64.Build.0 = Debug|Any CPU
- {F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug|x86.ActiveCfg = Debug|Any CPU
- {F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug|x86.Build.0 = Debug|Any CPU
{F6D362DE-2256-44B1-927A-8CE4705D839A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F6D362DE-2256-44B1-927A-8CE4705D839A}.Release|Any CPU.Build.0 = Release|Any CPU
- {F6D362DE-2256-44B1-927A-8CE4705D839A}.Release|x64.ActiveCfg = Release|Any CPU
- {F6D362DE-2256-44B1-927A-8CE4705D839A}.Release|x64.Build.0 = Release|Any CPU
- {F6D362DE-2256-44B1-927A-8CE4705D839A}.Release|x86.ActiveCfg = Release|Any CPU
- {F6D362DE-2256-44B1-927A-8CE4705D839A}.Release|x86.Build.0 = Release|Any CPU
{B438694F-8FF7-464A-9EC8-10427374471F}.Debug 4.7.2|Any CPU.ActiveCfg = Debug|Any CPU
{B438694F-8FF7-464A-9EC8-10427374471F}.Debug 4.7.2|Any CPU.Build.0 = Debug|Any CPU
- {B438694F-8FF7-464A-9EC8-10427374471F}.Debug 4.7.2|x64.ActiveCfg = Debug 4.7.2|Any CPU
- {B438694F-8FF7-464A-9EC8-10427374471F}.Debug 4.7.2|x64.Build.0 = Debug 4.7.2|Any CPU
- {B438694F-8FF7-464A-9EC8-10427374471F}.Debug 4.7.2|x86.ActiveCfg = Debug 4.7.2|Any CPU
- {B438694F-8FF7-464A-9EC8-10427374471F}.Debug 4.7.2|x86.Build.0 = Debug 4.7.2|Any CPU
{B438694F-8FF7-464A-9EC8-10427374471F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B438694F-8FF7-464A-9EC8-10427374471F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B438694F-8FF7-464A-9EC8-10427374471F}.Debug|x64.ActiveCfg = Debug|Any CPU
- {B438694F-8FF7-464A-9EC8-10427374471F}.Debug|x64.Build.0 = Debug|Any CPU
- {B438694F-8FF7-464A-9EC8-10427374471F}.Debug|x86.ActiveCfg = Debug|Any CPU
- {B438694F-8FF7-464A-9EC8-10427374471F}.Debug|x86.Build.0 = Debug|Any CPU
{B438694F-8FF7-464A-9EC8-10427374471F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B438694F-8FF7-464A-9EC8-10427374471F}.Release|Any CPU.Build.0 = Release|Any CPU
- {B438694F-8FF7-464A-9EC8-10427374471F}.Release|x64.ActiveCfg = Release|Any CPU
- {B438694F-8FF7-464A-9EC8-10427374471F}.Release|x64.Build.0 = Release|Any CPU
- {B438694F-8FF7-464A-9EC8-10427374471F}.Release|x86.ActiveCfg = Release|Any CPU
- {B438694F-8FF7-464A-9EC8-10427374471F}.Release|x86.Build.0 = Release|Any CPU
{E5336563-1194-501E-BC4A-79AD9283EF90}.Debug 4.7.2|Any CPU.ActiveCfg = Debug|Any CPU
{E5336563-1194-501E-BC4A-79AD9283EF90}.Debug 4.7.2|Any CPU.Build.0 = Debug|Any CPU
- {E5336563-1194-501E-BC4A-79AD9283EF90}.Debug 4.7.2|x64.ActiveCfg = Debug 4.7.2|Any CPU
- {E5336563-1194-501E-BC4A-79AD9283EF90}.Debug 4.7.2|x64.Build.0 = Debug 4.7.2|Any CPU
- {E5336563-1194-501E-BC4A-79AD9283EF90}.Debug 4.7.2|x86.ActiveCfg = Debug 4.7.2|Any CPU
- {E5336563-1194-501E-BC4A-79AD9283EF90}.Debug 4.7.2|x86.Build.0 = Debug 4.7.2|Any CPU
{E5336563-1194-501E-BC4A-79AD9283EF90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E5336563-1194-501E-BC4A-79AD9283EF90}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E5336563-1194-501E-BC4A-79AD9283EF90}.Debug|x64.ActiveCfg = Debug|Any CPU
- {E5336563-1194-501E-BC4A-79AD9283EF90}.Debug|x64.Build.0 = Debug|Any CPU
- {E5336563-1194-501E-BC4A-79AD9283EF90}.Debug|x86.ActiveCfg = Debug|Any CPU
- {E5336563-1194-501E-BC4A-79AD9283EF90}.Debug|x86.Build.0 = Debug|Any CPU
{E5336563-1194-501E-BC4A-79AD9283EF90}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E5336563-1194-501E-BC4A-79AD9283EF90}.Release|Any CPU.Build.0 = Release|Any CPU
- {E5336563-1194-501E-BC4A-79AD9283EF90}.Release|x64.ActiveCfg = Release|Any CPU
- {E5336563-1194-501E-BC4A-79AD9283EF90}.Release|x64.Build.0 = Release|Any CPU
- {E5336563-1194-501E-BC4A-79AD9283EF90}.Release|x86.ActiveCfg = Release|Any CPU
- {E5336563-1194-501E-BC4A-79AD9283EF90}.Release|x86.Build.0 = Release|Any CPU
- {680BA287-E61F-4B8D-BD7A-84C2504F5F9C}.Debug 4.7.2|Any CPU.ActiveCfg = Debug|Any CPU
- {680BA287-E61F-4B8D-BD7A-84C2504F5F9C}.Debug 4.7.2|x64.ActiveCfg = Debug|Any CPU
- {680BA287-E61F-4B8D-BD7A-84C2504F5F9C}.Debug 4.7.2|x86.ActiveCfg = Debug|Any CPU
- {680BA287-E61F-4B8D-BD7A-84C2504F5F9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {680BA287-E61F-4B8D-BD7A-84C2504F5F9C}.Debug|x64.ActiveCfg = Debug|Any CPU
- {680BA287-E61F-4B8D-BD7A-84C2504F5F9C}.Debug|x86.ActiveCfg = Debug|Any CPU
- {680BA287-E61F-4B8D-BD7A-84C2504F5F9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {680BA287-E61F-4B8D-BD7A-84C2504F5F9C}.Release|x64.ActiveCfg = Release|Any CPU
- {680BA287-E61F-4B8D-BD7A-84C2504F5F9C}.Release|x86.ActiveCfg = Release|Any CPU
- {F508E0BA-E885-424F-9D4C-359CF0011DEF}.Debug 4.7.2|Any CPU.ActiveCfg = Debug|Any CPU
- {F508E0BA-E885-424F-9D4C-359CF0011DEF}.Debug 4.7.2|x64.ActiveCfg = Debug|Any CPU
- {F508E0BA-E885-424F-9D4C-359CF0011DEF}.Debug 4.7.2|x86.ActiveCfg = Debug|Any CPU
- {F508E0BA-E885-424F-9D4C-359CF0011DEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F508E0BA-E885-424F-9D4C-359CF0011DEF}.Debug|x64.ActiveCfg = Debug|Any CPU
- {F508E0BA-E885-424F-9D4C-359CF0011DEF}.Debug|x86.ActiveCfg = Debug|Any CPU
- {F508E0BA-E885-424F-9D4C-359CF0011DEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F508E0BA-E885-424F-9D4C-359CF0011DEF}.Release|x64.ActiveCfg = Release|Any CPU
- {F508E0BA-E885-424F-9D4C-359CF0011DEF}.Release|x86.ActiveCfg = Release|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Debug 4.7.2|Any CPU.ActiveCfg = Debug|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Debug 4.7.2|Any CPU.Build.0 = Debug|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Debug 4.7.2|x64.ActiveCfg = Debug|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Debug 4.7.2|x64.Build.0 = Debug|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Debug 4.7.2|x86.ActiveCfg = Debug|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Debug 4.7.2|x86.Build.0 = Debug|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Debug|x64.ActiveCfg = Debug|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Debug|x64.Build.0 = Debug|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Debug|x86.ActiveCfg = Debug|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Debug|x86.Build.0 = Debug|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Release|Any CPU.Build.0 = Release|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Release|x64.ActiveCfg = Release|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Release|x64.Build.0 = Release|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Release|x86.ActiveCfg = Release|Any CPU
- {841EE676-7784-4456-8F76-3697C6D432A6}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -212,9 +90,6 @@ Global
{F6D362DE-2256-44B1-927A-8CE4705D839A} = {B24989D7-32B5-48D5-9AE1-5F3B17D25206}
{B438694F-8FF7-464A-9EC8-10427374471F} = {B24989D7-32B5-48D5-9AE1-5F3B17D25206}
{E5336563-1194-501E-BC4A-79AD9283EF90} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
- {680BA287-E61F-4B8D-BD7A-84C2504F5F9C} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
- {F508E0BA-E885-424F-9D4C-359CF0011DEF} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
- {841EE676-7784-4456-8F76-3697C6D432A6} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6907A4BF-7201-47CF-AAB1-3597F3B8E1C3}
diff --git a/docs/docs/technical-docs/Arch-activate.md b/docs/docs/technical-docs/Arch-activate.md
index 61a0b68b..72a9e3eb 100644
--- a/docs/docs/technical-docs/Arch-activate.md
+++ b/docs/docs/technical-docs/Arch-activate.md
@@ -32,7 +32,7 @@ If the `CustomActivate()` method is long, consider breaking it up into many smal
Note: It is best-practice in Essentials to not write arbitrarily-timed startup sequences to ensure that a "system" or room is functional. Rather, we encourage the developer to use various properties and conditions on devices to aggregate together "room is ready" statuses that can trigger further action. This ensures that all devices can be up and alive, allowing them to be debugged within a program that may otherwise be misbehaving - as well as not making users and expensive developers wait for code to start up!
```cs
-protected override bool CustomActivate()
+public override bool CustomActivate()
{
Debug.Console(0, this, "Final activation. Setting up actions and feedbacks");
SetupFunctions();
@@ -52,7 +52,7 @@ We can see in the example below that during the `CustomActivate()` phase, we def
### **Example**
```cs
-protected override bool CustomActivate()
+public override bool CustomActivate()
{
foreach (var i in Config.Inputs)
{
@@ -115,7 +115,7 @@ The main task that should be undertaken in the `Initialize()` method for any 3rd
### Example (from `PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.CiscoSparkCodec`)
```cs
- protected override void Initialize()
+ public override void Initialize()
{
var socket = Communication as ISocketStatus;
if (socket != null)
diff --git a/docs/docs/technical-docs/Connection-Based-Routing.md b/docs/docs/technical-docs/Connection-Based-Routing.md
index 4ba902db..07dc318b 100644
--- a/docs/docs/technical-docs/Connection-Based-Routing.md
+++ b/docs/docs/technical-docs/Connection-Based-Routing.md
@@ -2,13 +2,35 @@
## TL;DR
-Routing is defined by a connection graph or a wiring diagram. Routeable devices are sources, midpoints, or destinations. Devices are connected by tie lines. Tie lines represent the cables connecting devices, and are of type audio, video or both. Routes are made by telling a destination to get an audio/video/combined route from a source.
+Routing is defined by a connection graph or a wiring diagram. Routable devices are sources, midpoints, or destinations. Devices are connected by tie lines. Tie lines represent the cables connecting devices, and have specific signal types (audio, video, audioVideo, secondaryAudio, usbInput, usbOutput). Routes are made by telling a destination to get a route from a source for a specific signal type. Combined signal types (e.g., audioVideo) are automatically split into separate routing operations.
## Summary
Essentials routing is described by defining a graph of connections between devices in a system, typically in configuration. The audio, video and combination connections are like a wiring diagram. This graph is a collection of devices and tie lines, each tie line connecting a source device, source output port, destination device and destination input port. Tie lines are logically represented as a collection.
-When routes are to be executed, Essentials will use this connection graph to decide on routes from source to destination. A method call is made on a destination, which says “destination, find a way for source xyz to get to you.” An algorithm analyzes the tie lines, instantly walking backwards from the destination, down every connection until it finds a complete path from the source. If a connected path is found, the algorithm then walks forward through all midpoints to the destination, executing switches as required until the full route is complete. The developer or configurer only needs to say “destination, get source xyz” and Essentials figures out how, regardless of what devices lie in between.
+When routes are to be executed, Essentials will use this connection graph to decide on routes from source to destination. A method call is made on a destination, which says "destination, find a way for source xyz to get to you." An algorithm analyzes the tie lines, instantly walking backwards from the destination, down every connection until it finds a complete path from the source. If a connected path is found, the algorithm then walks forward through all midpoints to the destination, executing switches as required until the full route is complete. The developer or configurer only needs to say "destination, get source xyz" and Essentials figures out how, regardless of what devices lie in between.
+
+### Signal Type Handling
+
+When a combined signal type like `audioVideo` is requested, Essentials automatically splits it into two separate routing operations—one for audio and one for video. Each signal type is routed independently through the system, ensuring that:
+- Audio-only tie lines can be used for the audio portion
+- Video-only tie lines can be used for the video portion
+- AudioVideo tie lines can be used for both portions
+
+During path discovery, **only tie lines that support the requested signal type are considered**. For example, if a video route is requested, only tie lines with the video flag will be evaluated. This ensures signal compatibility throughout the entire routing chain.
+
+### Port-Specific Routing
+
+The routing system supports routing to and from specific ports on devices. You can specify:
+- A specific input port on the destination device
+- A specific output port on the source device
+- Both specific ports for precise routing control
+
+When no specific ports are specified, the algorithm will automatically discover the appropriate ports based on available tie lines.
+
+### Request Queuing
+
+All routing requests are processed sequentially through a queue. For devices that implement warming/cooling behavior (e.g., projectors), route requests are automatically held when a device is cooling down and executed once the device is ready. This prevents routing errors and ensures proper device state management.
### Classes Referenced
@@ -23,15 +45,15 @@ When routes are to be executed, Essentials will use this connection graph to dec
The diagram below shows the connections in a simple presentation system, with a few variations in connection paths. Example routes will be described following the diagram.
-Each visible line between ports on devices represents a tie line. A tie line connects an output port on one device to an input port on another device, for example: an HDMI port on a document camera to an HDMI input on a matrix switcher. A tie line may be audio, video or both. It is essentially a logical representation of a physical cable in a system. This diagram has 12 tie lines, and those tie lines are defined in the tieLines array in configuration.
+Each visible line between ports on devices represents a tie line. A tie line connects an output port on one device to an input port on another device, for example: an HDMI port on a document camera to an HDMI input on a matrix switcher. A tie line has a signal type (audio, video, audioVideo, secondaryAudio, usbInput, or usbOutput) that determines what signals can travel through it. It is essentially a logical representation of a physical cable in a system. This diagram has 12 tie lines, and those tie lines are defined in the tieLines array in configuration.

Let’s go through some examples of routing, using pseudo-code:
1. Method call: “Projector 1, show Doc cam.” Routing will walk backwards through DM-RMC-3 and DM-8x8 iterating through all “wired up” ports until it finds a path back to the Doc cam. Routing will then step back through all devices in the discovered chain, switching routes on those that are switchable: Doc cam: no switching; DM 8x8: route input 3 to output 3; DM-RMC-3: no switching; Projector 1: Select input HDMI In. Route is complete.
-2. Method call: “Projector 2, show Laptop, video-only.” Routing will walk backwards through DM-RMC-4, DM 8x8, DM-TX-1, iterating through all connected ports until it finds a connection to the laptop. Routing then steps back through all devices, switching video where it can: Laptop: No switching; DM-TX-1: Select HDMI in; DM 8x8: Route input 5 to output 4; DM-RMC-4: No switching; Projector 2: Select HDMI input. Route is complete.
-3. Method call: “Amplifier, connect Laptop audio.” Again walking backwards to Laptop, as in #2 above. Switching will take place on DM-TX-1, DM 8x8, audio-only.
+2. Method call: "Projector 2, show Laptop, video-only." Routing will walk backwards through DM-RMC-4, DM 8x8, DM-TX-1, iterating through all connected ports until it finds a connection to the laptop. During this search, only tie lines that support video signals are considered. Routing then steps back through all devices, switching video where it can: Laptop: No switching; DM-TX-1: Select HDMI in; DM 8x8: Route input 5 to output 4; DM-RMC-4: No switching; Projector 2: Select HDMI input. Route is complete.
+3. Method call: "Amplifier, connect Laptop audio." Again walking backwards to Laptop, as in #2 above, but this time only tie lines supporting audio signals are evaluated. Switching will take place on DM-TX-1, DM 8x8, audio-only.
4. Very simple call: “Lobby display, show signage controller.” Routing will walk back on HDMI input 1 and immediately find the signage controller. It then does a switch to HDMI 1 on the display.
All four of the above could be logically combined in a series of calls to define a possible “scene” in a room: Put Document camera on Projector 1, put Laptop on Projector 2 and the audio, put Signage on the Lobby display. They key takeaway is that the developer doesn’t need to define what is involved in making a certain route. The person configuring the system defines how it’s wired up, and the code only needs to tell a given destination to get a source, likely through configuration as well.
@@ -40,6 +62,37 @@ All of the above routes can be defined in source list routing tables, covered el
---
+## Routing Algorithm Details
+
+### Combined Signal Type Splitting
+
+When an `audioVideo` route is requested, the routing system automatically splits it into two independent routing operations:
+
+1. **Audio Route**: Finds the best path for audio signals from source to destination
+2. **Video Route**: Finds the best path for video signals from source to destination
+
+Each route can take a different physical path through the system. For example:
+- Video might travel: Laptop → DM-TX-1 → DM Matrix → Display
+- Audio might travel: Laptop → DM-TX-1 → DM Matrix → Audio Processor → Amplifier
+
+Both routes are discovered, stored, and executed independently. This allows for flexible system designs where audio and video follow different paths.
+
+The same splitting behavior occurs for `Video + SecondaryAudio` requests, where video and secondary audio are routed as separate operations.
+
+### Signal Type Filtering
+
+At each step of the route discovery process, the algorithm filters tie lines based on the requested signal type:
+
+- **Video request**: Only considers tie lines with the `video` flag set
+- **Audio request**: Only considers tie lines with the `audio` flag set
+- **AudioVideo request**: Routes audio and video separately, each following their respective filtering rules
+
+If no tie line exists with the required signal type at any point in the chain, that path is rejected and the algorithm continues searching for an alternative route. If no valid path is found, the route request fails and no switching occurs.
+
+This filtering ensures that incompatible signal types never interfere with routing decisions. For example, an audio-only cable will never be selected when routing video, preventing misconfiguration errors.
+
+---
+
### Definitions
#### Ports
@@ -64,18 +117,101 @@ A sink is a device at the end of a full signal path. For example, a display, amp
#### Tie-line
-A tie-line is a logical representation of a physical cable connection between two devices. It has five properties that define how the tie-line connects two devices. A configuration snippet for a single tie line connecting HDMI output 1 on a Cisco RoomKit to HDMI input 1 on a display, carrying both audio and video, is shown below.
+A tie-line is a logical representation of a physical cable connection between two devices. It has five properties that define how the tie-line connects two devices.
+
+##### How Tie Line Types Are Determined
+
+The effective type of a tie line is determined by one of two methods:
+
+1. **Automatic (Recommended)**: When no `type` property is specified in configuration, the tie line's type is automatically calculated as the **intersection** of signal types supported by both the source and destination ports. This ensures only compatible signals are considered for routing.
+
+ Example: If a source port supports `AudioVideo` and the destination port supports `Audio`, the tie line will have type `Audio` (the only common type).
+
+2. **Manual Override**: When the `type` property is explicitly set, it overrides the automatic calculation. This is useful when the physical cable supports fewer signal types than both ports are capable of.
+
+ Example: Both ports support `AudioVideo`, but the cable only carries audio, so you set `"type": "audio"`.
+
+##### Validation
+
+At startup, tie line configurations are validated to ensure:
+- Both ports exist on their respective devices
+- The source and destination ports have at least one common signal type
+- If a `type` override is specified, both ports must support that signal type
+
+Invalid tie lines will fail to build with descriptive error messages, preventing runtime routing issues.
+
+##### Signal Types
+
+Tie lines support the following signal types:
+
+- `audio` - Audio-only signals
+- `video` - Video-only signals
+- `audioVideo` - Combined audio and video (automatically split during routing)
+- `secondaryAudio` - Secondary audio channel (e.g., program audio separate from microphone audio)
+- `usbInput` - USB input signals
+- `usbOutput` - USB output signals
+
+The `type` property determines which signals can travel through the tie line. During route discovery, only tie lines matching the requested signal type will be considered as valid paths.
+
+**Note**: In most cases, you should omit the `type` property and let the system automatically calculate it from the port capabilities. Only use it when you need to restrict the tie line to fewer signal types than the ports support or when needed for clarity.
+
+##### Configuration Examples
+
+**Example 1: Automatic type calculation (recommended)**
+
+Connecting an HDMI cable between devices that both support audio and video. The `type` property is omitted, so the tie line will automatically support `AudioVideo`:
```json
{
"sourceKey": "ciscoSparkPlusCodec-1",
"sourcePort": "HdmiOut1",
"destinationKey": "display-1",
- "destinationPort": "HdmiIn1",
- "type": "audioVideo"
+ "destinationPort": "HdmiIn1"
}
```
+**Example 2: Type override for cable limitations**
+
+Both devices support `AudioVideo`, but the physical cable only carries audio. The `type` property restricts routing to audio only:
+
+```json
+{
+ "sourceKey": "dmSwitcher-1",
+ "sourcePort": "audioVideoOut1",
+ "destinationKey": "amplifier-1",
+ "destinationPort": "audioVideoIn1",
+ "type": "audio"
+}
+```
+
+**Example 3: Mismatched port types (automatically handled)**
+
+Source only supports audio, destination supports both. No `type` needed—the tie line will automatically be `Audio`:
+
+```json
+{
+ "sourceKey": "audioProcessor-1",
+ "sourcePort": "audioOut1",
+ "destinationKey": "dmSwitcher-1",
+ "destinationPort": "audioVideoIn1"
+}
+```
+
+**Invalid Example: Incompatible types**
+
+This configuration will **fail validation** at startup because the ports have no common signal types:
+
+```json
+{
+ "sourceKey": "audioProcessor-1",
+ "sourcePort": "audioOut1",
+ "destinationKey": "display-1",
+ "destinationPort": "hdmiIn1",
+ "type": "video"
+}
+```
+Error: `"Override type 'Video' is not supported by source port 'audioOut1' (type: Audio)"`
+
### Interfaces
Todo: Define Interfaces IRouting, IRoutingOutputs, IRoutingInputs
diff --git a/docs/docs/usage/GenericComm.md b/docs/docs/usage/GenericComm.md
index 243536e5..16f467e2 100644
--- a/docs/docs/usage/GenericComm.md
+++ b/docs/docs/usage/GenericComm.md
@@ -183,11 +183,12 @@ namespace PepperDash.Core
Cresnet = 8,
Cec = 9,
Udp = 10,
+ UdpClient = 11,
}
}
```
-These enumerations are not case sensitive. Not all methods are valid for a ```genericComm``` device. For a comport, the only valid type would be ```Com```. For a direct network socket, valid options are ```Ssh```, ```Tcpip```, ```Telnet```, and ```Udp```.
+These enumerations are not case sensitive. Not all methods are valid for a ```genericComm``` device. For a comport, the only valid type would be ```Com```. For a direct network socket, valid options are ```Ssh```, ```Tcpip```, ```Telnet```, ```UdpClient```, and ```Udp```.
##### ComParams
@@ -287,7 +288,7 @@ This property maps to the number of the port on the device you have mapped the r
##### TcpSshParams
-A ```Ssh```, ```TcpIp```, or ```Udp``` device requires a ```tcpSshProperties``` object to set the propeties of the socket.
+A ```Ssh```, ```TcpIp```, ```UdpClient```, or ```Udp``` device requires a ```tcpSshProperties``` object to set the propeties of the socket.
```Json
{
@@ -304,7 +305,7 @@ A ```Ssh```, ```TcpIp```, or ```Udp``` device requires a ```tcpSshProperties```
**```address```**
-This is the IP address, hostname, or FQDN of the resource you wish to open a socket to. In the case of a UDP device, you can set either a single whitelist address with this data, or an appropriate broadcast address.
+This is the IP address, hostname, or FQDN of the resource you wish to open a socket to. Use ```UdpClient``` for outbound UDP to a remote endpoint. Use ```Udp``` when you need Essentials to bind a local UDP listener.
**```port```**
diff --git a/runtimeconfig.json b/runtimeconfig.json
deleted file mode 100644
index 32835bac..00000000
--- a/runtimeconfig.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "runtimeOptions": {
- "configProperties": {
- "System.Globalization.Invariant": false
- }
- }
-}
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index ef8e86b0..235fee48 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -1,11 +1,11 @@
- 3.0.0-local
+ 2.42.1-local
$(Version)
PepperDash Technology
PepperDash Technology
PepperDash Essentials
- Copyright © 2025
+ Copyright © 2026
https://github.com/PepperDash/Essentials
git
Crestron; 4series
@@ -20,4 +20,7 @@
+
+
+
diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets
index 50816cf4..ce6c0eed 100644
--- a/src/Directory.Build.targets
+++ b/src/Directory.Build.targets
@@ -1,14 +1,14 @@
-
+
true
build;
-
+
true
build;
-
+
true
build;
diff --git a/src/PepperDash.Core.Abstractions/DebugServiceRegistration.cs b/src/PepperDash.Core.Abstractions/DebugServiceRegistration.cs
deleted file mode 100644
index c5e18c5a..00000000
--- a/src/PepperDash.Core.Abstractions/DebugServiceRegistration.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-namespace PepperDash.Core.Abstractions;
-
-///
-/// Allows pre-registration of Crestron service implementations before the Debug
-/// static class initialises. Call from the composition root
-/// (e.g. ControlSystem constructor) before any code touches Debug.*.
-/// Test projects should call it with no-op / in-memory implementations so that the
-/// Debug static constructor never tries to reach the real Crestron SDK.
-///
-public static class DebugServiceRegistration
-{
- /// Gets the registered environment abstraction, or null if not registered.
- public static ICrestronEnvironment? Environment { get; private set; }
-
- /// Gets the registered console abstraction, or null if not registered.
- public static ICrestronConsole? Console { get; private set; }
-
- /// Gets the registered data-store abstraction, or null if not registered.
- public static ICrestronDataStore? DataStore { get; private set; }
-
- ///
- /// Registers the service implementations that Debug will use when its
- /// static constructor runs. Any parameter may be null to leave the
- /// corresponding service unregistered (the Debug class will skip that
- /// capability gracefully).
- ///
- public static void Register(
- ICrestronEnvironment? environment,
- ICrestronConsole? console,
- ICrestronDataStore? dataStore)
- {
- Environment = environment;
- Console = console;
- DataStore = dataStore;
- }
-}
diff --git a/src/PepperDash.Core.Abstractions/Enums.cs b/src/PepperDash.Core.Abstractions/Enums.cs
deleted file mode 100644
index cd6052e6..00000000
--- a/src/PepperDash.Core.Abstractions/Enums.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-namespace PepperDash.Core.Abstractions;
-
-///
-/// Mirrors Crestron's eDevicePlatform without requiring the Crestron SDK.
-///
-public enum DevicePlatform
-{
- /// Hardware appliance (e.g. CP4, MC4).
- Appliance,
- /// Crestron Virtual Control / server runtime.
- Server,
-}
-
-///
-/// Mirrors Crestron's eRuntimeEnvironment.
-///
-public enum RuntimeEnvironment
-{
- /// SimplSharpPro program slot (hardware 4-series).
- SimplSharpPro,
- /// SimplSharp (older 3-series or server environments).
- SimplSharp,
- /// Any other environment — check for completeness.
- Other,
-}
-
-///
-/// Mirrors Crestron's ConsoleAccessLevelEnum.
-///
-public enum ConsoleAccessLevel
-{
- AccessAdministrator = 0,
- AccessOperator = 1,
- AccessProgrammer = 2,
-}
-
-///
-/// Mirrors Crestron's eProgramStatusEventType.
-///
-public enum ProgramStatusEventType
-{
- Starting,
- Stopping,
- Paused,
- Resumed,
-}
-
-///
-/// Mirrors the event type used by Crestron's EthernetEventArgs.
-///
-public enum EthernetEventType
-{
- LinkDown = 0,
- LinkUp = 1,
-}
-
-///
-/// Event args for Crestron ethernet link events.
-///
-public class PepperDashEthernetEventArgs : EventArgs
-{
- public EthernetEventType EthernetEventType { get; }
- public short EthernetAdapter { get; }
-
- public PepperDashEthernetEventArgs(EthernetEventType eventType, short adapter)
- {
- EthernetEventType = eventType;
- EthernetAdapter = adapter;
- }
-}
-
-///
-/// Mirrors the set of CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET values
-/// used across this codebase — does not aim to be exhaustive.
-///
-public enum EthernetParameterType
-{
- GetCurrentIpAddress,
- GetHostname,
- GetDomainName,
- GetLinkStatus,
- GetCurrentDhcpState,
- GetCurrentIpMask,
- GetCurrentRouter,
- GetMacAddress,
- GetDnsServer,
-}
-
-///
-/// Mirrors Crestron's SocketStatus without requiring the Crestron SDK.
-///
-public enum PepperDashSocketStatus
-{
- SocketNotConnected = 0,
- SocketConnected = 2,
- SocketConnectionInProgress = 6,
- SocketConnectFailed = 11,
- SocketDisconnecting = 12,
- SocketBrokenRemotely = 7,
-}
diff --git a/src/PepperDash.Core.Abstractions/ICrestronConsole.cs b/src/PepperDash.Core.Abstractions/ICrestronConsole.cs
deleted file mode 100644
index 335c4ee0..00000000
--- a/src/PepperDash.Core.Abstractions/ICrestronConsole.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-namespace PepperDash.Core.Abstractions;
-
-///
-/// Abstracts Crestron.SimplSharp.CrestronConsole to allow unit testing
-/// without the Crestron SDK.
-///
-public interface ICrestronConsole
-{
- /// Prints a line to the Crestron console/telnet output.
- void PrintLine(string message);
-
- /// Prints text (without newline) to the Crestron console/telnet output.
- void Print(string message);
-
- ///
- /// Sends a response string to the console for the currently-executing console command.
- ///
- void ConsoleCommandResponse(string message);
-
- ///
- /// Registers a new command with the Crestron console.
- ///
- /// Handler invoked when the command is typed.
- /// Command name (no spaces).
- /// Help text shown by the Crestron console.
- /// Minimum access level required to run the command.
- void AddNewConsoleCommand(
- Action callback,
- string command,
- string helpText,
- ConsoleAccessLevel accessLevel);
-}
diff --git a/src/PepperDash.Core.Abstractions/ICrestronDataStore.cs b/src/PepperDash.Core.Abstractions/ICrestronDataStore.cs
deleted file mode 100644
index 96fd1856..00000000
--- a/src/PepperDash.Core.Abstractions/ICrestronDataStore.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-namespace PepperDash.Core.Abstractions;
-
-///
-/// Abstracts Crestron.SimplSharp.CrestronDataStore.CrestronDataStoreStatic
-/// to allow unit testing without the Crestron SDK.
-///
-public interface ICrestronDataStore
-{
- /// Initialises the data store. Must be called once before any other operation.
- void InitStore();
-
- /// Reads an integer value from the local (program-slot) store.
- /// true if the value was found and read successfully.
- bool TryGetLocalInt(string key, out int value);
-
- /// Writes an integer value to the local (program-slot) store.
- /// true on success.
- bool SetLocalInt(string key, int value);
-
- /// Writes an unsigned integer value to the local (program-slot) store.
- /// true on success.
- bool SetLocalUint(string key, uint value);
-
- /// Reads a boolean value from the local (program-slot) store.
- /// true if the value was found and read successfully.
- bool TryGetLocalBool(string key, out bool value);
-
- /// Writes a boolean value to the local (program-slot) store.
- /// true on success.
- bool SetLocalBool(string key, bool value);
-}
diff --git a/src/PepperDash.Core.Abstractions/ICrestronEnvironment.cs b/src/PepperDash.Core.Abstractions/ICrestronEnvironment.cs
deleted file mode 100644
index 1e73c83f..00000000
--- a/src/PepperDash.Core.Abstractions/ICrestronEnvironment.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-namespace PepperDash.Core.Abstractions;
-
-///
-/// Abstracts Crestron.SimplSharp.CrestronEnvironment to allow unit testing
-/// without the Crestron SDK.
-///
-public interface ICrestronEnvironment
-{
- /// Gets the platform the program is executing on.
- DevicePlatform DevicePlatform { get; }
-
- /// Gets the current runtime environment.
- RuntimeEnvironment RuntimeEnvironment { get; }
-
- /// Gets the platform-appropriate newline string.
- string NewLine { get; }
-
- /// Gets the application number (program slot).
- uint ApplicationNumber { get; }
-
- /// Gets the room ID (used in Crestron Virtual Control / server environments).
- uint RoomId { get; }
-
- /// Raised when program status changes (starting, stopping, etc.).
- event EventHandler ProgramStatusChanged;
-
- /// Raised when the ethernet link changes state.
- event EventHandler EthernetEventReceived;
-
- /// Gets the application root directory path.
- string GetApplicationRootDirectory();
-
- ///
- /// Returns true when running on real Crestron hardware.
- /// Returns false in test / dev environments so that SDK-specific
- /// sinks and enrichers can be safely skipped.
- ///
- bool IsHardwareRuntime { get; }
-}
-
-///
-/// Event args for .
-///
-public class ProgramStatusEventArgs : EventArgs
-{
- public ProgramStatusEventType EventType { get; }
-
- public ProgramStatusEventArgs(ProgramStatusEventType eventType)
- {
- EventType = eventType;
- }
-}
diff --git a/src/PepperDash.Core.Abstractions/IEthernetHelper.cs b/src/PepperDash.Core.Abstractions/IEthernetHelper.cs
deleted file mode 100644
index 0ae1c83b..00000000
--- a/src/PepperDash.Core.Abstractions/IEthernetHelper.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace PepperDash.Core.Abstractions;
-
-///
-/// Abstracts Crestron.SimplSharp.CrestronEthernetHelper to allow unit testing
-/// without the Crestron SDK.
-///
-public interface IEthernetHelper
-{
- ///
- /// Returns a network parameter string for the specified adapter.
- ///
- /// The parameter to retrieve.
- /// Ethernet adapter index (0 = LAN A).
- /// String value of the requested parameter.
- string GetEthernetParameter(EthernetParameterType parameter, short ethernetAdapterId);
-}
diff --git a/src/PepperDash.Core.Abstractions/PepperDash.Core.Abstractions.csproj b/src/PepperDash.Core.Abstractions/PepperDash.Core.Abstractions.csproj
deleted file mode 100644
index c1cdf5bc..00000000
--- a/src/PepperDash.Core.Abstractions/PepperDash.Core.Abstractions.csproj
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
- PepperDash.Core.Abstractions
- PepperDash.Core.Abstractions
- net8.0
- enable
- true
- en
- PepperDash Core Abstractions
- PepperDash Technologies
- git
- https://github.com/PepperDash/PepperDashCore
- enable
- false
- $(Version)
- true
-
-
-
diff --git a/src/PepperDash.Core.Tests/Devices/DeviceTests.cs b/src/PepperDash.Core.Tests/Devices/DeviceTests.cs
deleted file mode 100644
index 2450e401..00000000
--- a/src/PepperDash.Core.Tests/Devices/DeviceTests.cs
+++ /dev/null
@@ -1,323 +0,0 @@
-using FluentAssertions;
-using PepperDash.Core;
-using Xunit;
-
-namespace PepperDash.Core.Tests.Devices;
-
-///
-/// Tests for — the base class for all PepperDash devices.
-/// These run without Crestron hardware; Debug is initialized with fakes via TestInitializer.
-///
-public class DeviceTests
-{
- // -----------------------------------------------------------------------
- // Construction
- // -----------------------------------------------------------------------
-
- [Fact]
- public void Constructor_SingleArg_SetsKey()
- {
- var device = new Device("my-device");
-
- device.Key.Should().Be("my-device");
- }
-
- [Fact]
- public void Constructor_SingleArg_SetsNameToEmpty()
- {
- var device = new Device("my-device");
-
- device.Name.Should().BeEmpty();
- }
-
- [Fact]
- public void Constructor_TwoArg_SetsKeyAndName()
- {
- var device = new Device("my-device", "My Device");
-
- device.Key.Should().Be("my-device");
- device.Name.Should().Be("My Device");
- }
-
- [Fact]
- public void Constructor_KeyWithDot_StillSetsKey()
- {
- // The dot triggers a debug log warning but must not prevent construction.
- var device = new Device("parent.child");
-
- device.Key.Should().Be("parent.child");
- }
-
- // -----------------------------------------------------------------------
- // ToString
- // -----------------------------------------------------------------------
-
- [Fact]
- public void ToString_WithName_FormatsKeyDashName()
- {
- var device = new Device("cam-01", "Front Camera");
-
- device.ToString().Should().Be("cam-01 - Front Camera");
- }
-
- [Fact]
- public void ToString_WithoutName_UsesDashPlaceholder()
- {
- var device = new Device("cam-01");
-
- device.ToString().Should().Be("cam-01 - ---");
- }
-
- // -----------------------------------------------------------------------
- // DefaultDevice
- // -----------------------------------------------------------------------
-
- [Fact]
- public void DefaultDevice_IsNotNull()
- {
- Device.DefaultDevice.Should().NotBeNull();
- }
-
- [Fact]
- public void DefaultDevice_HasKeyDefault()
- {
- Device.DefaultDevice.Key.Should().Be("Default");
- }
-
- // -----------------------------------------------------------------------
- // CustomActivate / Activate / Deactivate / Initialize
- // -----------------------------------------------------------------------
-
- [Fact]
- public void CustomActivate_DefaultReturnTrue()
- {
- var device = new TestDevice("d1");
-
- device.CallCustomActivate().Should().BeTrue();
- }
-
- [Fact]
- public void Deactivate_DefaultReturnsTrue()
- {
- var device = new Device("d1");
-
- device.Deactivate().Should().BeTrue();
- }
-
- [Fact]
- public void Activate_CallsCustomActivate_AndReturnsItsResult()
- {
- var stub = new ActivateTrackingDevice("d1", result: false);
-
- stub.Activate().Should().BeFalse();
- stub.CustomActivateCalled.Should().BeTrue();
- }
-
- [Fact]
- public void Activate_TrueWhenCustomActivateReturnsTrue()
- {
- var stub = new ActivateTrackingDevice("d1", result: true);
-
- stub.Activate().Should().BeTrue();
- }
-
- [Fact]
- public void Initialize_DoesNotThrow()
- {
- var device = new TestDevice("d1");
- var act = () => device.CallInitialize();
-
- act.Should().NotThrow();
- }
-
- // -----------------------------------------------------------------------
- // PreActivate
- // -----------------------------------------------------------------------
-
- [Fact]
- public void PreActivate_NoActions_DoesNotThrow()
- {
- var device = new TestDevice("d1");
- var act = () => device.PreActivate();
-
- act.Should().NotThrow();
- }
-
- [Fact]
- public void PreActivate_RunsRegisteredActionsInOrder()
- {
- var device = new TestDevice("d1");
- var order = new List();
-
- device.AddPreActivationAction(() => order.Add(1));
- device.AddPreActivationAction(() => order.Add(2));
- device.AddPreActivationAction(() => order.Add(3));
-
- device.PreActivate();
-
- order.Should().Equal(1, 2, 3);
- }
-
- [Fact]
- public void PreActivate_ContinuesAfterFaultingAction()
- {
- var device = new TestDevice("d1");
- var reached = false;
-
- device.AddPreActivationAction(() => throw new InvalidOperationException("boom"));
- device.AddPreActivationAction(() => reached = true);
-
- var act = () => device.PreActivate();
-
- act.Should().NotThrow("exceptions in individual actions must be caught internally");
- reached.Should().BeTrue("actions after a faulting action must still run");
- }
-
- // -----------------------------------------------------------------------
- // PostActivate
- // -----------------------------------------------------------------------
-
- [Fact]
- public void PostActivate_NoActions_DoesNotThrow()
- {
- var device = new TestDevice("d1");
- var act = () => device.PostActivate();
-
- act.Should().NotThrow();
- }
-
- [Fact]
- public void PostActivate_RunsRegisteredActionsInOrder()
- {
- var device = new TestDevice("d1");
- var order = new List();
-
- device.AddPostActivationAction(() => order.Add(1));
- device.AddPostActivationAction(() => order.Add(2));
-
- device.PostActivate();
-
- order.Should().Equal(1, 2);
- }
-
- [Fact]
- public void PostActivate_ContinuesAfterFaultingAction()
- {
- var device = new TestDevice("d1");
- var reached = false;
-
- device.AddPostActivationAction(() => throw new Exception("boom"));
- device.AddPostActivationAction(() => reached = true);
-
- var act = () => device.PostActivate();
-
- act.Should().NotThrow();
- reached.Should().BeTrue();
- }
-
- // -----------------------------------------------------------------------
- // Pre and Post actions are independent lists
- // -----------------------------------------------------------------------
-
- [Fact]
- public void PreActivationActions_DoNotRunOnPostActivate()
- {
- var device = new TestDevice("d1");
- var preRan = false;
-
- device.AddPreActivationAction(() => preRan = true);
- device.PostActivate();
-
- preRan.Should().BeFalse();
- }
-
- [Fact]
- public void PostActivationActions_DoNotRunOnPreActivate()
- {
- var device = new TestDevice("d1");
- var postRan = false;
-
- device.AddPostActivationAction(() => postRan = true);
- device.PreActivate();
-
- postRan.Should().BeFalse();
- }
-
- // -----------------------------------------------------------------------
- // OnFalse
- // -----------------------------------------------------------------------
-
- [Fact]
- public void OnFalse_FiresAction_WhenBoolIsFalse()
- {
- var device = new Device("d1");
- var fired = false;
-
- device.OnFalse(false, () => fired = true);
-
- fired.Should().BeTrue();
- }
-
- [Fact]
- public void OnFalse_DoesNotFireAction_WhenBoolIsTrue()
- {
- var device = new Device("d1");
- var fired = false;
-
- device.OnFalse(true, () => fired = true);
-
- fired.Should().BeFalse();
- }
-
- [Fact]
- public void OnFalse_DoesNotFireAction_ForNonBoolType()
- {
- var device = new Device("d1");
- var fired = false;
-
- device.OnFalse("not a bool", () => fired = true);
- device.OnFalse(0, () => fired = true);
- device.OnFalse(null!, () => fired = true);
-
- fired.Should().BeFalse();
- }
-
- // -----------------------------------------------------------------------
- // Helpers
- // -----------------------------------------------------------------------
-
- ///
- /// Exposes protected Device members so test methods can call them directly.
- ///
- private class TestDevice : Device
- {
- public TestDevice(string key) : base(key) { }
- public TestDevice(string key, string name) : base(key, name) { }
-
- public void AddPreActivationAction(Action act) => base.AddPreActivationAction(act);
- public void AddPostActivationAction(Action act) => base.AddPostActivationAction(act);
- public bool CallCustomActivate() => base.CustomActivate();
- public void CallInitialize() => base.Initialize();
- }
-
- ///
- /// Records whether CustomActivate was invoked and returns a configured result.
- /// Used to verify Activate() correctly delegates to CustomActivate().
- ///
- private sealed class ActivateTrackingDevice : Device
- {
- private readonly bool _result;
- public bool CustomActivateCalled { get; private set; }
-
- public ActivateTrackingDevice(string key, bool result = true) : base(key)
- {
- _result = result;
- }
-
- protected override bool CustomActivate()
- {
- CustomActivateCalled = true;
- return _result;
- }
- }
-}
diff --git a/src/PepperDash.Core.Tests/Fakes/CrestronConsoleFakes.cs b/src/PepperDash.Core.Tests/Fakes/CrestronConsoleFakes.cs
deleted file mode 100644
index afd5c324..00000000
--- a/src/PepperDash.Core.Tests/Fakes/CrestronConsoleFakes.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using PepperDash.Core.Abstractions;
-
-namespace PepperDash.Core.Tests.Fakes;
-
-///
-/// No-op ICrestronConsole that captures output for test assertions.
-///
-public class CapturingCrestronConsole : ICrestronConsole
-{
- public List Lines { get; } = new();
- public List CommandResponses { get; } = new();
- public List<(string Command, string HelpText)> RegisteredCommands { get; } = new();
-
- public void PrintLine(string message) => Lines.Add(message);
- public void Print(string message) => Lines.Add(message);
- public void ConsoleCommandResponse(string message) => CommandResponses.Add(message);
-
- public void AddNewConsoleCommand(
- Action callback,
- string command,
- string helpText,
- ConsoleAccessLevel accessLevel)
- {
- RegisteredCommands.Add((command, helpText));
- }
-}
-
-///
-/// Minimal no-op ICrestronConsole that discards all output. Useful when you only
-/// care about the system under test and not what it logs.
-///
-public class NoOpCrestronConsole : ICrestronConsole
-{
- public void PrintLine(string message) { }
- public void Print(string message) { }
- public void ConsoleCommandResponse(string message) { }
- public void AddNewConsoleCommand(Action _, string __, string ___, ConsoleAccessLevel ____) { }
-}
diff --git a/src/PepperDash.Core.Tests/Fakes/CrestronEnvironmentFakes.cs b/src/PepperDash.Core.Tests/Fakes/CrestronEnvironmentFakes.cs
deleted file mode 100644
index 1792206d..00000000
--- a/src/PepperDash.Core.Tests/Fakes/CrestronEnvironmentFakes.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using PepperDash.Core.Abstractions;
-
-namespace PepperDash.Core.Tests.Fakes;
-
-///
-/// Configurable ICrestronEnvironment for unit tests.
-/// Defaults: Appliance / SimplSharpPro / ApplicationNumber=1.
-///
-public class FakeCrestronEnvironment : ICrestronEnvironment
-{
- public DevicePlatform DevicePlatform { get; set; } = DevicePlatform.Appliance;
- public RuntimeEnvironment RuntimeEnvironment { get; set; } = RuntimeEnvironment.SimplSharpPro;
- public string NewLine { get; set; } = "\r\n";
- public uint ApplicationNumber { get; set; } = 1;
- public uint RoomId { get; set; } = 0;
-
- public event EventHandler? ProgramStatusChanged;
- public event EventHandler? EthernetEventReceived;
-
- public string GetApplicationRootDirectory() => System.IO.Path.GetTempPath();
-
- ///
- public bool IsHardwareRuntime => false;
-
- /// Simulates a program status event for tests.
- public void RaiseProgramStatus(ProgramStatusEventType type) =>
- ProgramStatusChanged?.Invoke(this, new ProgramStatusEventArgs(type));
-
- /// Simulates an ethernet event for tests.
- public void RaiseEthernetEvent(EthernetEventType type, short adapter = 0) =>
- EthernetEventReceived?.Invoke(this, new PepperDashEthernetEventArgs(type, adapter));
-}
-
-///
-/// No-op IEthernetHelper that returns configurable values.
-///
-public class FakeEthernetHelper : IEthernetHelper
-{
- private readonly Dictionary _values = new();
-
- public FakeEthernetHelper Seed(EthernetParameterType param, string value)
- {
- _values[param] = value;
- return this;
- }
-
- public string GetEthernetParameter(EthernetParameterType parameter, short ethernetAdapterId) =>
- _values.TryGetValue(parameter, out var v) ? v : string.Empty;
-}
diff --git a/src/PepperDash.Core.Tests/Fakes/InMemoryCrestronDataStore.cs b/src/PepperDash.Core.Tests/Fakes/InMemoryCrestronDataStore.cs
deleted file mode 100644
index 7cc2b618..00000000
--- a/src/PepperDash.Core.Tests/Fakes/InMemoryCrestronDataStore.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using PepperDash.Core.Abstractions;
-
-namespace PepperDash.Core.Tests.Fakes;
-
-///
-/// In-memory ICrestronDataStore backed by a dictionary.
-/// Use in unit tests to verify that keys are read from and written to the store correctly.
-///
-public class InMemoryCrestronDataStore : ICrestronDataStore
-{
- private readonly Dictionary _store = new();
-
- public bool Initialized { get; private set; }
-
- public void InitStore() => Initialized = true;
-
- public bool TryGetLocalInt(string key, out int value)
- {
- if (_store.TryGetValue(key, out var raw) && raw is int i)
- {
- value = i;
- return true;
- }
- value = 0;
- return false;
- }
-
- public bool SetLocalInt(string key, int value)
- {
- _store[key] = value;
- return true;
- }
-
- public bool SetLocalUint(string key, uint value)
- {
- _store[key] = (int)value;
- return true;
- }
-
- public bool TryGetLocalBool(string key, out bool value)
- {
- if (_store.TryGetValue(key, out var raw) && raw is bool b)
- {
- value = b;
- return true;
- }
- value = false;
- return false;
- }
-
- public bool SetLocalBool(string key, bool value)
- {
- _store[key] = value;
- return true;
- }
-
- /// Seeds a key for testing read paths.
- public void Seed(string key, object value) => _store[key] = value;
-}
diff --git a/src/PepperDash.Core.Tests/Logging/DebugServiceTests.cs b/src/PepperDash.Core.Tests/Logging/DebugServiceTests.cs
deleted file mode 100644
index 13ae0f9e..00000000
--- a/src/PepperDash.Core.Tests/Logging/DebugServiceTests.cs
+++ /dev/null
@@ -1,171 +0,0 @@
-using FluentAssertions;
-using PepperDash.Core.Abstractions;
-using PepperDash.Core.Tests.Fakes;
-using Xunit;
-
-namespace PepperDash.Core.Tests.Logging;
-
-///
-/// Tests for Debug-related service interfaces and implementations.
-/// These tests verify the behaviour of the abstractions in isolation (no Crestron SDK required).
-///
-public class DebugServiceTests
-{
- // -----------------------------------------------------------------------
- // ICrestronDataStore — InMemoryCrestronDataStore
- // -----------------------------------------------------------------------
-
- [Fact]
- public void DataStore_InitStore_SetsInitializedFlag()
- {
- var store = new InMemoryCrestronDataStore();
- store.Initialized.Should().BeFalse("not yet initialized");
-
- store.InitStore();
-
- store.Initialized.Should().BeTrue();
- }
-
- [Fact]
- public void DataStore_SetAndGetLocalInt_RoundTrips()
- {
- var store = new InMemoryCrestronDataStore();
-
- store.SetLocalInt("MyKey", 42).Should().BeTrue();
- store.TryGetLocalInt("MyKey", out var value).Should().BeTrue();
- value.Should().Be(42);
- }
-
- [Fact]
- public void DataStore_TryGetLocalInt_ReturnsFalse_WhenKeyAbsent()
- {
- var store = new InMemoryCrestronDataStore();
-
- store.TryGetLocalInt("Missing", out var value).Should().BeFalse();
- value.Should().Be(0);
- }
-
- [Fact]
- public void DataStore_SetAndGetLocalBool_RoundTrips()
- {
- var store = new InMemoryCrestronDataStore();
-
- store.SetLocalBool("FlagKey", true).Should().BeTrue();
- store.TryGetLocalBool("FlagKey", out var value).Should().BeTrue();
- value.Should().BeTrue();
- }
-
- [Fact]
- public void DataStore_TryGetLocalBool_ReturnsFalse_WhenKeyAbsent()
- {
- var store = new InMemoryCrestronDataStore();
-
- store.TryGetLocalBool("Missing", out var value).Should().BeFalse();
- value.Should().BeFalse();
- }
-
- [Fact]
- public void DataStore_SetLocalUint_CanBeReadBackAsInt()
- {
- var store = new InMemoryCrestronDataStore();
-
- store.SetLocalUint("UintKey", 3u).Should().BeTrue();
- store.TryGetLocalInt("UintKey", out var value).Should().BeTrue();
- value.Should().Be(3);
- }
-
- [Fact]
- public void DataStore_Seed_AllowsTestSetupOfReadPaths()
- {
- var store = new InMemoryCrestronDataStore();
- store.Seed("MyLevel", 2);
-
- store.TryGetLocalInt("MyLevel", out var level).Should().BeTrue();
- level.Should().Be(2);
- }
-
- // -----------------------------------------------------------------------
- // DebugServiceRegistration
- // -----------------------------------------------------------------------
-
- [Fact]
- public void ServiceRegistration_Register_StoresAllThreeServices()
- {
- var env = new FakeCrestronEnvironment();
- var console = new NoOpCrestronConsole();
- var store = new InMemoryCrestronDataStore();
-
- DebugServiceRegistration.Register(env, console, store);
-
- DebugServiceRegistration.Environment.Should().BeSameAs(env);
- DebugServiceRegistration.Console.Should().BeSameAs(console);
- DebugServiceRegistration.DataStore.Should().BeSameAs(store);
- }
-
- [Fact]
- public void ServiceRegistration_Register_AcceptsNullsWithoutThrowing()
- {
- var act = () => DebugServiceRegistration.Register(null, null, null);
- act.Should().NotThrow();
- }
-
- // -----------------------------------------------------------------------
- // ICrestronEnvironment — FakeCrestronEnvironment
- // -----------------------------------------------------------------------
-
- [Fact]
- public void FakeEnvironment_DefaultsToAppliance()
- {
- var env = new FakeCrestronEnvironment();
- env.DevicePlatform.Should().Be(DevicePlatform.Appliance);
- }
-
- [Fact]
- public void FakeEnvironment_RaiseProgramStatus_FiresEvent()
- {
- var env = new FakeCrestronEnvironment();
- ProgramStatusEventType? received = null;
- env.ProgramStatusChanged += (_, e) => received = e.EventType;
-
- env.RaiseProgramStatus(ProgramStatusEventType.Stopping);
-
- received.Should().Be(ProgramStatusEventType.Stopping);
- }
-
- [Fact]
- public void FakeEnvironment_RaiseEthernetEvent_FiresEvent()
- {
- var env = new FakeCrestronEnvironment();
- EthernetEventType? received = null;
- env.EthernetEventReceived += (_, e) => received = e.EthernetEventType;
-
- env.RaiseEthernetEvent(EthernetEventType.LinkUp, adapter: 0);
-
- received.Should().Be(EthernetEventType.LinkUp);
- }
-
- // -----------------------------------------------------------------------
- // ICrestronConsole — CapturingCrestronConsole
- // -----------------------------------------------------------------------
-
- [Fact]
- public void CapturingConsole_PrintLine_CapturesMessage()
- {
- var console = new CapturingCrestronConsole();
-
- console.PrintLine("hello world");
-
- console.Lines.Should().ContainSingle().Which.Should().Be("hello world");
- }
-
- [Fact]
- public void CapturingConsole_AddNewConsoleCommand_RecordsCommandName()
- {
- var console = new CapturingCrestronConsole();
-
- console.AddNewConsoleCommand(_ => { }, "appdebug", "Sets debug level", ConsoleAccessLevel.AccessOperator);
-
- console.RegisteredCommands.Should().ContainSingle()
- .Which.Command.Should().Be("appdebug");
- }
-}
diff --git a/src/PepperDash.Core.Tests/PepperDash.Core.Tests.csproj b/src/PepperDash.Core.Tests/PepperDash.Core.Tests.csproj
deleted file mode 100644
index dd131cf1..00000000
--- a/src/PepperDash.Core.Tests/PepperDash.Core.Tests.csproj
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
- net9.0
- enable
- enable
- false
- false
- false
-
-
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/PepperDash.Core.Tests/TestInitializer.cs b/src/PepperDash.Core.Tests/TestInitializer.cs
deleted file mode 100644
index 6455ea59..00000000
--- a/src/PepperDash.Core.Tests/TestInitializer.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System.Runtime.CompilerServices;
-using PepperDash.Core.Abstractions;
-using PepperDash.Core.Tests.Fakes;
-
-namespace PepperDash.Core.Tests;
-
-///
-/// Runs once before any type in this assembly is accessed.
-/// Registers fake Crestron service implementations with
-/// so that the Debug static constructor uses them instead of the real Crestron SDK.
-/// This must remain a module initializer (not a test fixture) because the static constructor
-/// fires the first time any type in PepperDash.Core is referenced — before xUnit
-/// has a chance to run fixture setup code.
-///
-internal static class TestInitializer
-{
- [ModuleInitializer]
- internal static void Initialize()
- {
- DebugServiceRegistration.Register(
- new FakeCrestronEnvironment
- {
- DevicePlatform = DevicePlatform.Server, // avoids any appliance-only code paths
- RuntimeEnvironment = RuntimeEnvironment.Other, // skips console command registration
- },
- new NoOpCrestronConsole(),
- new InMemoryCrestronDataStore());
- }
-}
diff --git a/src/PepperDash.Core/Adapters/CrestronConsoleAdapter.cs b/src/PepperDash.Core/Adapters/CrestronConsoleAdapter.cs
deleted file mode 100644
index 8c4a6faf..00000000
--- a/src/PepperDash.Core/Adapters/CrestronConsoleAdapter.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System;
-using Crestron.SimplSharp;
-using PdCore = PepperDash.Core.Abstractions;
-
-namespace PepperDash.Core.Adapters;
-
-///
-/// Production adapter — delegates ICrestronConsole calls to the real Crestron SDK.
-///
-public sealed class CrestronConsoleAdapter : PdCore.ICrestronConsole
-{
- public void PrintLine(string message) => CrestronConsole.PrintLine(message);
-
- public void Print(string message) => CrestronConsole.Print(message);
-
- public void ConsoleCommandResponse(string message) =>
- CrestronConsole.ConsoleCommandResponse(message);
-
- public void AddNewConsoleCommand(
- Action callback,
- string command,
- string helpText,
- PdCore.ConsoleAccessLevel accessLevel)
- {
- var crestronLevel = accessLevel switch
- {
- PdCore.ConsoleAccessLevel.AccessAdministrator => ConsoleAccessLevelEnum.AccessAdministrator,
- PdCore.ConsoleAccessLevel.AccessProgrammer => ConsoleAccessLevelEnum.AccessProgrammer,
- _ => ConsoleAccessLevelEnum.AccessOperator,
- };
-
- // Wrap Action in a lambda — Crestron's delegate is not a standard Action.
- CrestronConsole.AddNewConsoleCommand(s => callback(s), command, helpText, crestronLevel);
- }
-}
diff --git a/src/PepperDash.Core/Adapters/CrestronDataStoreAdapter.cs b/src/PepperDash.Core/Adapters/CrestronDataStoreAdapter.cs
deleted file mode 100644
index effed284..00000000
--- a/src/PepperDash.Core/Adapters/CrestronDataStoreAdapter.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using Crestron.SimplSharp.CrestronDataStore;
-using PepperDash.Core.Abstractions;
-
-namespace PepperDash.Core.Adapters;
-
-///
-/// Production adapter — delegates ICrestronDataStore calls to the real Crestron SDK.
-///
-public sealed class CrestronDataStoreAdapter : ICrestronDataStore
-{
- public void InitStore() => CrestronDataStoreStatic.InitCrestronDataStore();
-
- public bool TryGetLocalInt(string key, out int value)
- {
- var err = CrestronDataStoreStatic.GetLocalIntValue(key, out value);
- return err == CrestronDataStore.CDS_ERROR.CDS_SUCCESS;
- }
-
- public bool SetLocalInt(string key, int value)
- {
- var err = CrestronDataStoreStatic.SetLocalIntValue(key, value);
- return err == CrestronDataStore.CDS_ERROR.CDS_SUCCESS;
- }
-
- public bool SetLocalUint(string key, uint value)
- {
- var err = CrestronDataStoreStatic.SetLocalUintValue(key, value);
- return err == CrestronDataStore.CDS_ERROR.CDS_SUCCESS;
- }
-
- public bool TryGetLocalBool(string key, out bool value)
- {
- var err = CrestronDataStoreStatic.GetLocalBoolValue(key, out value);
- return err == CrestronDataStore.CDS_ERROR.CDS_SUCCESS;
- }
-
- public bool SetLocalBool(string key, bool value)
- {
- var err = CrestronDataStoreStatic.SetLocalBoolValue(key, value);
- return err == CrestronDataStore.CDS_ERROR.CDS_SUCCESS;
- }
-}
diff --git a/src/PepperDash.Core/Adapters/CrestronEnvironmentAdapter.cs b/src/PepperDash.Core/Adapters/CrestronEnvironmentAdapter.cs
deleted file mode 100644
index 62002d73..00000000
--- a/src/PepperDash.Core/Adapters/CrestronEnvironmentAdapter.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using System;
-using Crestron.SimplSharp;
-using PdCore = PepperDash.Core.Abstractions;
-
-namespace PepperDash.Core.Adapters;
-
-///
-/// Production adapter — delegates ICrestronEnvironment calls to the real Crestron SDK.
-///
-public sealed class CrestronEnvironmentAdapter : PdCore.ICrestronEnvironment
-{
- // Subscribe once in constructor and re-raise as our event types.
- private event EventHandler? _programStatusChanged;
- private event EventHandler? _ethernetEventReceived;
-
- public CrestronEnvironmentAdapter()
- {
- CrestronEnvironment.ProgramStatusEventHandler += type =>
- _programStatusChanged?.Invoke(this, new PdCore.ProgramStatusEventArgs(MapProgramStatus(type)));
-
- CrestronEnvironment.EthernetEventHandler += args =>
- _ethernetEventReceived?.Invoke(this, new PdCore.PepperDashEthernetEventArgs(
- args.EthernetEventType == eEthernetEventType.LinkDown
- ? PdCore.EthernetEventType.LinkDown
- : PdCore.EthernetEventType.LinkUp,
- (short)args.EthernetAdapter));
- }
-
- public PdCore.DevicePlatform DevicePlatform =>
- CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance
- ? PdCore.DevicePlatform.Appliance
- : PdCore.DevicePlatform.Server;
-
- public PdCore.RuntimeEnvironment RuntimeEnvironment =>
- CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro
- ? PdCore.RuntimeEnvironment.SimplSharpPro
- : PdCore.RuntimeEnvironment.Other;
-
- public string NewLine => CrestronEnvironment.NewLine;
-
- public uint ApplicationNumber => InitialParametersClass.ApplicationNumber;
-
- public uint RoomId => uint.TryParse(InitialParametersClass.RoomId, out var r) ? r : 0;
-
- public event EventHandler ProgramStatusChanged
- {
- add => _programStatusChanged += value;
- remove => _programStatusChanged -= value;
- }
-
- public event EventHandler EthernetEventReceived
- {
- add => _ethernetEventReceived += value;
- remove => _ethernetEventReceived -= value;
- }
-
- public string GetApplicationRootDirectory() =>
- Crestron.SimplSharp.CrestronIO.Directory.GetApplicationRootDirectory();
-
- ///
- public bool IsHardwareRuntime => true;
-
- private static PdCore.ProgramStatusEventType MapProgramStatus(eProgramStatusEventType type) =>
- type switch
- {
- eProgramStatusEventType.Stopping => PdCore.ProgramStatusEventType.Stopping,
- eProgramStatusEventType.Paused => PdCore.ProgramStatusEventType.Paused,
- eProgramStatusEventType.Resumed => PdCore.ProgramStatusEventType.Resumed,
- _ => PdCore.ProgramStatusEventType.Starting,
- };
-}
diff --git a/src/PepperDash.Core/Adapters/CrestronEthernetAdapter.cs b/src/PepperDash.Core/Adapters/CrestronEthernetAdapter.cs
deleted file mode 100644
index f2c80e5b..00000000
--- a/src/PepperDash.Core/Adapters/CrestronEthernetAdapter.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System;
-using Crestron.SimplSharp;
-using PepperDash.Core.Abstractions;
-
-namespace PepperDash.Core.Adapters;
-
-///
-/// Production adapter — delegates IEthernetHelper calls to the real Crestron SDK.
-///
-public sealed class CrestronEthernetAdapter : IEthernetHelper
-{
- public string GetEthernetParameter(EthernetParameterType parameter, short ethernetAdapterId)
- {
- var crestronParam = parameter switch
- {
- EthernetParameterType.GetCurrentIpAddress =>
- CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS,
- EthernetParameterType.GetHostname =>
- CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME,
- EthernetParameterType.GetDomainName =>
- CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME,
- EthernetParameterType.GetLinkStatus =>
- CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_LINK_STATUS,
- EthernetParameterType.GetCurrentDhcpState =>
- CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_DHCP_STATE,
- EthernetParameterType.GetCurrentIpMask =>
- CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK,
- EthernetParameterType.GetCurrentRouter =>
- CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER,
- EthernetParameterType.GetMacAddress =>
- CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS,
- EthernetParameterType.GetDnsServer =>
- CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER,
- _ => throw new ArgumentOutOfRangeException(nameof(parameter), parameter, null),
- };
-
- return CrestronEthernetHelper.GetEthernetParameter(crestronParam, ethernetAdapterId);
- }
-}
diff --git a/src/PepperDash.Core/ComTextHelper.cs b/src/PepperDash.Core/ComTextHelper.cs
new file mode 100644
index 00000000..28a76975
--- /dev/null
+++ b/src/PepperDash.Core/ComTextHelper.cs
@@ -0,0 +1,43 @@
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace PepperDash.Core
+{
+ ///
+ /// Helper class for formatting communication text and byte data for debugging purposes.
+ ///
+ public class ComTextHelper
+ {
+ ///
+ /// Gets escaped text for a byte array
+ ///
+ ///
+ /// string with all bytes escaped
+ public static string GetEscapedText(byte[] bytes)
+ {
+ return string.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
+ }
+
+ ///
+ /// Gets escaped text for a string
+ ///
+ ///
+ /// string with all bytes escaped
+ public static string GetEscapedText(string text)
+ {
+ var bytes = Encoding.GetEncoding(28591).GetBytes(text);
+ return string.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
+ }
+
+ ///
+ /// Gets debug text for a string
+ ///
+ ///
+ /// string with all non-printable characters escaped
+ public static string GetDebugText(string text)
+ {
+ return Regex.Replace(text, @"[^\u0020-\u007E]", a => GetEscapedText(a.Value));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Comm/CommunicationGather.cs b/src/PepperDash.Core/Comm/CommunicationGather.cs
index 4131d364..e75a012d 100644
--- a/src/PepperDash.Core/Comm/CommunicationGather.cs
+++ b/src/PepperDash.Core/Comm/CommunicationGather.cs
@@ -8,8 +8,8 @@ using Crestron.SimplSharp;
using PepperDash.Core;
-namespace PepperDash.Core;
-
+namespace PepperDash.Core
+{
///
/// Defines the string event handler for line events on the gather
///
@@ -30,7 +30,7 @@ namespace PepperDash.Core;
///
/// The communication port that this gathers on
///
- public ICommunicationReceiver Port { get; private set; }
+ public ICommunicationReceiver Port { get; private set; }
///
/// Default false. If true, the delimiter will be included in the line output
@@ -67,22 +67,22 @@ namespace PepperDash.Core;
///
///
///
- public CommunicationGather(ICommunicationReceiver port, string delimiter)
- :this(port, new string[] { delimiter} )
+ public CommunicationGather(ICommunicationReceiver port, string delimiter)
+ :this(port, new string[] { delimiter} )
{
}
- ///
- /// Constructor for using an array of string delimiters
- ///
- ///
- ///
- public CommunicationGather(ICommunicationReceiver port, string[] delimiters)
- {
- Port = port;
- StringDelimiters = delimiters;
- port.TextReceived += Port_TextReceivedStringDelimiter;
- }
+ ///
+ /// Constructor for using an array of string delimiters
+ ///
+ ///
+ ///
+ public CommunicationGather(ICommunicationReceiver port, string[] delimiters)
+ {
+ Port = port;
+ StringDelimiters = delimiters;
+ port.TextReceived += Port_TextReceivedStringDelimiter;
+ }
///
/// Stop method
@@ -135,35 +135,35 @@ namespace PepperDash.Core;
ReceiveBuffer.Append(args.Text);
var str = ReceiveBuffer.ToString();
- // Case: Receiving DEVICE get version\x0d\0x0a+OK "value":"1234"\x0d\x0a
+ // Case: Receiving DEVICE get version\x0d\0x0a+OK "value":"1234"\x0d\x0a
- // RX: DEV
- // Split: (1) "DEV"
- // RX: I
- // Split: (1) "DEVI"
- // RX: CE get version
- // Split: (1) "DEVICE get version"
- // RX: \x0d\x0a+OK "value":"1234"\x0d\x0a
- // Split: (2) DEVICE get version, +OK "value":"1234"
+ // RX: DEV
+ // Split: (1) "DEV"
+ // RX: I
+ // Split: (1) "DEVI"
+ // RX: CE get version
+ // Split: (1) "DEVICE get version"
+ // RX: \x0d\x0a+OK "value":"1234"\x0d\x0a
+ // Split: (2) DEVICE get version, +OK "value":"1234"
- // Iterate the delimiters and fire an event for any matching delimiter
- foreach (var delimiter in StringDelimiters)
- {
- var lines = Regex.Split(str, delimiter);
- if (lines.Length == 1)
- continue;
-
- for (int i = 0; i < lines.Length - 1; i++)
+ // Iterate the delimiters and fire an event for any matching delimiter
+ foreach (var delimiter in StringDelimiters)
{
- string strToSend = null;
- if (IncludeDelimiter)
- strToSend = lines[i] + delimiter;
- else
- strToSend = lines[i];
- handler(this, new GenericCommMethodReceiveTextArgs(strToSend, delimiter));
+ var lines = Regex.Split(str, delimiter);
+ if (lines.Length == 1)
+ continue;
+
+ for (int i = 0; i < lines.Length - 1; i++)
+ {
+ string strToSend = null;
+ if (IncludeDelimiter)
+ strToSend = lines[i] + delimiter;
+ else
+ strToSend = lines[i];
+ handler(this, new GenericCommMethodReceiveTextArgs(strToSend, delimiter));
+ }
+ ReceiveBuffer = new StringBuilder(lines[lines.Length - 1]);
}
- ReceiveBuffer = new StringBuilder(lines[lines.Length - 1]);
- }
}
}
@@ -174,4 +174,5 @@ namespace PepperDash.Core;
{
Stop();
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Comm/CommunicationStreamDebugging.cs b/src/PepperDash.Core/Comm/CommunicationStreamDebugging.cs
index d2da3257..780e65d0 100644
--- a/src/PepperDash.Core/Comm/CommunicationStreamDebugging.cs
+++ b/src/PepperDash.Core/Comm/CommunicationStreamDebugging.cs
@@ -1,159 +1,138 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
using System.Text;
-using System.Timers;
+using Crestron.SimplSharp;
using PepperDash.Core;
-namespace PepperDash.Core;
-
-///
-/// Controls the ability to disable/enable debugging of TX/RX data sent to/from a device with a built in timer to disable
-///
-public class CommunicationStreamDebugging
+namespace PepperDash.Core
{
///
- /// Device Key that this instance configures
+ /// Controls the ability to disable/enable debugging of TX/RX data sent to/from a device with a built in timer to disable
///
- public string ParentDeviceKey { get; private set; }
-
- ///
- /// Timer to disable automatically if not manually disabled
- ///
- private Timer DebugExpiryPeriod;
-
- ///
- /// The current debug setting
- ///
- public eStreamDebuggingSetting DebugSetting { get; private set; }
-
- private uint _DebugTimeoutInMs;
- private const uint _DefaultDebugTimeoutMin = 30;
-
- ///
- /// Timeout in Minutes
- ///
- public uint DebugTimeoutMinutes
+ public class CommunicationStreamDebugging
{
- get
+ ///
+ /// Device Key that this instance configures
+ ///
+ public string ParentDeviceKey { get; private set; }
+
+ ///
+ /// Timer to disable automatically if not manually disabled
+ ///
+ private CTimer DebugExpiryPeriod;
+
+ ///
+ /// Gets or sets the DebugSetting
+ ///
+ public eStreamDebuggingSetting DebugSetting { get; private set; }
+
+ private uint _DebugTimeoutInMs;
+ private const uint _DefaultDebugTimeoutMin = 30;
+
+ ///
+ /// Timeout in Minutes
+ ///
+ public uint DebugTimeoutMinutes
{
- return _DebugTimeoutInMs/60000;
- }
- }
-
- ///
- /// Indicates that receive stream debugging is enabled
- ///
- public bool RxStreamDebuggingIsEnabled{ get; private set; }
-
- ///
- /// Indicates that transmit stream debugging is enabled
- ///
- public bool TxStreamDebuggingIsEnabled { get; private set; }
-
- ///
- /// Constructor
- ///
- ///
- public CommunicationStreamDebugging(string parentDeviceKey)
- {
- ParentDeviceKey = parentDeviceKey;
- }
-
-
- ///
- /// Sets the debugging setting and if not setting to off, assumes the default of 30 mintues
- ///
- ///
- public void SetDebuggingWithDefaultTimeout(eStreamDebuggingSetting setting)
- {
- if (setting == eStreamDebuggingSetting.Off)
- {
- DisableDebugging();
- return;
+ get
+ {
+ return _DebugTimeoutInMs / 60000;
+ }
}
- SetDebuggingWithSpecificTimeout(setting, _DefaultDebugTimeoutMin);
- }
+ ///
+ /// Gets or sets the RxStreamDebuggingIsEnabled
+ ///
+ public bool RxStreamDebuggingIsEnabled { get; private set; }
- ///
- /// Sets the debugging setting for the specified number of minutes
- ///
- ///
- ///
- public void SetDebuggingWithSpecificTimeout(eStreamDebuggingSetting setting, uint minutes)
- {
- if (setting == eStreamDebuggingSetting.Off)
+ ///
+ /// Indicates that transmit stream debugging is enabled
+ ///
+ public bool TxStreamDebuggingIsEnabled { get; private set; }
+
+ ///
+ /// Constructor
+ ///
+ ///
+ public CommunicationStreamDebugging(string parentDeviceKey)
{
- DisableDebugging();
- return;
+ ParentDeviceKey = parentDeviceKey;
}
- _DebugTimeoutInMs = minutes * 60000;
- StopDebugTimer();
-
- DebugExpiryPeriod = new Timer(_DebugTimeoutInMs) { AutoReset = false };
- DebugExpiryPeriod.Elapsed += (s, e) => DisableDebugging();
- DebugExpiryPeriod.Start();
-
- if ((setting & eStreamDebuggingSetting.Rx) == eStreamDebuggingSetting.Rx)
- RxStreamDebuggingIsEnabled = true;
-
- if ((setting & eStreamDebuggingSetting.Tx) == eStreamDebuggingSetting.Tx)
- TxStreamDebuggingIsEnabled = true;
-
- Debug.SetDeviceDebugSettings(ParentDeviceKey, setting);
-
- }
-
- ///
- /// Disabled debugging
- ///
- private void DisableDebugging()
- {
- StopDebugTimer();
-
- Debug.SetDeviceDebugSettings(ParentDeviceKey, eStreamDebuggingSetting.Off);
- }
-
- private void StopDebugTimer()
- {
- RxStreamDebuggingIsEnabled = false;
- TxStreamDebuggingIsEnabled = false;
-
- if (DebugExpiryPeriod == null)
+ ///
+ /// Sets the debugging setting and if not setting to off, assumes the default of 30 mintues
+ ///
+ ///
+ ///
+ /// SetDebuggingWithDefaultTimeout method
+ ///
+ public void SetDebuggingWithDefaultTimeout(eStreamDebuggingSetting setting)
{
- return;
+ if (setting == eStreamDebuggingSetting.Off)
+ {
+ DisableDebugging();
+ return;
+ }
+
+ SetDebuggingWithSpecificTimeout(setting, _DefaultDebugTimeoutMin);
}
- DebugExpiryPeriod.Stop();
- DebugExpiryPeriod.Dispose();
- DebugExpiryPeriod = null;
+ ///
+ /// Sets the debugging setting for the specified number of minutes
+ ///
+ ///
+ ///
+ ///
+ /// SetDebuggingWithSpecificTimeout method
+ ///
+ public void SetDebuggingWithSpecificTimeout(eStreamDebuggingSetting setting, uint minutes)
+ {
+ if (setting == eStreamDebuggingSetting.Off)
+ {
+ DisableDebugging();
+ return;
+ }
+
+ _DebugTimeoutInMs = minutes * 60000;
+
+ StopDebugTimer();
+
+ DebugExpiryPeriod = new CTimer((o) => DisableDebugging(), _DebugTimeoutInMs);
+
+ if ((setting & eStreamDebuggingSetting.Rx) == eStreamDebuggingSetting.Rx)
+ RxStreamDebuggingIsEnabled = true;
+
+ if ((setting & eStreamDebuggingSetting.Tx) == eStreamDebuggingSetting.Tx)
+ TxStreamDebuggingIsEnabled = true;
+
+ Debug.SetDeviceDebugSettings(ParentDeviceKey, setting);
+
+ }
+
+ ///
+ /// Disabled debugging
+ ///
+ private void DisableDebugging()
+ {
+ StopDebugTimer();
+
+ Debug.SetDeviceDebugSettings(ParentDeviceKey, eStreamDebuggingSetting.Off);
+ }
+
+ private void StopDebugTimer()
+ {
+ RxStreamDebuggingIsEnabled = false;
+ TxStreamDebuggingIsEnabled = false;
+
+ if (DebugExpiryPeriod == null)
+ {
+ return;
+ }
+
+ DebugExpiryPeriod.Stop();
+ DebugExpiryPeriod.Dispose();
+ DebugExpiryPeriod = null;
+ }
}
}
-
-///
-/// The available settings for stream debugging
-///
-[Flags]
-public enum eStreamDebuggingSetting
-{
- ///
- /// Debug off
- ///
- Off = 0,
- ///
- /// Debug received data
- ///
- Rx = 1,
- ///
- /// Debug transmitted data
- ///
- Tx = 2,
- ///
- /// Debug both received and transmitted data
- ///
- Both = Rx | Tx
-}
-
diff --git a/src/PepperDash.Core/Comm/ControlPropertiesConfig.cs b/src/PepperDash.Core/Comm/ControlPropertiesConfig.cs
index e359eade..c665dad8 100644
--- a/src/PepperDash.Core/Comm/ControlPropertiesConfig.cs
+++ b/src/PepperDash.Core/Comm/ControlPropertiesConfig.cs
@@ -1,97 +1,93 @@
-extern alias NewtonsoftJson;
-
-using System;
+using System;
using Crestron.SimplSharp;
-using JsonConverter = NewtonsoftJson::Newtonsoft.Json.JsonConverterAttribute;
-using JsonIgnore = NewtonsoftJson::Newtonsoft.Json.JsonIgnoreAttribute;
-using JsonProperty = NewtonsoftJson::Newtonsoft.Json.JsonPropertyAttribute;
-using NullValueHandling = NewtonsoftJson::Newtonsoft.Json.NullValueHandling;
-using StringEnumConverter = NewtonsoftJson::Newtonsoft.Json.Converters.StringEnumConverter;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
-namespace PepperDash.Core;
-
-///
-/// Config properties that indicate how to communicate with a device for control
-///
-public class ControlPropertiesConfig
+namespace PepperDash.Core
{
///
- /// The method of control
+ /// Represents a ControlPropertiesConfig
///
- [JsonProperty("method")]
- [JsonConverter(typeof(StringEnumConverter))]
- public eControlMethod Method { get; set; }
+ public class ControlPropertiesConfig
+ {
+ ///
+ /// The method of control
+ ///
+ [JsonProperty("method")]
+ [JsonConverter(typeof(StringEnumConverter))]
+ public eControlMethod Method { get; set; }
- ///
- /// The key of the device that contains the control port
- ///
- [JsonProperty("controlPortDevKey", NullValueHandling = NullValueHandling.Ignore)]
- public string ControlPortDevKey { get; set; }
+ ///
+ /// The key of the device that contains the control port
+ ///
+ [JsonProperty("controlPortDevKey", NullValueHandling = NullValueHandling.Ignore)]
+ public string ControlPortDevKey { get; set; }
- ///
- /// The number of the control port on the device specified by ControlPortDevKey
- ///
- [JsonProperty("controlPortNumber", NullValueHandling = NullValueHandling.Ignore)] // In case "null" is present in config on this value
- public uint? ControlPortNumber { get; set; }
+ ///
+ /// The number of the control port on the device specified by ControlPortDevKey
+ ///
+ [JsonProperty("controlPortNumber", NullValueHandling = NullValueHandling.Ignore)] // In case "null" is present in config on this value
+ public uint? ControlPortNumber { get; set; }
- ///
- /// The name of the control port on the device specified by ControlPortDevKey
- ///
- [JsonProperty("controlPortName", NullValueHandling = NullValueHandling.Ignore)] // In case "null" is present in config on this value
- public string ControlPortName { get; set; }
+ ///
+ /// The name of the control port on the device specified by ControlPortDevKey
+ ///
+ [JsonProperty("controlPortName", NullValueHandling = NullValueHandling.Ignore)] // In case "null" is present in config on this value
+ public string ControlPortName { get; set; }
- ///
- /// Properties for ethernet based communications
- ///
- [JsonProperty("tcpSshProperties", NullValueHandling = NullValueHandling.Ignore)]
- public TcpSshPropertiesConfig TcpSshProperties { get; set; }
+ ///
+ /// Properties for ethernet based communications
+ ///
+ [JsonProperty("tcpSshProperties", NullValueHandling = NullValueHandling.Ignore)]
+ public TcpSshPropertiesConfig TcpSshProperties { get; set; }
- ///
- /// The filename and path for the IR file
- ///
- [JsonProperty("irFile", NullValueHandling = NullValueHandling.Ignore)]
- public string IrFile { get; set; }
+ ///
+ /// The filename and path for the IR file
+ ///
+ [JsonProperty("irFile", NullValueHandling = NullValueHandling.Ignore)]
+ public string IrFile { get; set; }
- ///
- /// The IpId of a Crestron device
- ///
- [JsonProperty("ipId", NullValueHandling = NullValueHandling.Ignore)]
- public string IpId { get; set; }
+ ///
+ /// The IpId of a Crestron device
+ ///
+ [JsonProperty("ipId", NullValueHandling = NullValueHandling.Ignore)]
+ public string IpId { get; set; }
- ///
- /// Readonly uint representation of the IpId
- ///
- [JsonIgnore]
- public uint IpIdInt { get { return Convert.ToUInt32(IpId, 16); } }
+ ///
+ /// Readonly uint representation of the IpId
+ ///
+ [JsonIgnore]
+ public uint IpIdInt { get { return Convert.ToUInt32(IpId, 16); } }
- ///
- /// Char indicating end of line
- ///
- [JsonProperty("endOfLineChar", NullValueHandling = NullValueHandling.Ignore)]
- public char EndOfLineChar { get; set; }
+ ///
+ /// Char indicating end of line
+ ///
+ [JsonProperty("endOfLineChar", NullValueHandling = NullValueHandling.Ignore)]
+ public char EndOfLineChar { get; set; }
- ///
- /// Defaults to Environment.NewLine;
- ///
- [JsonProperty("endOfLineString", NullValueHandling = NullValueHandling.Ignore)]
- public string EndOfLineString { get; set; }
+ ///
+ /// Defaults to Environment.NewLine;
+ ///
+ [JsonProperty("endOfLineString", NullValueHandling = NullValueHandling.Ignore)]
+ public string EndOfLineString { get; set; }
- ///
- /// Indicates
- ///
- [JsonProperty("deviceReadyResponsePattern", NullValueHandling = NullValueHandling.Ignore)]
- public string DeviceReadyResponsePattern { get; set; }
+ ///
+ /// Indicates
+ ///
+ [JsonProperty("deviceReadyResponsePattern", NullValueHandling = NullValueHandling.Ignore)]
+ public string DeviceReadyResponsePattern { get; set; }
- ///
- /// Used when communcating to programs running in VC-4
- ///
- [JsonProperty("roomId", NullValueHandling = NullValueHandling.Ignore)]
- public string RoomId { get; set; }
+ ///
+ /// Used when communcating to programs running in VC-4
+ ///
+ [JsonProperty("roomId", NullValueHandling = NullValueHandling.Ignore)]
+ public string RoomId { get; set; }
- ///
- /// Constructor
- ///
- public ControlPropertiesConfig()
- {
+ ///
+ /// Constructor
+ ///
+ public ControlPropertiesConfig()
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Comm/EventArgs.cs b/src/PepperDash.Core/Comm/EventArgs.cs
index d68dda7c..0b394423 100644
--- a/src/PepperDash.Core/Comm/EventArgs.cs
+++ b/src/PepperDash.Core/Comm/EventArgs.cs
@@ -16,237 +16,236 @@ using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronSockets;
-namespace PepperDash.Core;
-
-///
-/// Delegate for notifying of socket status changes
-///
-///
-public delegate void GenericSocketStatusChangeEventDelegate(ISocketStatus client);
-
-///
-/// EventArgs class for socket status changes
-///
-public class GenericSocketStatusChageEventArgs : EventArgs
+namespace PepperDash.Core
{
///
- /// Gets or sets the Client
- ///
- public ISocketStatus Client { get; private set; }
-
- ///
- /// Constructor
+ /// Delegate for notifying of socket status changes
///
///
- public GenericSocketStatusChageEventArgs(ISocketStatus client)
- {
- Client = client;
+ public delegate void GenericSocketStatusChangeEventDelegate(ISocketStatus client);
+
+ ///
+ /// EventArgs class for socket status changes
+ ///
+ public class GenericSocketStatusChageEventArgs : EventArgs
+ {
+ ///
+ /// Gets or sets the Client
+ ///
+ public ISocketStatus Client { get; private set; }
+
+ ///
+ ///
+ ///
+ ///
+ public GenericSocketStatusChageEventArgs(ISocketStatus client)
+ {
+ Client = client;
+ }
+ ///
+ /// S+ Constructor
+ ///
+ public GenericSocketStatusChageEventArgs() { }
}
- ///
- /// S+ Constructor
- ///
- public GenericSocketStatusChageEventArgs() { }
-}
-
-///
-/// Delegate for notifying of TCP Server state changes
-///
-///
-public delegate void GenericTcpServerStateChangedEventDelegate(ServerState state);
-
-///
-/// EventArgs class for TCP Server state changes
-///
-public class GenericTcpServerStateChangedEventArgs : EventArgs
-{
- ///
- /// Gets or sets the State
- ///
- public ServerState State { get; private set; }
///
- /// Constructor
+ /// Delegate for notifying of TCP Server state changes
///
///
- public GenericTcpServerStateChangedEventArgs(ServerState state)
+ public delegate void GenericTcpServerStateChangedEventDelegate(ServerState state);
+
+ ///
+ /// EventArgs class for TCP Server state changes
+ ///
+ public class GenericTcpServerStateChangedEventArgs : EventArgs
{
- State = state;
- }
- ///
- /// S+ Constructor
- ///
- public GenericTcpServerStateChangedEventArgs() { }
-}
+ ///
+ /// Gets or sets the State
+ ///
+ public ServerState State { get; private set; }
-///
-/// Delegate for TCP Server socket status changes
-///
-///
-///
-///
-public delegate void GenericTcpServerSocketStatusChangeEventDelegate(object socket, uint clientIndex, SocketStatus clientStatus);
-///
-/// EventArgs for TCP server socket status changes
-///
-public class GenericTcpServerSocketStatusChangeEventArgs : EventArgs
-{
- ///
- /// Gets or sets the Socket
- ///
- public object Socket { get; private set; }
-
- ///
- /// Gets or sets the index of the client from which the status change was received
- ///
- public uint ReceivedFromClientIndex { get; private set; }
-
- ///
- /// Gets or sets the ClientStatus
- ///
- public SocketStatus ClientStatus { get; set; }
-
- ///
- /// Constructor
- ///
- ///
- ///
- public GenericTcpServerSocketStatusChangeEventArgs(object socket, SocketStatus clientStatus)
- {
- Socket = socket;
- ClientStatus = clientStatus;
- }
-
- ///
- /// Constructor
- ///
- ///
- ///
- ///
- public GenericTcpServerSocketStatusChangeEventArgs(object socket, uint clientIndex, SocketStatus clientStatus)
- {
- Socket = socket;
- ReceivedFromClientIndex = clientIndex;
- ClientStatus = clientStatus;
- }
- ///
- /// S+ Constructor
- ///
- public GenericTcpServerSocketStatusChangeEventArgs() { }
-}
-
-///
-/// EventArgs for TCP server com method receive text
-///
-public class GenericTcpServerCommMethodReceiveTextArgs : EventArgs
-{
- ///
- /// Gets or sets the index of the client from which the text was received
- ///
- public uint ReceivedFromClientIndex { get; private set; }
-
- ///
- /// Gets the index of the client from which the text was received as a ushort
- ///
- public ushort ReceivedFromClientIndexShort
- {
- get
+ ///
+ ///
+ ///
+ ///
+ public GenericTcpServerStateChangedEventArgs(ServerState state)
{
- return (ushort)ReceivedFromClientIndex;
+ State = state;
}
+ ///
+ /// S+ Constructor
+ ///
+ public GenericTcpServerStateChangedEventArgs() { }
}
///
- /// Gets or sets the Text
+ /// Delegate for TCP Server socket status changes
///
- public string Text { get; private set; }
-
- ///
- /// Constructor
- ///
- ///
- public GenericTcpServerCommMethodReceiveTextArgs(string text)
- {
- Text = text;
- }
-
- ///
- /// Constructor
- ///
- ///
+ ///
///
- public GenericTcpServerCommMethodReceiveTextArgs(string text, uint clientIndex)
+ ///
+ public delegate void GenericTcpServerSocketStatusChangeEventDelegate(object socket, uint clientIndex, SocketStatus clientStatus);
+ ///
+ /// EventArgs for TCP server socket status changes
+ ///
+ public class GenericTcpServerSocketStatusChangeEventArgs : EventArgs
{
- Text = text;
- ReceivedFromClientIndex = clientIndex;
- }
- ///
- /// S+ Constructor
- ///
- public GenericTcpServerCommMethodReceiveTextArgs() { }
-}
+ ///
+ ///
+ ///
+ public object Socket { get; private set; }
+ ///
+ ///
+ ///
+ public uint ReceivedFromClientIndex { get; private set; }
+ ///
+ ///
+ ///
+ public SocketStatus ClientStatus { get; set; }
-///
-/// EventArgs for TCP server client ready for communication
-///
-public class GenericTcpServerClientReadyForcommunicationsEventArgs : EventArgs
-{
- ///
- /// Gets or sets IsReady
- ///
- public bool IsReady;
+ ///
+ ///
+ ///
+ ///
+ ///
+ public GenericTcpServerSocketStatusChangeEventArgs(object socket, SocketStatus clientStatus)
+ {
+ Socket = socket;
+ ClientStatus = clientStatus;
+ }
- ///
- /// Constructor
- ///
- ///
- public GenericTcpServerClientReadyForcommunicationsEventArgs(bool isReady)
- {
- IsReady = isReady;
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public GenericTcpServerSocketStatusChangeEventArgs(object socket, uint clientIndex, SocketStatus clientStatus)
+ {
+ Socket = socket;
+ ReceivedFromClientIndex = clientIndex;
+ ClientStatus = clientStatus;
+ }
+ ///
+ /// S+ Constructor
+ ///
+ public GenericTcpServerSocketStatusChangeEventArgs() { }
}
///
- /// S+ Constructor
+ /// EventArgs for TCP server com method receive text
///
- public GenericTcpServerClientReadyForcommunicationsEventArgs() { }
-}
-
-///
-/// EventArgs for UDP connected
-///
-public class GenericUdpConnectedEventArgs : EventArgs
-{
- ///
- /// Gets or sets the UConnected
- ///
- public ushort UConnected;
-
- ///
- /// Gets or sets the Connected status
- ///
- public bool Connected;
-
- ///
- /// Constructor
- ///
- public GenericUdpConnectedEventArgs() { }
-
- ///
- /// Constructor
- ///
- ///
- public GenericUdpConnectedEventArgs(ushort uconnected)
+ public class GenericTcpServerCommMethodReceiveTextArgs : EventArgs
{
- UConnected = uconnected;
+ ///
+ ///
+ ///
+ public uint ReceivedFromClientIndex { get; private set; }
+
+ ///
+ ///
+ ///
+ public ushort ReceivedFromClientIndexShort
+ {
+ get
+ {
+ return (ushort)ReceivedFromClientIndex;
+ }
+ }
+
+ ///
+ /// Gets or sets the Text
+ ///
+ public string Text { get; private set; }
+
+ ///
+ ///
+ ///
+ ///
+ public GenericTcpServerCommMethodReceiveTextArgs(string text)
+ {
+ Text = text;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public GenericTcpServerCommMethodReceiveTextArgs(string text, uint clientIndex)
+ {
+ Text = text;
+ ReceivedFromClientIndex = clientIndex;
+ }
+ ///
+ /// S+ Constructor
+ ///
+ public GenericTcpServerCommMethodReceiveTextArgs() { }
}
///
- /// Constructor
+ /// EventArgs for TCP server client ready for communication
///
- ///
- public GenericUdpConnectedEventArgs(bool connected)
+ public class GenericTcpServerClientReadyForcommunicationsEventArgs : EventArgs
{
- Connected = connected;
+ ///
+ ///
+ ///
+ public bool IsReady;
+
+ ///
+ ///
+ ///
+ ///
+ public GenericTcpServerClientReadyForcommunicationsEventArgs(bool isReady)
+ {
+ IsReady = isReady;
+ }
+ ///
+ /// S+ Constructor
+ ///
+ public GenericTcpServerClientReadyForcommunicationsEventArgs() { }
}
-}
+ ///
+ /// EventArgs for UDP connected
+ ///
+ public class GenericUdpConnectedEventArgs : EventArgs
+ {
+ ///
+ ///
+ ///
+ public ushort UConnected;
+ ///
+ ///
+ ///
+ public bool Connected;
+ ///
+ /// Constructor
+ ///
+ public GenericUdpConnectedEventArgs() { }
+
+ ///
+ ///
+ ///
+ ///
+ public GenericUdpConnectedEventArgs(ushort uconnected)
+ {
+ UConnected = uconnected;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public GenericUdpConnectedEventArgs(bool connected)
+ {
+ Connected = connected;
+ }
+
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Comm/GenericSecureTcpIpClient.cs b/src/PepperDash.Core/Comm/GenericSecureTcpIpClient.cs
index c988db02..204c548b 100644
--- a/src/PepperDash.Core/Comm/GenericSecureTcpIpClient.cs
+++ b/src/PepperDash.Core/Comm/GenericSecureTcpIpClient.cs
@@ -3,960 +3,956 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
-using System.Timers;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronSockets;
using PepperDash.Core.Logging;
-namespace PepperDash.Core;
-
-///
-/// A class to handle secure TCP/IP communications with a server
-///
-public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
+namespace PepperDash.Core
{
- private const string SplusKey = "Uninitialized Secure Tcp _client";
///
- /// Stream debugging
+ /// A class to handle secure TCP/IP communications with a server
///
- public CommunicationStreamDebugging StreamDebugging { get; private set; }
-
- ///
- /// Fires when data is received from the server and returns it as a Byte array
- ///
- public event EventHandler BytesReceived;
-
- ///
- /// Fires when data is received from the server and returns it as text
- ///
- public event EventHandler TextReceived;
-
- #region GenericSecureTcpIpClient Events & Delegates
-
- ///
- ///
- ///
- //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
- public event EventHandler ConnectionChange;
-
- ///
- /// Auto reconnect evant handler
- ///
- public event EventHandler AutoReconnectTriggered;
-
- ///
- /// Event for Receiving text. Once subscribed to this event the receive callback will start a thread that dequeues the messages and invokes the event on a new thread.
- /// It is not recommended to use both the TextReceived event and the TextReceivedQueueInvoke event.
- ///
- public event EventHandler TextReceivedQueueInvoke;
-
- ///
- /// For a client with a pre shared key, this will fire after the communication is established and the key exchange is complete. If you require
- /// a key and subscribe to the socket change event and try to send data on a connection the data sent will interfere with the key exchange and disconnect.
- ///
- public event EventHandler ClientReadyForCommunications;
-
- #endregion
-
-
- #region GenricTcpIpClient properties
-
- private string _hostname;
-
- ///
- /// Address of server
- ///
- public string Hostname
+ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
{
- get { return _hostname; }
- set
+ private const string SplusKey = "Uninitialized Secure Tcp _client";
+ ///
+ /// Stream debugging
+ ///
+ public CommunicationStreamDebugging StreamDebugging { get; private set; }
+
+ ///
+ /// Fires when data is received from the server and returns it as a Byte array
+ ///
+ public event EventHandler BytesReceived;
+
+ ///
+ /// Fires when data is received from the server and returns it as text
+ ///
+ public event EventHandler TextReceived;
+
+ #region GenericSecureTcpIpClient Events & Delegates
+
+ ///
+ ///
+ ///
+ //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
+ public event EventHandler ConnectionChange;
+
+ ///
+ /// Auto reconnect evant handler
+ ///
+ public event EventHandler AutoReconnectTriggered;
+
+ ///
+ /// Event for Receiving text. Once subscribed to this event the receive callback will start a thread that dequeues the messages and invokes the event on a new thread.
+ /// It is not recommended to use both the TextReceived event and the TextReceivedQueueInvoke event.
+ ///
+ public event EventHandler TextReceivedQueueInvoke;
+
+ ///
+ /// For a client with a pre shared key, this will fire after the communication is established and the key exchange is complete. If you require
+ /// a key and subscribe to the socket change event and try to send data on a connection the data sent will interfere with the key exchange and disconnect.
+ ///
+ public event EventHandler ClientReadyForCommunications;
+
+ #endregion
+
+
+ #region GenricTcpIpClient properties
+
+ private string _hostname;
+
+ ///
+ /// Address of server
+ ///
+ public string Hostname
{
- _hostname = value;
- if (_client != null)
+ get { return _hostname; }
+ set
{
- _client.AddressClientConnectedTo = _hostname;
- }
- }
- }
-
- ///
- /// Port on server
- ///
- public int Port { get; set; }
-
- ///
- /// S+ helper
- ///
- public ushort UPort
- {
- get { return Convert.ToUInt16(Port); }
- set { Port = Convert.ToInt32(value); }
- }
-
- ///
- /// Defaults to 2000
- ///
- public int BufferSize { get; set; }
-
- ///
- /// Internal secure client
- ///
- private SecureTCPClient _client;
-
- ///
- /// Bool showing if socket is connected
- ///
- public bool IsConnected
- {
- get { return _client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
- }
-
- ///
- /// S+ helper for IsConnected
- ///
- public ushort UIsConnected
- {
- get { return (ushort)(IsConnected ? 1 : 0); }
- }
-
- ///
- /// _client socket status Read only
- ///
- public SocketStatus ClientStatus
- {
- get
- {
- return _client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : _client.ClientStatus;
- }
- }
-
- ///
- /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
- /// and IsConnected would be true when this == 2.
- ///
- public ushort UStatus
- {
- get { return (ushort)ClientStatus; }
- }
-
- ///
- /// Status text shows the message associated with socket status
- ///
- public string ClientStatusText { get { return ClientStatus.ToString(); } }
-
- ///
- /// Connection failure reason
- ///
- public string ConnectionFailure { get { return ClientStatus.ToString(); } }
-
- ///
- /// bool to track if auto reconnect should be set on the socket
- ///
- public bool AutoReconnect { get; set; }
-
- ///
- /// S+ helper for AutoReconnect
- ///
- public ushort UAutoReconnect
- {
- get { return (ushort)(AutoReconnect ? 1 : 0); }
- set { AutoReconnect = value == 1; }
- }
-
- ///
- /// Milliseconds to wait before attempting to reconnect. Defaults to 5000
- ///
- public int AutoReconnectIntervalMs { get; set; }
-
- ///
- /// Flag Set only when the disconnect method is called.
- ///
- bool DisconnectCalledByUser;
-
- ///
- ///
- ///
- public bool Connected
- {
- get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
- }
-
- // private Timer for auto reconnect
- private Timer RetryTimer;
-
- #endregion
-
- #region GenericSecureTcpIpClient properties
-
- ///
- /// Bool to show whether the server requires a preshared key. This is used in the DynamicTCPServer class
- ///
- public bool SharedKeyRequired { get; set; }
-
- ///
- /// S+ helper for requires shared key bool
- ///
- public ushort USharedKeyRequired
- {
- set
- {
- if (value == 1)
- SharedKeyRequired = true;
- else
- SharedKeyRequired = false;
- }
- }
-
- ///
- /// SharedKey is sent for varification to the server. Shared key can be any text (255 char limit in SIMPL+ Module), but must match the Shared Key on the Server module
- ///
- public string SharedKey { get; set; }
-
- ///
- /// flag to show the client is waiting for the server to send the shared key
- ///
- private bool WaitingForSharedKeyResponse { get; set; }
-
- ///
- /// Semaphore on connect method
- ///
- bool IsTryingToConnect;
-
- ///
- /// Bool showing if socket is ready for communication after shared key exchange
- ///
- public bool IsReadyForCommunication { get; set; }
-
- ///
- /// S+ helper for IsReadyForCommunication
- ///
- public ushort UIsReadyForCommunication
- {
- get { return (ushort)(IsReadyForCommunication ? 1 : 0); }
- }
-
- ///
- /// Bool Heartbeat Enabled flag
- ///
- public bool HeartbeatEnabled { get; set; }
-
- ///
- /// S+ helper for Heartbeat Enabled
- ///
- public ushort UHeartbeatEnabled
- {
- get { return (ushort)(HeartbeatEnabled ? 1 : 0); }
- set { HeartbeatEnabled = value == 1; }
- }
-
- ///
- /// Heartbeat String
- ///
- public string HeartbeatString { get; set; }
- //public int HeartbeatInterval = 50000;
-
- ///
- /// Milliseconds before server expects another heartbeat. Set by property HeartbeatRequiredIntervalInSeconds which is driven from S+
- ///
- public int HeartbeatInterval { get; set; }
-
- ///
- /// Simpl+ Heartbeat Analog value in seconds
- ///
- public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatInterval = (value * 1000); } }
-
- Timer HeartbeatSendTimer;
- Timer HeartbeatAckTimer;
-
- // Used to force disconnection on a dead connect attempt
- Timer ConnectFailTimer;
- Timer WaitForSharedKey;
- private int ConnectionCount;
-
- bool ProgramIsStopping;
-
- ///
- /// Queue lock
- ///
- private readonly object _dequeueLock = new();
-
- ///
- /// Receive Queue size. Defaults to 20. Will set to 20 if QueueSize property is less than 20. Use constructor or set queue size property before
- /// calling initialize.
- ///
- public int ReceiveQueueSize { get; set; }
-
- ///
- /// Queue to temporarily store received messages with the source IP and Port info. Defaults to size 20. Use constructor or set queue size property before
- /// calling initialize.
- ///
- private CrestronQueue MessageQueue;
-
- #endregion
-
- #region Constructors
-
- ///
- /// Constructor
- ///
- ///
- ///
- ///
- ///
- public GenericSecureTcpIpClient(string key, string address, int port, int bufferSize)
- : base(key)
- {
- StreamDebugging = new CommunicationStreamDebugging(key);
- Hostname = address;
- Port = port;
- BufferSize = bufferSize;
- AutoReconnectIntervalMs = 5000;
-
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- }
-
- ///
- /// Contstructor that sets all properties by calling the initialize method with a config object.
- ///
- ///
- ///
- public GenericSecureTcpIpClient(string key, TcpClientConfigObject clientConfigObject)
- : base(key)
- {
- StreamDebugging = new CommunicationStreamDebugging(key);
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- AutoReconnectIntervalMs = 5000;
- BufferSize = 2000;
-
- Initialize(clientConfigObject);
- }
-
- ///
- /// Default constructor for S+
- ///
- public GenericSecureTcpIpClient()
- : base(SplusKey)
- {
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- AutoReconnectIntervalMs = 5000;
- BufferSize = 2000;
- }
-
- ///
- /// Just to help S+ set the key
- ///
- public void Initialize(string key)
- {
- Key = key;
- }
-
- ///
- /// Initialize called by the constructor that accepts a client config object. Can be called later to reset properties of client.
- ///
- ///
- public void Initialize(TcpClientConfigObject config)
- {
- if (config == null)
- {
- this.LogWarning( "Could not initialize client with key: {0}", Key);
- return;
- }
- try
- {
- Hostname = config.Control.TcpSshProperties.Address;
- Port = config.Control.TcpSshProperties.Port > 0 && config.Control.TcpSshProperties.Port <= 65535
- ? config.Control.TcpSshProperties.Port
- : 80;
-
- AutoReconnect = config.Control.TcpSshProperties.AutoReconnect;
- AutoReconnectIntervalMs = config.Control.TcpSshProperties.AutoReconnectIntervalMs > 1000
- ? config.Control.TcpSshProperties.AutoReconnectIntervalMs
- : 5000;
-
- SharedKey = config.SharedKey;
- SharedKeyRequired = config.SharedKeyRequired;
-
- HeartbeatEnabled = config.HeartbeatRequired;
- HeartbeatRequiredIntervalInSeconds = config.HeartbeatRequiredIntervalInSeconds > 0
- ? config.HeartbeatRequiredIntervalInSeconds
- : (ushort)15;
-
-
- HeartbeatString = string.IsNullOrEmpty(config.HeartbeatStringToMatch)
- ? "heartbeat"
- : config.HeartbeatStringToMatch;
-
- BufferSize = config.Control.TcpSshProperties.BufferSize > 2000
- ? config.Control.TcpSshProperties.BufferSize
- : 2000;
-
- ReceiveQueueSize = config.ReceiveQueueSize > 20
- ? config.ReceiveQueueSize
- : 20;
-
- MessageQueue = new CrestronQueue(ReceiveQueueSize);
- }
- catch (Exception ex)
- {
- this.LogError("Exception initializing client with key: {0}\rException: {1}", Key, ex);
- }
- }
-
- #endregion
-
- ///
- /// Handles closing this up when the program shuts down
- ///
- void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
- {
- if (programEventType == eProgramStatusEventType.Stopping || programEventType == eProgramStatusEventType.Paused)
- {
- this.LogInformation("Program stopping. Closing _client connection");
- ProgramIsStopping = true;
- Disconnect();
- }
-
- }
-
- ///
- /// Deactivate the client
- ///
- ///
- public override bool Deactivate()
- {
- if (_client != null)
- {
- _client.SocketStatusChange -= this.Client_SocketStatusChange;
- DisconnectClient();
- }
- return true;
- }
-
- ///
- /// Connect Method. Will return if already connected. Will write errors if missing address, port, or unique key/name.
- ///
- public void Connect()
- {
- ConnectionCount++;
- this.LogVerbose("Attempting connect Count:{0}", ConnectionCount);
-
-
- if (IsConnected)
- {
- this.LogInformation("Already connected. Ignoring.");
- return;
- }
- if (IsTryingToConnect)
- {
- this.LogInformation("Already trying to connect. Ignoring.");
- return;
- }
- try
- {
- IsTryingToConnect = true;
- if (RetryTimer != null)
- {
- RetryTimer.Stop();
- RetryTimer = null;
- }
- if (string.IsNullOrEmpty(Hostname))
- {
- this.LogWarning("DynamicTcpClient: No address set");
- return;
- }
- if (Port < 1 || Port > 65535)
- {
- this.LogWarning("DynamicTcpClient: Invalid port");
- return;
- }
- if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
- {
- this.LogWarning("DynamicTcpClient: No Shared Key set");
- return;
- }
-
- // clean up previous client
- if (_client != null)
- {
- Disconnect();
- }
- DisconnectCalledByUser = false;
-
- _client = new SecureTCPClient(Hostname, Port, BufferSize);
- _client.SocketStatusChange += Client_SocketStatusChange;
- if (HeartbeatEnabled)
- _client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);
- _client.AddressClientConnectedTo = Hostname;
- _client.PortNumber = Port;
- // SecureClient = c;
-
- //var timeOfConnect = DateTime.Now.ToString("HH:mm:ss.fff");
-
- ConnectFailTimer = new Timer(30000) { AutoReset = false };
- ConnectFailTimer.Elapsed += (s, e) =>
- {
- this.LogError("Connect attempt has not finished after 30sec Count:{0}", ConnectionCount);
- if (IsTryingToConnect)
+ _hostname = value;
+ if (_client != null)
{
- IsTryingToConnect = false;
-
- //if (ConnectionHasHungCallback != null)
- //{
- // ConnectionHasHungCallback();
- //}
- //SecureClient.DisconnectFromServer();
- //CheckClosedAndTryReconnect();
+ _client.AddressClientConnectedTo = _hostname;
}
- };
- ConnectFailTimer.Start();
+ }
+ }
- this.LogVerbose("Making Connection Count:{0}", ConnectionCount);
- _client.ConnectToServerAsync(o =>
+ ///
+ /// Gets or sets the Port
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// S+ helper
+ ///
+ public ushort UPort
+ {
+ get { return Convert.ToUInt16(Port); }
+ set { Port = Convert.ToInt32(value); }
+ }
+
+ ///
+ /// Defaults to 2000
+ ///
+ public int BufferSize { get; set; }
+
+ ///
+ /// Internal secure client
+ ///
+ private SecureTCPClient _client;
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsConnected
+ {
+ get { return _client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsConnected
+ {
+ get { return (ushort)(IsConnected ? 1 : 0); }
+ }
+
+ ///
+ /// _client socket status Read only
+ ///
+ public SocketStatus ClientStatus
+ {
+ get
{
- this.LogVerbose("ConnectToServerAsync Count:{0} Ran!", ConnectionCount);
+ return _client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : _client.ClientStatus;
+ }
+ }
- if (ConnectFailTimer != null)
- {
- ConnectFailTimer.Stop();
- }
- IsTryingToConnect = false;
+ ///
+ /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
+ /// and IsConnected would be true when this == 2.
+ ///
+ public ushort UStatus
+ {
+ get { return (ushort)ClientStatus; }
+ }
- if (o.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- this.LogVerbose("_client connected to {0} on port {1}", o.AddressClientConnectedTo, o.LocalPortNumberOfClient);
- o.ReceiveDataAsync(Receive);
+ ///
+ /// Status text shows the message associated with socket status
+ ///
+ public string ClientStatusText { get { return ClientStatus.ToString(); } }
- if (SharedKeyRequired)
- {
- WaitingForSharedKeyResponse = true;
- WaitForSharedKey = new Timer(15000) { AutoReset = false };
- WaitForSharedKey.Elapsed += (s, e) =>
- {
- this.LogWarning("Shared key exchange timer expired. IsReadyForCommunication={0}", IsReadyForCommunication);
- // This is the only case where we should call DisconectFromServer...Event handeler will trigger the cleanup
- o.DisconnectFromServer();
- //CheckClosedAndTryReconnect();
- //OnClientReadyForcommunications(false); // Should send false event
- };
- WaitForSharedKey.Start();
- }
- else
- {
- //CLient connected and shared key is not required so just raise the ready for communication event. if Shared key
- //required this is called by the shared key being negotiated
- if (IsReadyForCommunication == false)
- {
- OnClientReadyForcommunications(true); // Key not required
- }
- }
- }
+ ///
+ /// Connection failure reason
+ ///
+ public string ConnectionFailure { get { return ClientStatus.ToString(); } }
+
+ ///
+ /// Gets or sets the AutoReconnect
+ ///
+ public bool AutoReconnect { get; set; }
+
+ ///
+ /// S+ helper for AutoReconnect
+ ///
+ public ushort UAutoReconnect
+ {
+ get { return (ushort)(AutoReconnect ? 1 : 0); }
+ set { AutoReconnect = value == 1; }
+ }
+
+ ///
+ /// Milliseconds to wait before attempting to reconnect. Defaults to 5000
+ ///
+ public int AutoReconnectIntervalMs { get; set; }
+
+ ///
+ /// Flag Set only when the disconnect method is called.
+ ///
+ bool DisconnectCalledByUser;
+
+ ///
+ ///
+ ///
+ public bool Connected
+ {
+ get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
+ }
+
+ // private Timer for auto reconnect
+ private CTimer RetryTimer;
+
+ #endregion
+
+ #region GenericSecureTcpIpClient properties
+
+ ///
+ /// Gets or sets the SharedKeyRequired
+ ///
+ public bool SharedKeyRequired { get; set; }
+
+ ///
+ /// S+ helper for requires shared key bool
+ ///
+ public ushort USharedKeyRequired
+ {
+ set
+ {
+ if (value == 1)
+ SharedKeyRequired = true;
else
- {
- this.LogWarning("Connect attempt failed {0}", o.ClientStatus);
- CheckClosedAndTryReconnect();
- }
- });
- }
- catch (Exception ex)
- {
- this.LogError("_client connection exception: {0}", ex.Message);
- IsTryingToConnect = false;
- CheckClosedAndTryReconnect();
- }
- }
-
- ///
- ///
- ///
- public void Disconnect()
- {
- this.LogVerbose("Disconnect Called");
-
- DisconnectCalledByUser = true;
-
- // stop trying reconnects, if we are
- if (RetryTimer != null)
- {
- RetryTimer.Stop();
- RetryTimer = null;
- }
-
- if (_client != null)
- {
- DisconnectClient();
- this.LogDebug("Disconnected");
- }
- }
-
- ///
- /// Does the actual disconnect business
- ///
- public void DisconnectClient()
- {
- if (_client == null) return;
-
- this.LogInformation("Disconnecting client");
- if (IsConnected)
- _client.DisconnectFromServer();
-
- // close up client. ALWAYS use this when disconnecting.
- IsTryingToConnect = false;
-
- this.LogVerbose("Disconnecting _client {0}", DisconnectCalledByUser ? ", Called by user" : "");
- _client.SocketStatusChange -= Client_SocketStatusChange;
- _client.Dispose();
- _client = null;
-
- if (ConnectFailTimer == null) return;
- ConnectFailTimer.Stop();
- ConnectFailTimer.Dispose();
- ConnectFailTimer = null;
- }
-
- #region Methods
-
- ///
- /// Called from Connect failure or Socket Status change if
- /// auto reconnect and socket disconnected (Not disconnected by user)
- ///
- void CheckClosedAndTryReconnect()
- {
- if (_client != null)
- {
- this.LogVerbose("Cleaning up remotely closed/failed connection.");
- Disconnect();
- }
- if (!DisconnectCalledByUser && AutoReconnect)
- {
- var halfInterval = AutoReconnectIntervalMs / 2;
- var rndTime = new Random().Next(-halfInterval, halfInterval) + AutoReconnectIntervalMs;
- this.LogVerbose("Attempting reconnect in {0} ms, randomized", rndTime);
- if (RetryTimer != null)
- {
- RetryTimer.Stop();
- RetryTimer = null;
+ SharedKeyRequired = false;
}
- if (AutoReconnectTriggered != null)
- AutoReconnectTriggered(this, new EventArgs());
- RetryTimer = new Timer(rndTime) { AutoReset = false };
- RetryTimer.Elapsed += (s, e) => Connect();
- RetryTimer.Start();
}
- }
- ///
- /// Receive callback
- ///
- ///
- ///
- void Receive(SecureTCPClient client, int numBytes)
- {
- if (numBytes > 0)
+ ///
+ /// Gets or sets the SharedKey
+ ///
+ public string SharedKey { get; set; }
+
+ ///
+ /// flag to show the client is waiting for the server to send the shared key
+ ///
+ private bool WaitingForSharedKeyResponse { get; set; }
+
+ ///
+ /// Semaphore on connect method
+ ///
+ bool IsTryingToConnect;
+
+ ///
+ /// Gets or sets the IsReadyForCommunication
+ ///
+ public bool IsReadyForCommunication { get; set; }
+
+ ///
+ /// S+ helper for IsReadyForCommunication
+ ///
+ public ushort UIsReadyForCommunication
{
- string str = string.Empty;
- var handler = TextReceivedQueueInvoke;
+ get { return (ushort)(IsReadyForCommunication ? 1 : 0); }
+ }
+
+ ///
+ /// Bool Heartbeat Enabled flag
+ ///
+ public bool HeartbeatEnabled { get; set; }
+
+ ///
+ /// S+ helper for Heartbeat Enabled
+ ///
+ public ushort UHeartbeatEnabled
+ {
+ get { return (ushort)(HeartbeatEnabled ? 1 : 0); }
+ set { HeartbeatEnabled = value == 1; }
+ }
+
+ ///
+ /// Heartbeat String
+ ///
+ public string HeartbeatString { get; set; }
+ //public int HeartbeatInterval = 50000;
+
+ ///
+ /// Milliseconds before server expects another heartbeat. Set by property HeartbeatRequiredIntervalInSeconds which is driven from S+
+ ///
+ public int HeartbeatInterval { get; set; }
+
+ ///
+ /// Simpl+ Heartbeat Analog value in seconds
+ ///
+ public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatInterval = (value * 1000); } }
+
+ CTimer HeartbeatSendTimer;
+ CTimer HeartbeatAckTimer;
+
+ // Used to force disconnection on a dead connect attempt
+ CTimer ConnectFailTimer;
+ CTimer WaitForSharedKey;
+ private int ConnectionCount;
+
+ bool ProgramIsStopping;
+
+ ///
+ /// Queue lock
+ ///
+ CCriticalSection DequeueLock = new CCriticalSection();
+
+ ///
+ /// Receive Queue size. Defaults to 20. Will set to 20 if QueueSize property is less than 20. Use constructor or set queue size property before
+ /// calling initialize.
+ ///
+ public int ReceiveQueueSize { get; set; }
+
+ ///
+ /// Queue to temporarily store received messages with the source IP and Port info. Defaults to size 20. Use constructor or set queue size property before
+ /// calling initialize.
+ ///
+ private CrestronQueue MessageQueue;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ ///
+ ///
+ public GenericSecureTcpIpClient(string key, string address, int port, int bufferSize)
+ : base(key)
+ {
+ StreamDebugging = new CommunicationStreamDebugging(key);
+ Hostname = address;
+ Port = port;
+ BufferSize = bufferSize;
+ AutoReconnectIntervalMs = 5000;
+
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ }
+
+ ///
+ /// Contstructor that sets all properties by calling the initialize method with a config object.
+ ///
+ ///
+ ///
+ public GenericSecureTcpIpClient(string key, TcpClientConfigObject clientConfigObject)
+ : base(key)
+ {
+ StreamDebugging = new CommunicationStreamDebugging(key);
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ AutoReconnectIntervalMs = 5000;
+ BufferSize = 2000;
+
+ Initialize(clientConfigObject);
+ }
+
+ ///
+ /// Default constructor for S+
+ ///
+ public GenericSecureTcpIpClient()
+ : base(SplusKey)
+ {
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ AutoReconnectIntervalMs = 5000;
+ BufferSize = 2000;
+ }
+
+ ///
+ /// Initialize method
+ ///
+ public void Initialize(string key)
+ {
+ Key = key;
+ }
+
+ ///
+ /// Initialize called by the constructor that accepts a client config object. Can be called later to reset properties of client.
+ ///
+ ///
+ public void Initialize(TcpClientConfigObject config)
+ {
+ if (config == null)
+ {
+ Debug.Console(0, this, "Could not initialize client with key: {0}", Key);
+ return;
+ }
try
{
- var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
- str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
- this.LogVerbose("_client Received:\r--------\r{0}\r--------", str);
- if (!string.IsNullOrEmpty(checkHeartbeat(str)))
- {
+ Hostname = config.Control.TcpSshProperties.Address;
+ Port = config.Control.TcpSshProperties.Port > 0 && config.Control.TcpSshProperties.Port <= 65535
+ ? config.Control.TcpSshProperties.Port
+ : 80;
- if (SharedKeyRequired && str == "SharedKey:")
- {
- this.LogVerbose("Server asking for shared key, sending");
- SendText(SharedKey + "\n");
- }
- else if (SharedKeyRequired && str == "Shared Key Match")
- {
- StopWaitForSharedKeyTimer();
+ AutoReconnect = config.Control.TcpSshProperties.AutoReconnect;
+ AutoReconnectIntervalMs = config.Control.TcpSshProperties.AutoReconnectIntervalMs > 1000
+ ? config.Control.TcpSshProperties.AutoReconnectIntervalMs
+ : 5000;
+
+ SharedKey = config.SharedKey;
+ SharedKeyRequired = config.SharedKeyRequired;
+
+ HeartbeatEnabled = config.HeartbeatRequired;
+ HeartbeatRequiredIntervalInSeconds = config.HeartbeatRequiredIntervalInSeconds > 0
+ ? config.HeartbeatRequiredIntervalInSeconds
+ : (ushort)15;
- this.LogVerbose("Shared key confirmed. Ready for communication");
- OnClientReadyForcommunications(true); // Successful key exchange
- }
- else
- {
- //var bytesHandler = BytesReceived;
- //if (bytesHandler != null)
- // bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
- var textHandler = TextReceived;
- if (textHandler != null)
- textHandler(this, new GenericCommMethodReceiveTextArgs(str));
- if (handler != null)
- {
- MessageQueue.TryToEnqueue(new GenericTcpServerCommMethodReceiveTextArgs(str));
- }
- }
- }
+ HeartbeatString = string.IsNullOrEmpty(config.HeartbeatStringToMatch)
+ ? "heartbeat"
+ : config.HeartbeatStringToMatch;
+
+ BufferSize = config.Control.TcpSshProperties.BufferSize > 2000
+ ? config.Control.TcpSshProperties.BufferSize
+ : 2000;
+
+ ReceiveQueueSize = config.ReceiveQueueSize > 20
+ ? config.ReceiveQueueSize
+ : 20;
+
+ MessageQueue = new CrestronQueue(ReceiveQueueSize);
}
catch (Exception ex)
{
- this.LogError("Error receiving data: {1}. Error: {0}", str, ex.Message);
+ Debug.Console(0, this, "Exception initializing client with key: {0}\rException: {1}", Key, ex);
}
- if (client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
- client.ReceiveDataAsync(Receive);
+ }
- //Check to see if there is a subscription to the TextReceivedQueueInvoke event. If there is start the dequeue thread.
- if (handler != null)
+ #endregion
+
+ ///
+ /// Handles closing this up when the program shuts down
+ ///
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping || programEventType == eProgramStatusEventType.Paused)
{
- if (System.Threading.Monitor.TryEnter(_dequeueLock))
- System.Threading.Tasks.Task.Run(() => DequeueEvent());
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing _client connection");
+ ProgramIsStopping = true;
+ Disconnect();
}
- }
- else //JAG added this as I believe the error return is 0 bytes like the server. See help when hover on ReceiveAsync
- {
- client.DisconnectFromServer();
- }
- }
- ///
- /// This method gets spooled up in its own thread an protected by a lock to prevent multiple threads from running concurrently.
- /// It will dequeue items as they are enqueued automatically.
- ///
- void DequeueEvent()
- {
- try
+ }
+
+ ///
+ /// Deactivate the client
+ ///
+ ///
+ ///
+ /// Deactivate method
+ ///
+ public override bool Deactivate()
{
- while (true)
+ if (_client != null)
{
- // Pull from Queue and fire an event. Block indefinitely until an item can be removed, similar to a Gather.
- var message = MessageQueue.Dequeue();
- var handler = TextReceivedQueueInvoke;
- if (handler != null)
- {
- handler(this, message);
- }
- }
- }
- catch (Exception e)
- {
- this.LogError(e, "DequeueEvent error: {0}", e.Message);
- }
- // Make sure to release the lock in case an exception above stops this thread, or we won't be able to restart it.
- System.Threading.Monitor.Exit(_dequeueLock);
- }
-
- void HeartbeatStart()
- {
- if (HeartbeatEnabled)
- {
- this.LogVerbose("Starting Heartbeat");
- if (HeartbeatSendTimer == null)
- {
-
- HeartbeatSendTimer = new Timer(HeartbeatInterval) { AutoReset = true };
- HeartbeatSendTimer.Elapsed += (s, e) => SendHeartbeat(null);
- HeartbeatSendTimer.Start();
- }
- if (HeartbeatAckTimer == null)
- {
- HeartbeatAckTimer = new Timer(HeartbeatInterval * 2) { AutoReset = true };
- HeartbeatAckTimer.Elapsed += (s, e) => HeartbeatAckTimerFail(null);
- HeartbeatAckTimer.Start();
+ _client.SocketStatusChange -= this.Client_SocketStatusChange;
+ DisconnectClient();
}
+ return true;
}
- }
- void HeartbeatStop()
- {
-
- if (HeartbeatSendTimer != null)
+ ///
+ /// Connect method
+ ///
+ public void Connect()
{
- this.LogVerbose("Stoping Heartbeat Send");
- HeartbeatSendTimer.Stop();
- HeartbeatSendTimer = null;
- }
- if (HeartbeatAckTimer != null)
- {
- this.LogVerbose("Stoping Heartbeat Ack");
- HeartbeatAckTimer.Stop();
- HeartbeatAckTimer = null;
- }
+ ConnectionCount++;
+ Debug.Console(2, this, "Attempting connect Count:{0}", ConnectionCount);
- }
- void SendHeartbeat(object notused)
- {
- this.SendText(HeartbeatString);
- this.LogVerbose("Sending Heartbeat");
-
- }
-
- //private method to check heartbeat requirements and start or reset timer
- string checkHeartbeat(string received)
- {
- try
- {
- if (HeartbeatEnabled)
- {
- if (!string.IsNullOrEmpty(HeartbeatString))
- {
- var remainingText = received.Replace(HeartbeatString, "");
- var noDelimiter = received.Trim(new char[] { '\r', '\n' });
- if (noDelimiter.Contains(HeartbeatString))
- {
- if (HeartbeatAckTimer != null)
- {
- HeartbeatAckTimer.Stop();
- HeartbeatAckTimer.Interval = HeartbeatInterval * 2;
- HeartbeatAckTimer.Start();
- }
- else
- {
- HeartbeatAckTimer = new Timer(HeartbeatInterval * 2) { AutoReset = true };
- HeartbeatAckTimer.Elapsed += (s, e) => HeartbeatAckTimerFail(null);
- HeartbeatAckTimer.Start();
- }
- this.LogVerbose("Heartbeat Received: {0}, from Server", HeartbeatString);
- return remainingText;
- }
- }
- }
- }
- catch (Exception ex)
- {
- this.LogError(ex, "Error checking heartbeat: {0}", ex.Message);
- }
- return received;
- }
-
-
-
- void HeartbeatAckTimerFail(object o)
- {
- try
- {
if (IsConnected)
{
- this.LogWarning("Heartbeat not received from Server...DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE");
- SendText("Heartbeat not received by server, closing connection");
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Already connected. Ignoring.");
+ return;
+ }
+ if (IsTryingToConnect)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Already trying to connect. Ignoring.");
+ return;
+ }
+ try
+ {
+ IsTryingToConnect = true;
+ if (RetryTimer != null)
+ {
+ RetryTimer.Stop();
+ RetryTimer = null;
+ }
+ if (string.IsNullOrEmpty(Hostname))
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: No address set");
+ return;
+ }
+ if (Port < 1 || Port > 65535)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: Invalid port");
+ return;
+ }
+ if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: No Shared Key set");
+ return;
+ }
+
+ // clean up previous client
+ if (_client != null)
+ {
+ Disconnect();
+ }
+ DisconnectCalledByUser = false;
+
+ _client = new SecureTCPClient(Hostname, Port, BufferSize);
+ _client.SocketStatusChange += Client_SocketStatusChange;
+ if (HeartbeatEnabled)
+ _client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);
+ _client.AddressClientConnectedTo = Hostname;
+ _client.PortNumber = Port;
+ // SecureClient = c;
+
+ //var timeOfConnect = DateTime.Now.ToString("HH:mm:ss.fff");
+
+ ConnectFailTimer = new CTimer(o =>
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Connect attempt has not finished after 30sec Count:{0}", ConnectionCount);
+ if (IsTryingToConnect)
+ {
+ IsTryingToConnect = false;
+
+ //if (ConnectionHasHungCallback != null)
+ //{
+ // ConnectionHasHungCallback();
+ //}
+ //SecureClient.DisconnectFromServer();
+ //CheckClosedAndTryReconnect();
+ }
+ }, 30000);
+
+ Debug.Console(2, this, "Making Connection Count:{0}", ConnectionCount);
+ _client.ConnectToServerAsync(o =>
+ {
+ Debug.Console(2, this, "ConnectToServerAsync Count:{0} Ran!", ConnectionCount);
+
+ if (ConnectFailTimer != null)
+ {
+ ConnectFailTimer.Stop();
+ }
+ IsTryingToConnect = false;
+
+ if (o.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ Debug.Console(2, this, "_client connected to {0} on port {1}", o.AddressClientConnectedTo, o.LocalPortNumberOfClient);
+ o.ReceiveDataAsync(Receive);
+
+ if (SharedKeyRequired)
+ {
+ WaitingForSharedKeyResponse = true;
+ WaitForSharedKey = new CTimer(timer =>
+ {
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Shared key exchange timer expired. IsReadyForCommunication={0}", IsReadyForCommunication);
+ // Debug.Console(1, this, "Connect attempt failed {0}", c.ClientStatus);
+ // This is the only case where we should call DisconectFromServer...Event handeler will trigger the cleanup
+ o.DisconnectFromServer();
+ //CheckClosedAndTryReconnect();
+ //OnClientReadyForcommunications(false); // Should send false event
+ }, 15000);
+ }
+ else
+ {
+ //CLient connected and shared key is not required so just raise the ready for communication event. if Shared key
+ //required this is called by the shared key being negotiated
+ if (IsReadyForCommunication == false)
+ {
+ OnClientReadyForcommunications(true); // Key not required
+ }
+ }
+ }
+ else
+ {
+ Debug.Console(1, this, "Connect attempt failed {0}", o.ClientStatus);
+ CheckClosedAndTryReconnect();
+ }
+ });
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Error, "_client connection exception: {0}", ex.Message);
+ IsTryingToConnect = false;
CheckClosedAndTryReconnect();
}
-
}
- catch (Exception ex)
+
+ ///
+ /// Disconnect method
+ ///
+ public void Disconnect()
{
- this.LogError(ex, "Heartbeat timeout Error on _client: {0}, {1}", Key, ex.Message);
- }
- }
+ this.LogVerbose("Disconnect Called");
- ///
- ///
- ///
- void StopWaitForSharedKeyTimer()
- {
- if (WaitForSharedKey != null)
+ DisconnectCalledByUser = true;
+
+ // stop trying reconnects, if we are
+ if (RetryTimer != null)
+ {
+ RetryTimer.Stop();
+ RetryTimer = null;
+ }
+
+ if (_client != null)
+ {
+ DisconnectClient();
+ this.LogDebug("Disconnected");
+ }
+ }
+
+ ///
+ /// DisconnectClient method
+ ///
+ public void DisconnectClient()
{
- WaitForSharedKey.Stop();
- WaitForSharedKey = null;
- }
- }
+ if (_client == null) return;
- ///
- /// General send method
- ///
- public void SendText(string text)
- {
- if (!string.IsNullOrEmpty(text))
+ Debug.Console(1, this, "Disconnecting client");
+ if (IsConnected)
+ _client.DisconnectFromServer();
+
+ // close up client. ALWAYS use this when disconnecting.
+ IsTryingToConnect = false;
+
+ Debug.Console(2, this, "Disconnecting _client {0}", DisconnectCalledByUser ? ", Called by user" : "");
+ _client.SocketStatusChange -= Client_SocketStatusChange;
+ _client.Dispose();
+ _client = null;
+
+ if (ConnectFailTimer == null) return;
+ ConnectFailTimer.Stop();
+ ConnectFailTimer.Dispose();
+ ConnectFailTimer = null;
+ }
+
+ #region Methods
+
+ ///
+ /// Called from Connect failure or Socket Status change if
+ /// auto reconnect and socket disconnected (Not disconnected by user)
+ ///
+ void CheckClosedAndTryReconnect()
+ {
+ if (_client != null)
+ {
+ Debug.Console(2, this, "Cleaning up remotely closed/failed connection.");
+ Disconnect();
+ }
+ if (!DisconnectCalledByUser && AutoReconnect)
+ {
+ var halfInterval = AutoReconnectIntervalMs / 2;
+ var rndTime = new Random().Next(-halfInterval, halfInterval) + AutoReconnectIntervalMs;
+ Debug.Console(2, this, "Attempting reconnect in {0} ms, randomized", rndTime);
+ if (RetryTimer != null)
+ {
+ RetryTimer.Stop();
+ RetryTimer = null;
+ }
+ if (AutoReconnectTriggered != null)
+ AutoReconnectTriggered(this, new EventArgs());
+ RetryTimer = new CTimer(o => Connect(), rndTime);
+ }
+ }
+
+ ///
+ /// Receive callback
+ ///
+ ///
+ ///
+ void Receive(SecureTCPClient client, int numBytes)
+ {
+ if (numBytes > 0)
+ {
+ string str = string.Empty;
+ var handler = TextReceivedQueueInvoke;
+ try
+ {
+ var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
+ str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
+ Debug.Console(2, this, "_client Received:\r--------\r{0}\r--------", str);
+ if (!string.IsNullOrEmpty(checkHeartbeat(str)))
+ {
+
+ if (SharedKeyRequired && str == "SharedKey:")
+ {
+ Debug.Console(2, this, "Server asking for shared key, sending");
+ SendText(SharedKey + "\n");
+ }
+ else if (SharedKeyRequired && str == "Shared Key Match")
+ {
+ StopWaitForSharedKeyTimer();
+
+
+ Debug.Console(2, this, "Shared key confirmed. Ready for communication");
+ OnClientReadyForcommunications(true); // Successful key exchange
+ }
+ else
+ {
+ //var bytesHandler = BytesReceived;
+ //if (bytesHandler != null)
+ // bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
+ var textHandler = TextReceived;
+ if (textHandler != null)
+ textHandler(this, new GenericCommMethodReceiveTextArgs(str));
+ if (handler != null)
+ {
+ MessageQueue.TryToEnqueue(new GenericTcpServerCommMethodReceiveTextArgs(str));
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(1, this, "Error receiving data: {1}. Error: {0}", ex.Message, str);
+ }
+ if (client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ client.ReceiveDataAsync(Receive);
+
+ //Check to see if there is a subscription to the TextReceivedQueueInvoke event. If there is start the dequeue thread.
+ if (handler != null)
+ {
+ var gotLock = DequeueLock.TryEnter();
+ if (gotLock)
+ CrestronInvoke.BeginInvoke((o) => DequeueEvent());
+ }
+ }
+ else //JAG added this as I believe the error return is 0 bytes like the server. See help when hover on ReceiveAsync
+ {
+ client.DisconnectFromServer();
+ }
+ }
+
+ ///
+ /// This method gets spooled up in its own thread an protected by a CCriticalSection to prevent multiple threads from running concurrently.
+ /// It will dequeue items as they are enqueued automatically.
+ ///
+ void DequeueEvent()
{
try
{
- var bytes = Encoding.GetEncoding(28591).GetBytes(text);
- if (_client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ while (true)
{
- _client.SendDataAsync(bytes, bytes.Length, (c, n) =>
+ // Pull from Queue and fire an event. Block indefinitely until an item can be removed, similar to a Gather.
+ var message = MessageQueue.Dequeue();
+ var handler = TextReceivedQueueInvoke;
+ if (handler != null)
{
- // HOW IN THE HELL DO WE CATCH AN EXCEPTION IN SENDING?????
- if (n <= 0)
+ handler(this, message);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ this.LogException(e, "DequeueEvent error");
+ }
+ // Make sure to leave the CCritical section in case an exception above stops this thread, or we won't be able to restart it.
+ if (DequeueLock != null)
+ {
+ DequeueLock.Leave();
+ }
+ }
+
+ void HeartbeatStart()
+ {
+ if (HeartbeatEnabled)
+ {
+ this.LogVerbose("Starting Heartbeat");
+ if (HeartbeatSendTimer == null)
+ {
+
+ HeartbeatSendTimer = new CTimer(this.SendHeartbeat, null, HeartbeatInterval, HeartbeatInterval);
+ }
+ if (HeartbeatAckTimer == null)
+ {
+ HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
+ }
+ }
+
+ }
+ void HeartbeatStop()
+ {
+
+ if (HeartbeatSendTimer != null)
+ {
+ Debug.Console(2, this, "Stoping Heartbeat Send");
+ HeartbeatSendTimer.Stop();
+ HeartbeatSendTimer = null;
+ }
+ if (HeartbeatAckTimer != null)
+ {
+ Debug.Console(2, this, "Stoping Heartbeat Ack");
+ HeartbeatAckTimer.Stop();
+ HeartbeatAckTimer = null;
+ }
+
+ }
+ void SendHeartbeat(object notused)
+ {
+ this.SendText(HeartbeatString);
+ Debug.Console(2, this, "Sending Heartbeat");
+
+ }
+
+ //private method to check heartbeat requirements and start or reset timer
+ string checkHeartbeat(string received)
+ {
+ try
+ {
+ if (HeartbeatEnabled)
+ {
+ if (!string.IsNullOrEmpty(HeartbeatString))
+ {
+ var remainingText = received.Replace(HeartbeatString, "");
+ var noDelimiter = received.Trim(new char[] { '\r', '\n' });
+ if (noDelimiter.Contains(HeartbeatString))
{
- this.LogWarning("[{0}] Sent zero bytes. Was there an error?", this.Key);
+ if (HeartbeatAckTimer != null)
+ {
+ HeartbeatAckTimer.Reset(HeartbeatInterval * 2);
+ }
+ else
+ {
+ HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
+ }
+ Debug.Console(2, this, "Heartbeat Received: {0}, from Server", HeartbeatString);
+ return remainingText;
}
- });
+ }
}
}
catch (Exception ex)
{
- this.LogError(ex, "Error sending text: {1}. Error: {0}", ex.Message, text);
+ Debug.Console(1, this, "Error checking heartbeat: {0}", ex.Message);
}
+ return received;
}
- }
- ///
- ///
- ///
- public void SendBytes(byte[] bytes)
- {
- if (bytes.Length > 0)
+
+
+ void HeartbeatAckTimerFail(object o)
{
try
{
- if (_client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
- _client.SendData(bytes, bytes.Length);
+
+ if (IsConnected)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "Heartbeat not received from Server...DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE");
+ SendText("Heartbeat not received by server, closing connection");
+ CheckClosedAndTryReconnect();
+ }
+
}
catch (Exception ex)
{
- this.LogError(ex, "Error sending bytes. Error: {0}", ex.Message);
+ ErrorLog.Error("Heartbeat timeout Error on _client: {0}, {1}", Key, ex);
}
}
- }
- ///
- /// SocketStatusChange Callback
- ///
- ///
- ///
- void Client_SocketStatusChange(SecureTCPClient client, SocketStatus clientSocketStatus)
- {
- if (ProgramIsStopping)
+ ///
+ ///
+ ///
+ void StopWaitForSharedKeyTimer()
{
- ProgramIsStopping = false;
- return;
- }
- try
- {
- this.LogVerbose("Socket status change: {0} ({1})", client.ClientStatus, (ushort)(client.ClientStatus));
-
- OnConnectionChange();
- // The client could be null or disposed by this time...
- if (_client == null || _client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ if (WaitForSharedKey != null)
{
- HeartbeatStop();
- OnClientReadyForcommunications(false); // socket has gone low
- CheckClosedAndTryReconnect();
+ WaitForSharedKey.Stop();
+ WaitForSharedKey = null;
}
}
- catch (Exception ex)
+
+ ///
+ /// SendText method
+ ///
+ public void SendText(string text)
{
- this.LogError(ex, "Error in socket status change callback. Error: {0}\r\r{1}", ex, ex.InnerException);
+ if (!string.IsNullOrEmpty(text))
+ {
+ try
+ {
+ var bytes = Encoding.GetEncoding(28591).GetBytes(text);
+ if (_client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ _client.SendDataAsync(bytes, bytes.Length, (c, n) =>
+ {
+ // HOW IN THE HELL DO WE CATCH AN EXCEPTION IN SENDING?????
+ if (n <= 0)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "[{0}] Sent zero bytes. Was there an error?", this.Key);
+ }
+ });
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, "Error sending text: {1}. Error: {0}", ex.Message, text);
+ }
+ }
}
+
+ ///
+ /// SendBytes method
+ ///
+ public void SendBytes(byte[] bytes)
+ {
+ if (bytes.Length > 0)
+ {
+ try
+ {
+ if (_client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ _client.SendData(bytes, bytes.Length);
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, "Error sending bytes. Error: {0}", ex.Message);
+ }
+ }
+ }
+
+ ///
+ /// SocketStatusChange Callback
+ ///
+ ///
+ ///
+ void Client_SocketStatusChange(SecureTCPClient client, SocketStatus clientSocketStatus)
+ {
+ if (ProgramIsStopping)
+ {
+ ProgramIsStopping = false;
+ return;
+ }
+ try
+ {
+ Debug.Console(2, this, "Socket status change: {0} ({1})", client.ClientStatus, (ushort)(client.ClientStatus));
+
+ OnConnectionChange();
+ // The client could be null or disposed by this time...
+ if (_client == null || _client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ HeartbeatStop();
+ OnClientReadyForcommunications(false); // socket has gone low
+ CheckClosedAndTryReconnect();
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Error in socket status change callback. Error: {0}\r\r{1}", ex, ex.InnerException);
+ }
+ }
+
+ ///
+ /// Helper for ConnectionChange event
+ ///
+ void OnConnectionChange()
+ {
+ var handler = ConnectionChange;
+ if (handler == null) return;
+
+ handler(this, new GenericSocketStatusChageEventArgs(this));
+ }
+
+ ///
+ /// Helper to fire ClientReadyForCommunications event
+ ///
+ void OnClientReadyForcommunications(bool isReady)
+ {
+ IsReadyForCommunication = isReady;
+ if (IsReadyForCommunication)
+ HeartbeatStart();
+
+ var handler = ClientReadyForCommunications;
+ if (handler == null) return;
+
+ handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));
+ }
+ #endregion
}
- ///
- /// Helper for ConnectionChange event
- ///
- void OnConnectionChange()
- {
- var handler = ConnectionChange;
- if (handler == null) return;
-
- handler(this, new GenericSocketStatusChageEventArgs(this));
- }
-
- ///
- /// Helper to fire ClientReadyForCommunications event
- ///
- void OnClientReadyForcommunications(bool isReady)
- {
- IsReadyForCommunication = isReady;
- if (IsReadyForCommunication)
- HeartbeatStart();
-
- var handler = ClientReadyForCommunications;
- if (handler == null) return;
-
- handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));
- }
- #endregion
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Comm/GenericSecureTcpIpClient_ForServer.cs b/src/PepperDash.Core/Comm/GenericSecureTcpIpClient_ForServer.cs
index 84af5a47..49350164 100644
--- a/src/PepperDash.Core/Comm/GenericSecureTcpIpClient_ForServer.cs
+++ b/src/PepperDash.Core/Comm/GenericSecureTcpIpClient_ForServer.cs
@@ -15,905 +15,895 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Timers;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronSockets;
using PepperDash.Core.Logging;
-namespace PepperDash.Core;
-
-///
-/// Generic secure TCP/IP client for server
-///
-public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
+namespace PepperDash.Core
{
///
- /// Band aid delegate for choked server
+ /// Generic secure TCP/IP client for server
///
- internal delegate void ConnectionHasHungCallbackDelegate();
-
- #region Events
-
- //public event EventHandler BytesReceived;
-
- ///
- /// Notifies of text received
- ///
- public event EventHandler TextReceived;
-
- ///
- /// Notifies of auto reconnect sequence triggered
- ///
- public event EventHandler AutoReconnectTriggered;
-
- ///
- /// Event for Receiving text. Once subscribed to this event the receive callback will start a thread that dequeues the messages and invokes the event on a new thread.
- /// It is not recommended to use both the TextReceived event and the TextReceivedQueueInvoke event.
- ///
- public event EventHandler TextReceivedQueueInvoke;
-
- ///
- /// Notifies of socket status change
- ///
- public event EventHandler ConnectionChange;
-
-
- ///
- /// This is something of a band-aid callback. If the client times out during the connection process, because the server
- /// is stuck, this will fire. It is intended to be used by the Server class monitor client, to help
- /// keep a watch on the server and reset it if necessary.
- ///
- internal ConnectionHasHungCallbackDelegate ConnectionHasHungCallback;
-
- ///
- /// For a client with a pre shared key, this will fire after the communication is established and the key exchange is complete. If you require
- /// a key and subscribe to the socket change event and try to send data on a connection the data sent will interfere with the key exchange and disconnect.
- ///
- public event EventHandler ClientReadyForCommunications;
-
- #endregion
-
- #region Properties & Variables
-
- ///
- /// Address of server
- ///
- public string Hostname { get; set; }
-
- ///
- /// The port number on which the server is listening.
- ///
- public int Port { get; set; }
-
- ///
- /// S+ helper
- ///
- public ushort UPort
+ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
{
- get { return Convert.ToUInt16(Port); }
- set { Port = Convert.ToInt32(value); }
- }
+ ///
+ /// Band aid delegate for choked server
+ ///
+ internal delegate void ConnectionHasHungCallbackDelegate();
- ///
- /// Bool to show whether the server requires a preshared key. This is used in the DynamicTCPServer class
- ///
- public bool SharedKeyRequired { get; set; }
+ #region Events
- ///
- /// S+ helper for requires shared key bool
- ///
- public ushort USharedKeyRequired
- {
- set
+ //public event EventHandler BytesReceived;
+
+ ///
+ /// Notifies of text received
+ ///
+ public event EventHandler TextReceived;
+
+ ///
+ /// Notifies of auto reconnect sequence triggered
+ ///
+ public event EventHandler AutoReconnectTriggered;
+
+ ///
+ /// Event for Receiving text. Once subscribed to this event the receive callback will start a thread that dequeues the messages and invokes the event on a new thread.
+ /// It is not recommended to use both the TextReceived event and the TextReceivedQueueInvoke event.
+ ///
+ public event EventHandler TextReceivedQueueInvoke;
+
+ ///
+ /// Notifies of socket status change
+ ///
+ public event EventHandler ConnectionChange;
+
+
+ ///
+ /// This is something of a band-aid callback. If the client times out during the connection process, because the server
+ /// is stuck, this will fire. It is intended to be used by the Server class monitor client, to help
+ /// keep a watch on the server and reset it if necessary.
+ ///
+ internal ConnectionHasHungCallbackDelegate ConnectionHasHungCallback;
+
+ ///
+ /// For a client with a pre shared key, this will fire after the communication is established and the key exchange is complete. If you require
+ /// a key and subscribe to the socket change event and try to send data on a connection the data sent will interfere with the key exchange and disconnect.
+ ///
+ public event EventHandler ClientReadyForCommunications;
+
+ #endregion
+
+ #region Properties & Variables
+
+ ///
+ /// Address of server
+ ///
+ public string Hostname { get; set; }
+
+ ///
+ /// Gets or sets the Port
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// S+ helper
+ ///
+ public ushort UPort
{
- if (value == 1)
- SharedKeyRequired = true;
- else
- SharedKeyRequired = false;
+ get { return Convert.ToUInt16(Port); }
+ set { Port = Convert.ToInt32(value); }
}
- }
- ///
- /// SharedKey is sent for verification to the server. Shared key can be any text (255 char limit in SIMPL+ Module), but must match the Shared Key on the Server module
- ///
- public string SharedKey { get; set; }
+ ///
+ /// Bool to show whether the server requires a preshared key. This is used in the DynamicTCPServer class
+ ///
+ public bool SharedKeyRequired { get; set; }
- ///
- /// flag to show the client is waiting for the server to send the shared key
- ///
- private bool WaitingForSharedKeyResponse { get; set; }
-
- ///
- /// Defaults to 2000
- ///
- public int BufferSize { get; set; }
-
- ///
- /// Semaphore on connect method
- ///
- bool IsTryingToConnect;
-
- ///
- /// Bool showing if socket is connected
- ///
- public bool IsConnected
- {
- get
+ ///
+ /// S+ helper for requires shared key bool
+ ///
+ public ushort USharedKeyRequired
{
- if (Client != null)
- return Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED;
- else
- return false;
- }
- }
-
- ///
- /// S+ helper for IsConnected
- ///
- public ushort UIsConnected
- {
- get { return (ushort)(IsConnected ? 1 : 0); }
- }
-
- ///
- /// Bool showing if socket is ready for communication after shared key exchange
- ///
- public bool IsReadyForCommunication { get; set; }
-
- ///
- /// S+ helper for IsReadyForCommunication
- ///
- public ushort UIsReadyForCommunication
- {
- get { return (ushort)(IsReadyForCommunication ? 1 : 0); }
- }
-
- ///
- /// Client socket status Read only
- ///
- public SocketStatus ClientStatus
- {
- get
- {
- if (Client != null)
- return Client.ClientStatus;
- else
- return SocketStatus.SOCKET_STATUS_NO_CONNECT;
- }
- }
-
- ///
- /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
- /// and IsConnected would be true when this == 2.
- ///
- public ushort UStatus
- {
- get { return (ushort)ClientStatus; }
- }
-
- ///
- /// Status text shows the message associated with socket status
- ///
- public string ClientStatusText { get { return ClientStatus.ToString(); } }
-
- ///
- /// bool to track if auto reconnect should be set on the socket
- ///
- public bool AutoReconnect { get; set; }
-
- ///
- /// S+ helper for AutoReconnect
- ///
- public ushort UAutoReconnect
- {
- get { return (ushort)(AutoReconnect ? 1 : 0); }
- set { AutoReconnect = value == 1; }
- }
- ///
- /// Milliseconds to wait before attempting to reconnect. Defaults to 5000
- ///
- public int AutoReconnectIntervalMs { get; set; }
-
- ///
- /// Flag Set only when the disconnect method is called.
- ///
- bool DisconnectCalledByUser;
-
- ///
- /// private Timer for auto reconnect
- ///
- System.Timers.Timer RetryTimer;
-
-
- ///
- ///
- ///
- public bool HeartbeatEnabled { get; set; }
- ///
- ///
- ///
- public ushort UHeartbeatEnabled
- {
- get { return (ushort)(HeartbeatEnabled ? 1 : 0); }
- set { HeartbeatEnabled = value == 1; }
- }
-
- ///
- ///
- ///
- public string HeartbeatString { get; set; }
- //public int HeartbeatInterval = 50000;
-
- ///
- /// Milliseconds before server expects another heartbeat. Set by property HeartbeatRequiredIntervalInSeconds which is driven from S+
- ///
- public int HeartbeatInterval { get; set; }
-
- ///
- /// Simpl+ Heartbeat Analog value in seconds
- ///
- public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatInterval = (value * 1000); } }
-
- System.Timers.Timer HeartbeatSendTimer;
- System.Timers.Timer HeartbeatAckTimer;
- ///
- /// Used to force disconnection on a dead connect attempt
- ///
- System.Timers.Timer ConnectFailTimer;
- System.Timers.Timer WaitForSharedKey;
- private int ConnectionCount;
- ///
- /// Internal secure client
- ///
- SecureTCPClient Client;
-
- bool ProgramIsStopping;
-
- ///
- /// Queue lock
- ///
- private readonly object _dequeueLock = new();
-
- ///
- /// Receive Queue size. Defaults to 20. Will set to 20 if QueueSize property is less than 20. Use constructor or set queue size property before
- /// calling initialize.
- ///
- public int ReceiveQueueSize { get; set; }
-
- ///
- /// Queue to temporarily store received messages with the source IP and Port info. Defaults to size 20. Use constructor or set queue size property before
- /// calling initialize.
- ///
- private CrestronQueue MessageQueue;
-
-
- #endregion
-
- #region Constructors
-
- ///
- /// Constructor
- ///
- ///
- ///
- ///
- ///
- public GenericSecureTcpIpClient_ForServer(string key, string address, int port, int bufferSize)
- : base(key)
- {
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- Hostname = address;
- Port = port;
- BufferSize = bufferSize;
- AutoReconnectIntervalMs = 5000;
-
- }
-
- ///
- /// Constructor for S+
- ///
- public GenericSecureTcpIpClient_ForServer()
- : base("Uninitialized Secure Tcp Client For Server")
- {
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- AutoReconnectIntervalMs = 5000;
- BufferSize = 2000;
- }
-
- ///
- /// Contstructor that sets all properties by calling the initialize method with a config object.
- ///
- ///
- ///
- public GenericSecureTcpIpClient_ForServer(string key, TcpClientConfigObject clientConfigObject)
- : base(key)
- {
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- Initialize(clientConfigObject);
- }
-
- #endregion
-
- #region Methods
-
- ///
- /// Initializes the client's key property, which is used to identify this client instance.
- ///
- /// The unique key that identifies this client instance.
- public void Initialize(string key)
- {
- Key = key;
- }
-
- ///
- /// Initialize called by the constructor that accepts a client config object. Can be called later to reset properties of client.
- ///
- /// The configuration object containing the client's settings.
- public void Initialize(TcpClientConfigObject clientConfigObject)
- {
- try
- {
- if (clientConfigObject != null)
+ set
{
- var TcpSshProperties = clientConfigObject.Control.TcpSshProperties;
- Hostname = TcpSshProperties.Address;
- AutoReconnect = TcpSshProperties.AutoReconnect;
- AutoReconnectIntervalMs = TcpSshProperties.AutoReconnectIntervalMs > 1000 ?
- TcpSshProperties.AutoReconnectIntervalMs : 5000;
- SharedKey = clientConfigObject.SharedKey;
- SharedKeyRequired = clientConfigObject.SharedKeyRequired;
- HeartbeatEnabled = clientConfigObject.HeartbeatRequired;
- HeartbeatRequiredIntervalInSeconds = clientConfigObject.HeartbeatRequiredIntervalInSeconds > 0 ?
- clientConfigObject.HeartbeatRequiredIntervalInSeconds : (ushort)15;
- HeartbeatString = string.IsNullOrEmpty(clientConfigObject.HeartbeatStringToMatch) ? "heartbeat" : clientConfigObject.HeartbeatStringToMatch;
- Port = TcpSshProperties.Port;
- BufferSize = TcpSshProperties.BufferSize > 2000 ? TcpSshProperties.BufferSize : 2000;
- ReceiveQueueSize = clientConfigObject.ReceiveQueueSize > 20 ? clientConfigObject.ReceiveQueueSize : 20;
- MessageQueue = new CrestronQueue(ReceiveQueueSize);
+ if (value == 1)
+ SharedKeyRequired = true;
+ else
+ SharedKeyRequired = false;
}
- else
+ }
+
+ ///
+ /// Gets or sets the SharedKey
+ ///
+ public string SharedKey { get; set; }
+
+ ///
+ /// flag to show the client is waiting for the server to send the shared key
+ ///
+ private bool WaitingForSharedKeyResponse { get; set; }
+
+ ///
+ /// Gets or sets the BufferSize
+ ///
+ public int BufferSize { get; set; }
+
+ ///
+ /// Semaphore on connect method
+ ///
+ bool IsTryingToConnect;
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsConnected
+ {
+ get
+ {
+ if (Client != null)
+ return Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED;
+ else
+ return false;
+ }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsConnected
+ {
+ get { return (ushort)(IsConnected ? 1 : 0); }
+ }
+
+ ///
+ /// Bool showing if socket is ready for communication after shared key exchange
+ ///
+ public bool IsReadyForCommunication { get; set; }
+
+ ///
+ /// S+ helper for IsReadyForCommunication
+ ///
+ public ushort UIsReadyForCommunication
+ {
+ get { return (ushort)(IsReadyForCommunication ? 1 : 0); }
+ }
+
+ ///
+ /// Client socket status Read only
+ ///
+ public SocketStatus ClientStatus
+ {
+ get
+ {
+ if (Client != null)
+ return Client.ClientStatus;
+ else
+ return SocketStatus.SOCKET_STATUS_NO_CONNECT;
+ }
+ }
+
+ ///
+ /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
+ /// and IsConnected would be true when this == 2.
+ ///
+ public ushort UStatus
+ {
+ get { return (ushort)ClientStatus; }
+ }
+
+ ///
+ /// Status text shows the message associated with socket status
+ ///
+ public string ClientStatusText { get { return ClientStatus.ToString(); } }
+
+ ///
+ /// bool to track if auto reconnect should be set on the socket
+ ///
+ public bool AutoReconnect { get; set; }
+
+ ///
+ /// S+ helper for AutoReconnect
+ ///
+ public ushort UAutoReconnect
+ {
+ get { return (ushort)(AutoReconnect ? 1 : 0); }
+ set { AutoReconnect = value == 1; }
+ }
+ ///
+ /// Milliseconds to wait before attempting to reconnect. Defaults to 5000
+ ///
+ public int AutoReconnectIntervalMs { get; set; }
+
+ ///
+ /// Flag Set only when the disconnect method is called.
+ ///
+ bool DisconnectCalledByUser;
+
+ ///
+ /// private Timer for auto reconnect
+ ///
+ CTimer RetryTimer;
+
+
+ ///
+ ///
+ ///
+ public bool HeartbeatEnabled { get; set; }
+ ///
+ ///
+ ///
+ public ushort UHeartbeatEnabled
+ {
+ get { return (ushort)(HeartbeatEnabled ? 1 : 0); }
+ set { HeartbeatEnabled = value == 1; }
+ }
+
+ ///
+ ///
+ ///
+ public string HeartbeatString { get; set; }
+ //public int HeartbeatInterval = 50000;
+
+ ///
+ /// Milliseconds before server expects another heartbeat. Set by property HeartbeatRequiredIntervalInSeconds which is driven from S+
+ ///
+ public int HeartbeatInterval { get; set; }
+
+ ///
+ /// Simpl+ Heartbeat Analog value in seconds
+ ///
+ public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatInterval = (value * 1000); } }
+
+ CTimer HeartbeatSendTimer;
+ CTimer HeartbeatAckTimer;
+ ///
+ /// Used to force disconnection on a dead connect attempt
+ ///
+ CTimer ConnectFailTimer;
+ CTimer WaitForSharedKey;
+ private int ConnectionCount;
+ ///
+ /// Internal secure client
+ ///
+ SecureTCPClient Client;
+
+ bool ProgramIsStopping;
+
+ ///
+ /// Queue lock
+ ///
+ CCriticalSection DequeueLock = new CCriticalSection();
+
+ ///
+ /// Receive Queue size. Defaults to 20. Will set to 20 if QueueSize property is less than 20. Use constructor or set queue size property before
+ /// calling initialize.
+ ///
+ public int ReceiveQueueSize { get; set; }
+
+ ///
+ /// Queue to temporarily store received messages with the source IP and Port info. Defaults to size 20. Use constructor or set queue size property before
+ /// calling initialize.
+ ///
+ private CrestronQueue MessageQueue;
+
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ ///
+ ///
+ public GenericSecureTcpIpClient_ForServer(string key, string address, int port, int bufferSize)
+ : base(key)
+ {
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ Hostname = address;
+ Port = port;
+ BufferSize = bufferSize;
+ AutoReconnectIntervalMs = 5000;
+
+ }
+
+ ///
+ /// Constructor for S+
+ ///
+ public GenericSecureTcpIpClient_ForServer()
+ : base("Uninitialized Secure Tcp Client For Server")
+ {
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ AutoReconnectIntervalMs = 5000;
+ BufferSize = 2000;
+ }
+
+ ///
+ /// Contstructor that sets all properties by calling the initialize method with a config object.
+ ///
+ ///
+ ///
+ public GenericSecureTcpIpClient_ForServer(string key, TcpClientConfigObject clientConfigObject)
+ : base(key)
+ {
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ Initialize(clientConfigObject);
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Initialize method
+ ///
+ public void Initialize(string key)
+ {
+ Key = key;
+ }
+
+ ///
+ /// Initialize called by the constructor that accepts a client config object. Can be called later to reset properties of client.
+ ///
+ ///
+ public void Initialize(TcpClientConfigObject clientConfigObject)
+ {
+ try
+ {
+ if (clientConfigObject != null)
+ {
+ var TcpSshProperties = clientConfigObject.Control.TcpSshProperties;
+ Hostname = TcpSshProperties.Address;
+ AutoReconnect = TcpSshProperties.AutoReconnect;
+ AutoReconnectIntervalMs = TcpSshProperties.AutoReconnectIntervalMs > 1000 ?
+ TcpSshProperties.AutoReconnectIntervalMs : 5000;
+ SharedKey = clientConfigObject.SharedKey;
+ SharedKeyRequired = clientConfigObject.SharedKeyRequired;
+ HeartbeatEnabled = clientConfigObject.HeartbeatRequired;
+ HeartbeatRequiredIntervalInSeconds = clientConfigObject.HeartbeatRequiredIntervalInSeconds > 0 ?
+ clientConfigObject.HeartbeatRequiredIntervalInSeconds : (ushort)15;
+ HeartbeatString = string.IsNullOrEmpty(clientConfigObject.HeartbeatStringToMatch) ? "heartbeat" : clientConfigObject.HeartbeatStringToMatch;
+ Port = TcpSshProperties.Port;
+ BufferSize = TcpSshProperties.BufferSize > 2000 ? TcpSshProperties.BufferSize : 2000;
+ ReceiveQueueSize = clientConfigObject.ReceiveQueueSize > 20 ? clientConfigObject.ReceiveQueueSize : 20;
+ MessageQueue = new CrestronQueue(ReceiveQueueSize);
+ }
+ else
+ {
+ ErrorLog.Error("Could not initialize client with key: {0}", Key);
+ }
+ }
+ catch
{
ErrorLog.Error("Could not initialize client with key: {0}", Key);
}
}
- catch
- {
- ErrorLog.Error("Could not initialize client with key: {0}", Key);
- }
- }
- ///
- /// Handles closing this up when the program shuts down
- ///
- void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
- {
- if (programEventType == eProgramStatusEventType.Stopping || programEventType == eProgramStatusEventType.Paused)
+ ///
+ /// Handles closing this up when the program shuts down
+ ///
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
{
- this.LogInformation("Program stopping. Closing Client connection");
- ProgramIsStopping = true;
- Disconnect();
- }
-
- }
-
- ///
- /// Connect Method. Will return if already connected. Will write errors if missing address, port, or unique key/name.
- ///
- public void Connect()
- {
- ConnectionCount++;
- this.LogVerbose("Attempting connect Count:{0}", ConnectionCount);
-
-
- if (IsConnected)
- {
- this.LogInformation("Already connected. Ignoring.");
- return;
- }
- if (IsTryingToConnect)
- {
- this.LogInformation("Already trying to connect. Ignoring.");
- return;
- }
- try
- {
- IsTryingToConnect = true;
- if (RetryTimer != null)
+ if (programEventType == eProgramStatusEventType.Stopping || programEventType == eProgramStatusEventType.Paused)
{
- RetryTimer.Stop();
- RetryTimer = null;
- }
- if (string.IsNullOrEmpty(Hostname))
- {
- this.LogWarning("DynamicTcpClient: No address set");
- return;
- }
- if (Port < 1 || Port > 65535)
- {
- this.LogWarning("DynamicTcpClient: Invalid port");
- return;
- }
- if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
- {
- this.LogWarning("DynamicTcpClient: No Shared Key set");
- return;
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing Client connection");
+ ProgramIsStopping = true;
+ Disconnect();
}
- // clean up previous client
- if (Client != null)
- {
- Cleanup();
- }
- DisconnectCalledByUser = false;
-
- Client = new SecureTCPClient(Hostname, Port, BufferSize);
- Client.SocketStatusChange += Client_SocketStatusChange;
- if (HeartbeatEnabled)
- Client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);
- Client.AddressClientConnectedTo = Hostname;
- Client.PortNumber = Port;
- // SecureClient = c;
-
- //var timeOfConnect = DateTime.Now.ToString("HH:mm:ss.fff");
-
- ConnectFailTimer = new System.Timers.Timer(30000) { AutoReset = false };
- ConnectFailTimer.Elapsed += (s, e) =>
- {
- this.LogError("Connect attempt has not finished after 30sec Count:{0}", ConnectionCount);
- if (IsTryingToConnect)
- {
- IsTryingToConnect = false;
-
- //if (ConnectionHasHungCallback != null)
- //{
- // ConnectionHasHungCallback();
- //}
- //SecureClient.DisconnectFromServer();
- //CheckClosedAndTryReconnect();
- }
- };
- ConnectFailTimer.Start();
-
- this.LogVerbose("Making Connection Count:{0}", ConnectionCount);
- Client.ConnectToServerAsync(o =>
- {
- this.LogVerbose("ConnectToServerAsync Count:{0} Ran!", ConnectionCount);
-
- if (ConnectFailTimer != null)
- {
- ConnectFailTimer.Stop();
- }
- IsTryingToConnect = false;
-
- if (o.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- this.LogVerbose("Client connected to {0} on port {1}", o.AddressClientConnectedTo, o.LocalPortNumberOfClient);
- o.ReceiveDataAsync(Receive);
-
- if (SharedKeyRequired)
- {
- WaitingForSharedKeyResponse = true;
- WaitForSharedKey = new System.Timers.Timer(15000) { AutoReset = false };
- WaitForSharedKey.Elapsed += (s, e) =>
- {
- this.LogWarning("Shared key exchange timer expired. IsReadyForCommunication={0}", IsReadyForCommunication);
- // This is the only case where we should call DisconectFromServer...Event handeler will trigger the cleanup
- o.DisconnectFromServer();
- //CheckClosedAndTryReconnect();
- //OnClientReadyForcommunications(false); // Should send false event
- };
- WaitForSharedKey.Start();
- }
- else
- {
- //CLient connected and shared key is not required so just raise the ready for communication event. if Shared key
- //required this is called by the shared key being negotiated
- if (IsReadyForCommunication == false)
- {
- OnClientReadyForcommunications(true); // Key not required
- }
- }
- }
- else
- {
- this.LogWarning("Connect attempt failed {0}", o.ClientStatus);
- CheckClosedAndTryReconnect();
- }
- });
- }
- catch (Exception ex)
- {
- this.LogError("Client connection exception: {0}", ex.Message);
- IsTryingToConnect = false;
- CheckClosedAndTryReconnect();
- }
- }
-
- ///
- ///
- ///
- public void Disconnect()
- {
- this.LogVerbose("Disconnect Called");
-
- DisconnectCalledByUser = true;
- if (IsConnected)
- {
- Client.DisconnectFromServer();
-
- }
- if (RetryTimer != null)
- {
- RetryTimer.Stop();
- RetryTimer = null;
- }
- Cleanup();
- }
-
- ///
- /// Internal call to close up client. ALWAYS use this when disconnecting.
- ///
- void Cleanup()
- {
- IsTryingToConnect = false;
-
- if (Client != null)
- {
- //SecureClient.DisconnectFromServer();
- this.LogVerbose("Disconnecting Client {0}", DisconnectCalledByUser ? ", Called by user" : "");
- Client.SocketStatusChange -= Client_SocketStatusChange;
- Client.Dispose();
- Client = null;
- }
- if (ConnectFailTimer != null)
- {
- ConnectFailTimer.Stop();
- ConnectFailTimer.Dispose();
- ConnectFailTimer = null;
- }
- }
-
-
- /// ff
- /// Called from Connect failure or Socket Status change if
- /// auto reconnect and socket disconnected (Not disconnected by user)
- ///
- void CheckClosedAndTryReconnect()
- {
- if (Client != null)
- {
- this.LogVerbose("Cleaning up remotely closed/failed connection.");
- Cleanup();
- }
- if (!DisconnectCalledByUser && AutoReconnect)
- {
- var halfInterval = AutoReconnectIntervalMs / 2;
- var rndTime = new Random().Next(-halfInterval, halfInterval) + AutoReconnectIntervalMs;
- this.LogVerbose("Attempting reconnect in {0} ms, randomized", rndTime);
- if (RetryTimer != null)
- {
- RetryTimer.Stop();
- RetryTimer = null;
- }
- if (AutoReconnectTriggered != null)
- AutoReconnectTriggered(this, new EventArgs());
- RetryTimer = new System.Timers.Timer(rndTime) { AutoReset = false };
- RetryTimer.Elapsed += (s, e) => Connect();
- RetryTimer.Start();
- }
- }
-
- ///
- /// Receive callback
- ///
- ///
- ///
- void Receive(SecureTCPClient client, int numBytes)
- {
- if (numBytes > 0)
- {
- string str = string.Empty;
- var handler = TextReceivedQueueInvoke;
- try
- {
- var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
- str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
- this.LogVerbose("Client Received:\r--------\r{0}\r--------", str);
- if (!string.IsNullOrEmpty(checkHeartbeat(str)))
- {
-
- if (SharedKeyRequired && str == "SharedKey:")
- {
- this.LogVerbose("Server asking for shared key, sending");
- SendText(SharedKey + "\n");
- }
- else if (SharedKeyRequired && str == "Shared Key Match")
- {
- StopWaitForSharedKeyTimer();
-
-
- this.LogVerbose("Shared key confirmed. Ready for communication");
- OnClientReadyForcommunications(true); // Successful key exchange
- }
- else
- {
- //var bytesHandler = BytesReceived;
- //if (bytesHandler != null)
- // bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
- var textHandler = TextReceived;
- if (textHandler != null)
- textHandler(this, new GenericTcpServerCommMethodReceiveTextArgs(str));
- if (handler != null)
- {
- MessageQueue.TryToEnqueue(new GenericTcpServerCommMethodReceiveTextArgs(str));
- }
- }
- }
- }
- catch (Exception ex)
- {
- this.LogError("Error receiving data: {1}. Error: {0}", ex.Message, str);
- }
- if (client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
- client.ReceiveDataAsync(Receive);
-
- //Check to see if there is a subscription to the TextReceivedQueueInvoke event. If there is start the dequeue thread.
- if (handler != null)
- {
- if (Monitor.TryEnter(_dequeueLock))
- Task.Run(() => DequeueEvent());
- }
- }
- else //JAG added this as I believe the error return is 0 bytes like the server. See help when hover on ReceiveAsync
- {
- client.DisconnectFromServer();
- }
- }
-
- ///
- /// This method gets spooled up in its own thread an protected by a lock to prevent multiple threads from running concurrently.
- /// It will dequeue items as they are enqueued automatically.
- ///
- void DequeueEvent()
- {
- try
- {
- while (true)
- {
- // Pull from Queue and fire an event. Block indefinitely until an item can be removed, similar to a Gather.
- var message = MessageQueue.Dequeue();
- var handler = TextReceivedQueueInvoke;
- if (handler != null)
- {
- handler(this, message);
- }
- }
- }
- catch (Exception e)
- {
- this.LogError("DequeueEvent error: {0}", e.Message, e);
- }
- // Make sure to release the lock in case an exception above stops this thread, or we won't be able to restart it.
- Monitor.Exit(_dequeueLock);
- }
-
- void HeartbeatStart()
- {
- if (HeartbeatEnabled)
- {
- this.LogVerbose("Starting Heartbeat");
- if (HeartbeatSendTimer == null)
- {
-
- HeartbeatSendTimer = new System.Timers.Timer(HeartbeatInterval) { AutoReset = true };
- HeartbeatSendTimer.Elapsed += (s, e) => SendHeartbeat(null);
- HeartbeatSendTimer.Start();
- }
- if (HeartbeatAckTimer == null)
- {
- HeartbeatAckTimer = new System.Timers.Timer(HeartbeatInterval * 2) { AutoReset = true };
- HeartbeatAckTimer.Elapsed += (s, e) => HeartbeatAckTimerFail(null);
- HeartbeatAckTimer.Start();
- }
}
- }
- void HeartbeatStop()
- {
-
- if (HeartbeatSendTimer != null)
+ ///
+ /// Connect method
+ ///
+ public void Connect()
{
- this.LogVerbose("Stopping Heartbeat Send");
- HeartbeatSendTimer.Stop();
- HeartbeatSendTimer = null;
- }
- if (HeartbeatAckTimer != null)
- {
- this.LogVerbose("Stopping Heartbeat Ack");
- HeartbeatAckTimer.Stop();
- HeartbeatAckTimer = null;
- }
+ ConnectionCount++;
+ Debug.Console(2, this, "Attempting connect Count:{0}", ConnectionCount);
- }
- void SendHeartbeat(object notused)
- {
- this.SendText(HeartbeatString);
- this.LogVerbose("Sending Heartbeat");
-
- }
-
- //private method to check heartbeat requirements and start or reset timer
- string checkHeartbeat(string received)
- {
- try
- {
- if (HeartbeatEnabled)
- {
- if (!string.IsNullOrEmpty(HeartbeatString))
- {
- var remainingText = received.Replace(HeartbeatString, "");
- var noDelimiter = received.Trim(new char[] { '\r', '\n' });
- if (noDelimiter.Contains(HeartbeatString))
- {
- if (HeartbeatAckTimer != null)
- {
- HeartbeatAckTimer.Stop();
- HeartbeatAckTimer.Interval = HeartbeatInterval * 2;
- HeartbeatAckTimer.Start();
- }
- else
- {
- HeartbeatAckTimer = new System.Timers.Timer(HeartbeatInterval * 2) { AutoReset = true };
- HeartbeatAckTimer.Elapsed += (s, e) => HeartbeatAckTimerFail(null);
- HeartbeatAckTimer.Start();
- }
- this.LogVerbose("Heartbeat Received: {0}, from Server", HeartbeatString);
- return remainingText;
- }
- }
- }
- }
- catch (Exception ex)
- {
- this.LogError("Error checking heartbeat: {0}", ex.Message, ex);
- }
- return received;
- }
-
-
-
- void HeartbeatAckTimerFail(object o)
- {
- try
- {
if (IsConnected)
{
- this.LogWarning("Heartbeat not received from Server...DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE");
- SendText("Heartbeat not received by server, closing connection");
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Already connected. Ignoring.");
+ return;
+ }
+ if (IsTryingToConnect)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Already trying to connect. Ignoring.");
+ return;
+ }
+ try
+ {
+ IsTryingToConnect = true;
+ if (RetryTimer != null)
+ {
+ RetryTimer.Stop();
+ RetryTimer = null;
+ }
+ if (string.IsNullOrEmpty(Hostname))
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: No address set");
+ return;
+ }
+ if (Port < 1 || Port > 65535)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: Invalid port");
+ return;
+ }
+ if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: No Shared Key set");
+ return;
+ }
+
+ // clean up previous client
+ if (Client != null)
+ {
+ Cleanup();
+ }
+ DisconnectCalledByUser = false;
+
+ Client = new SecureTCPClient(Hostname, Port, BufferSize);
+ Client.SocketStatusChange += Client_SocketStatusChange;
+ if (HeartbeatEnabled)
+ Client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);
+ Client.AddressClientConnectedTo = Hostname;
+ Client.PortNumber = Port;
+ // SecureClient = c;
+
+ //var timeOfConnect = DateTime.Now.ToString("HH:mm:ss.fff");
+
+ ConnectFailTimer = new CTimer(o =>
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Connect attempt has not finished after 30sec Count:{0}", ConnectionCount);
+ if (IsTryingToConnect)
+ {
+ IsTryingToConnect = false;
+
+ //if (ConnectionHasHungCallback != null)
+ //{
+ // ConnectionHasHungCallback();
+ //}
+ //SecureClient.DisconnectFromServer();
+ //CheckClosedAndTryReconnect();
+ }
+ }, 30000);
+
+ Debug.Console(2, this, "Making Connection Count:{0}", ConnectionCount);
+ Client.ConnectToServerAsync(o =>
+ {
+ Debug.Console(2, this, "ConnectToServerAsync Count:{0} Ran!", ConnectionCount);
+
+ if (ConnectFailTimer != null)
+ {
+ ConnectFailTimer.Stop();
+ }
+ IsTryingToConnect = false;
+
+ if (o.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ Debug.Console(2, this, "Client connected to {0} on port {1}", o.AddressClientConnectedTo, o.LocalPortNumberOfClient);
+ o.ReceiveDataAsync(Receive);
+
+ if (SharedKeyRequired)
+ {
+ WaitingForSharedKeyResponse = true;
+ WaitForSharedKey = new CTimer(timer =>
+ {
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Shared key exchange timer expired. IsReadyForCommunication={0}", IsReadyForCommunication);
+ // Debug.Console(1, this, "Connect attempt failed {0}", c.ClientStatus);
+ // This is the only case where we should call DisconectFromServer...Event handeler will trigger the cleanup
+ o.DisconnectFromServer();
+ //CheckClosedAndTryReconnect();
+ //OnClientReadyForcommunications(false); // Should send false event
+ }, 15000);
+ }
+ else
+ {
+ //CLient connected and shared key is not required so just raise the ready for communication event. if Shared key
+ //required this is called by the shared key being negotiated
+ if (IsReadyForCommunication == false)
+ {
+ OnClientReadyForcommunications(true); // Key not required
+ }
+ }
+ }
+ else
+ {
+ Debug.Console(1, this, "Connect attempt failed {0}", o.ClientStatus);
+ CheckClosedAndTryReconnect();
+ }
+ });
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Client connection exception: {0}", ex.Message);
+ IsTryingToConnect = false;
CheckClosedAndTryReconnect();
}
-
}
- catch (Exception ex)
+
+ ///
+ /// Disconnect method
+ ///
+ public void Disconnect()
{
- this.LogError("Heartbeat timeout Error on Client: {0}, {1}", Key, ex);
- }
- }
+ this.LogVerbose("Disconnect Called");
- ///
- ///
- ///
- void StopWaitForSharedKeyTimer()
- {
- if (WaitForSharedKey != null)
+ DisconnectCalledByUser = true;
+ if (IsConnected)
+ {
+ Client.DisconnectFromServer();
+
+ }
+ if (RetryTimer != null)
+ {
+ RetryTimer.Stop();
+ RetryTimer = null;
+ }
+ Cleanup();
+ }
+
+ ///
+ /// Internal call to close up client. ALWAYS use this when disconnecting.
+ ///
+ void Cleanup()
{
- WaitForSharedKey.Stop();
- WaitForSharedKey = null;
- }
- }
+ IsTryingToConnect = false;
- ///
- /// General send method
- ///
- public void SendText(string text)
- {
- if (!string.IsNullOrEmpty(text))
+ if (Client != null)
+ {
+ //SecureClient.DisconnectFromServer();
+ Debug.Console(2, this, "Disconnecting Client {0}", DisconnectCalledByUser ? ", Called by user" : "");
+ Client.SocketStatusChange -= Client_SocketStatusChange;
+ Client.Dispose();
+ Client = null;
+ }
+ if (ConnectFailTimer != null)
+ {
+ ConnectFailTimer.Stop();
+ ConnectFailTimer.Dispose();
+ ConnectFailTimer = null;
+ }
+ }
+
+
+ /// ff
+ /// Called from Connect failure or Socket Status change if
+ /// auto reconnect and socket disconnected (Not disconnected by user)
+ ///
+ void CheckClosedAndTryReconnect()
+ {
+ if (Client != null)
+ {
+ Debug.Console(2, this, "Cleaning up remotely closed/failed connection.");
+ Cleanup();
+ }
+ if (!DisconnectCalledByUser && AutoReconnect)
+ {
+ var halfInterval = AutoReconnectIntervalMs / 2;
+ var rndTime = new Random().Next(-halfInterval, halfInterval) + AutoReconnectIntervalMs;
+ Debug.Console(2, this, "Attempting reconnect in {0} ms, randomized", rndTime);
+ if (RetryTimer != null)
+ {
+ RetryTimer.Stop();
+ RetryTimer = null;
+ }
+ if(AutoReconnectTriggered != null)
+ AutoReconnectTriggered(this, new EventArgs());
+ RetryTimer = new CTimer(o => Connect(), rndTime);
+ }
+ }
+
+ ///
+ /// Receive callback
+ ///
+ ///
+ ///
+ void Receive(SecureTCPClient client, int numBytes)
+ {
+ if (numBytes > 0)
+ {
+ string str = string.Empty;
+ var handler = TextReceivedQueueInvoke;
+ try
+ {
+ var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
+ str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
+ Debug.Console(2, this, "Client Received:\r--------\r{0}\r--------", str);
+ if (!string.IsNullOrEmpty(checkHeartbeat(str)))
+ {
+
+ if (SharedKeyRequired && str == "SharedKey:")
+ {
+ Debug.Console(2, this, "Server asking for shared key, sending");
+ SendText(SharedKey + "\n");
+ }
+ else if (SharedKeyRequired && str == "Shared Key Match")
+ {
+ StopWaitForSharedKeyTimer();
+
+
+ Debug.Console(2, this, "Shared key confirmed. Ready for communication");
+ OnClientReadyForcommunications(true); // Successful key exchange
+ }
+ else
+ {
+ //var bytesHandler = BytesReceived;
+ //if (bytesHandler != null)
+ // bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
+ var textHandler = TextReceived;
+ if (textHandler != null)
+ textHandler(this, new GenericTcpServerCommMethodReceiveTextArgs(str));
+ if (handler != null)
+ {
+ MessageQueue.TryToEnqueue(new GenericTcpServerCommMethodReceiveTextArgs(str));
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(1, this, "Error receiving data: {1}. Error: {0}", ex.Message, str);
+ }
+ if (client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ client.ReceiveDataAsync(Receive);
+
+ //Check to see if there is a subscription to the TextReceivedQueueInvoke event. If there is start the dequeue thread.
+ if (handler != null)
+ {
+ var gotLock = DequeueLock.TryEnter();
+ if (gotLock)
+ CrestronInvoke.BeginInvoke((o) => DequeueEvent());
+ }
+ }
+ else //JAG added this as I believe the error return is 0 bytes like the server. See help when hover on ReceiveAsync
+ {
+ client.DisconnectFromServer();
+ }
+ }
+
+ ///
+ /// This method gets spooled up in its own thread an protected by a CCriticalSection to prevent multiple threads from running concurrently.
+ /// It will dequeue items as they are enqueued automatically.
+ ///
+ void DequeueEvent()
{
try
{
- var bytes = Encoding.GetEncoding(28591).GetBytes(text);
- if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ while (true)
{
- Client.SendDataAsync(bytes, bytes.Length, (c, n) =>
+ // Pull from Queue and fire an event. Block indefinitely until an item can be removed, similar to a Gather.
+ var message = MessageQueue.Dequeue();
+ var handler = TextReceivedQueueInvoke;
+ if (handler != null)
{
- // HOW IN THE HELL DO WE CATCH AN EXCEPTION IN SENDING?????
- if (n <= 0)
+ handler(this, message);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ this.LogException(e, "DequeueEvent error");
+ }
+ // Make sure to leave the CCritical section in case an exception above stops this thread, or we won't be able to restart it.
+ if (DequeueLock != null)
+ {
+ DequeueLock.Leave();
+ }
+ }
+
+ void HeartbeatStart()
+ {
+ if (HeartbeatEnabled)
+ {
+ Debug.Console(2, this, "Starting Heartbeat");
+ if (HeartbeatSendTimer == null)
+ {
+
+ HeartbeatSendTimer = new CTimer(this.SendHeartbeat, null, HeartbeatInterval, HeartbeatInterval);
+ }
+ if (HeartbeatAckTimer == null)
+ {
+ HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
+ }
+ }
+
+ }
+ void HeartbeatStop()
+ {
+
+ if (HeartbeatSendTimer != null)
+ {
+ Debug.Console(2, this, "Stoping Heartbeat Send");
+ HeartbeatSendTimer.Stop();
+ HeartbeatSendTimer = null;
+ }
+ if (HeartbeatAckTimer != null)
+ {
+ Debug.Console(2, this, "Stoping Heartbeat Ack");
+ HeartbeatAckTimer.Stop();
+ HeartbeatAckTimer = null;
+ }
+
+ }
+ void SendHeartbeat(object notused)
+ {
+ this.SendText(HeartbeatString);
+ Debug.Console(2, this, "Sending Heartbeat");
+
+ }
+
+ //private method to check heartbeat requirements and start or reset timer
+ string checkHeartbeat(string received)
+ {
+ try
+ {
+ if (HeartbeatEnabled)
+ {
+ if (!string.IsNullOrEmpty(HeartbeatString))
+ {
+ var remainingText = received.Replace(HeartbeatString, "");
+ var noDelimiter = received.Trim(new char[] { '\r', '\n' });
+ if (noDelimiter.Contains(HeartbeatString))
{
- this.LogWarning("[{0}] Sent zero bytes. Was there an error?", this.Key);
+ if (HeartbeatAckTimer != null)
+ {
+ HeartbeatAckTimer.Reset(HeartbeatInterval * 2);
+ }
+ else
+ {
+ HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
+ }
+ Debug.Console(2, this, "Heartbeat Received: {0}, from Server", HeartbeatString);
+ return remainingText;
}
- });
+ }
}
}
catch (Exception ex)
{
- this.LogError("Error sending text: {1}. Error: {0}", text, ex.Message);
+ Debug.Console(1, this, "Error checking heartbeat: {0}", ex.Message);
}
+ return received;
}
- }
- ///
- ///
- ///
- public void SendBytes(byte[] bytes)
- {
- if (bytes.Length > 0)
+
+
+ void HeartbeatAckTimerFail(object o)
{
try
{
- if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
- Client.SendData(bytes, bytes.Length);
+
+ if (IsConnected)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "Heartbeat not received from Server...DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE");
+ SendText("Heartbeat not received by server, closing connection");
+ CheckClosedAndTryReconnect();
+ }
+
}
catch (Exception ex)
{
- this.LogError("Error sending bytes. Error: {0}", ex.Message, ex);
+ ErrorLog.Error("Heartbeat timeout Error on Client: {0}, {1}", Key, ex);
}
}
- }
- ///
- /// SocketStatusChange Callback
- ///
- ///
- ///
- void Client_SocketStatusChange(SecureTCPClient client, SocketStatus clientSocketStatus)
- {
- if (ProgramIsStopping)
+ ///
+ ///
+ ///
+ void StopWaitForSharedKeyTimer()
{
- ProgramIsStopping = false;
- return;
- }
- try
- {
- this.LogDebug("Socket status change: {0} ({1})", client.ClientStatus, (ushort)(client.ClientStatus));
-
- OnConnectionChange();
- // The client could be null or disposed by this time...
- if (Client == null || Client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ if (WaitForSharedKey != null)
{
- HeartbeatStop();
- OnClientReadyForcommunications(false); // socket has gone low
- CheckClosedAndTryReconnect();
+ WaitForSharedKey.Stop();
+ WaitForSharedKey = null;
}
}
- catch (Exception ex)
+
+ ///
+ /// SendText method
+ ///
+ public void SendText(string text)
{
- this.LogError("Error in socket status change callback. Error: {0}\r\r{1}", ex, ex.InnerException);
+ if (!string.IsNullOrEmpty(text))
+ {
+ try
+ {
+ var bytes = Encoding.GetEncoding(28591).GetBytes(text);
+ if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ Client.SendDataAsync(bytes, bytes.Length, (c, n) =>
+ {
+ // HOW IN THE HELL DO WE CATCH AN EXCEPTION IN SENDING?????
+ if (n <= 0)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "[{0}] Sent zero bytes. Was there an error?", this.Key);
+ }
+ });
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, "Error sending text: {1}. Error: {0}", ex.Message, text);
+ }
+ }
}
+
+ ///
+ /// SendBytes method
+ ///
+ public void SendBytes(byte[] bytes)
+ {
+ if (bytes.Length > 0)
+ {
+ try
+ {
+ if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ Client.SendData(bytes, bytes.Length);
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, "Error sending bytes. Error: {0}", ex.Message);
+ }
+ }
+ }
+
+ ///
+ /// SocketStatusChange Callback
+ ///
+ ///
+ ///
+ void Client_SocketStatusChange(SecureTCPClient client, SocketStatus clientSocketStatus)
+ {
+ if (ProgramIsStopping)
+ {
+ ProgramIsStopping = false;
+ return;
+ }
+ try
+ {
+ Debug.Console(2, this, "Socket status change: {0} ({1})", client.ClientStatus, (ushort)(client.ClientStatus));
+
+ OnConnectionChange();
+ // The client could be null or disposed by this time...
+ if (Client == null || Client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ HeartbeatStop();
+ OnClientReadyForcommunications(false); // socket has gone low
+ CheckClosedAndTryReconnect();
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Error in socket status change callback. Error: {0}\r\r{1}", ex, ex.InnerException);
+ }
+ }
+
+ ///
+ /// Helper for ConnectionChange event
+ ///
+ void OnConnectionChange()
+ {
+ var handler = ConnectionChange;
+ if (handler != null)
+ ConnectionChange(this, new GenericTcpServerSocketStatusChangeEventArgs(this, Client.ClientStatus));
+ }
+
+ ///
+ /// Helper to fire ClientReadyForCommunications event
+ ///
+ void OnClientReadyForcommunications(bool isReady)
+ {
+ IsReadyForCommunication = isReady;
+ if (this.IsReadyForCommunication) { HeartbeatStart(); }
+ var handler = ClientReadyForCommunications;
+ if (handler != null)
+ handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));
+ }
+ #endregion
}
- ///
- /// Helper for ConnectionChange event
- ///
- void OnConnectionChange()
- {
- var handler = ConnectionChange;
- if (handler != null)
- ConnectionChange(this, new GenericTcpServerSocketStatusChangeEventArgs(this, Client.ClientStatus));
- }
-
- ///
- /// Helper to fire ClientReadyForCommunications event
- ///
- void OnClientReadyForcommunications(bool isReady)
- {
- IsReadyForCommunication = isReady;
- if (this.IsReadyForCommunication) { HeartbeatStart(); }
- var handler = ClientReadyForCommunications;
- if (handler != null)
- handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));
- }
- #endregion
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Comm/GenericSecureTcpIpServer.cs b/src/PepperDash.Core/Comm/GenericSecureTcpIpServer.cs
index fafafbdc..6502d544 100644
--- a/src/PepperDash.Core/Comm/GenericSecureTcpIpServer.cs
+++ b/src/PepperDash.Core/Comm/GenericSecureTcpIpServer.cs
@@ -1,415 +1,421 @@
-using System;
+/*PepperDash Technology Corp.
+JAG
+Copyright: 2017
+------------------------------------
+***Notice of Ownership and Copyright***
+The material in which this notice appears is the property of PepperDash Technology Corporation,
+which claims copyright under the laws of the United States of America in the entire body of material
+and in all parts thereof, regardless of the use to which it is being put. Any use, in whole or in part,
+of this material by another party without the express written permission of PepperDash Technology Corporation is prohibited.
+PepperDash Technology Corporation reserves all rights under applicable laws.
+------------------------------------ */
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
-using System.Timers;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronSockets;
using PepperDash.Core.Logging;
-namespace PepperDash.Core;
-
-///
-/// Generic secure TCP/IP server
-///
-public class GenericSecureTcpIpServer : Device
+namespace PepperDash.Core
{
- #region Events
///
- /// Event for Receiving text
+ /// Generic secure TCP/IP server
///
- public event EventHandler TextReceived;
-
- ///
- /// Event for Receiving text. Once subscribed to this event the receive callback will start a thread that dequeues the messages and invokes the event on a new thread.
- /// It is not recommended to use both the TextReceived event and the TextReceivedQueueInvoke event.
- ///
- public event EventHandler TextReceivedQueueInvoke;
-
- ///
- /// Event for client connection socket status change
- ///
- public event EventHandler ClientConnectionChange;
-
- ///
- /// Event for Server State Change
- ///
- public event EventHandler ServerStateChange;
-
- ///
- /// For a server with a pre shared key, this will fire after the communication is established and the key exchange is complete. If no shared key, this will fire
- /// after connection is successful. Use this event to know when the client is ready for communication to avoid stepping on shared key.
- ///
- public event EventHandler ServerClientReadyForCommunications;
-
- ///
- /// A band aid event to notify user that the server has choked.
- ///
- public ServerHasChokedCallbackDelegate ServerHasChoked { get; set; }
-
- ///
- ///
- ///
- public delegate void ServerHasChokedCallbackDelegate();
-
- #endregion
-
- #region Properties/Variables
-
- ///
- /// Server listen lock
- ///
- private readonly object _serverLock = new();
-
- ///
- /// Queue lock
- ///
- private readonly object _dequeueLock = new();
-
- ///
- /// Broadcast lock
- ///
- private readonly object _broadcastLock = new();
-
- ///
- /// Receive Queue size. Defaults to 20. Will set to 20 if QueueSize property is less than 20. Use constructor or set queue size property before
- /// calling initialize.
- ///
- public int ReceiveQueueSize { get; set; }
-
- ///
- /// Queue to temporarily store received messages with the source IP and Port info. Defaults to size 20. Use constructor or set queue size property before
- /// calling initialize.
- ///
- private CrestronQueue MessageQueue;
-
- ///
- /// A bandaid client that monitors whether the server is reachable
- ///
- GenericSecureTcpIpClient_ForServer MonitorClient;
-
- ///
- /// Timer to operate the bandaid monitor client in a loop.
- ///
- Timer MonitorClientTimer;
-
- ///
- ///
- ///
- int MonitorClientFailureCount;
-
- ///
- /// 3 by default
- ///
- public int MonitorClientMaxFailureCount { get; set; }
-
- ///
- /// Text representation of the Socket Status enum values for the server
- ///
- public string Status
+ public class GenericSecureTcpIpServer : Device
{
- get
+ #region Events
+ ///
+ /// Event for Receiving text
+ ///
+ public event EventHandler TextReceived;
+
+ ///
+ /// Event for Receiving text. Once subscribed to this event the receive callback will start a thread that dequeues the messages and invokes the event on a new thread.
+ /// It is not recommended to use both the TextReceived event and the TextReceivedQueueInvoke event.
+ ///
+ public event EventHandler TextReceivedQueueInvoke;
+
+ ///
+ /// Event for client connection socket status change
+ ///
+ public event EventHandler ClientConnectionChange;
+
+ ///
+ /// Event for Server State Change
+ ///
+ public event EventHandler ServerStateChange;
+
+ ///
+ /// For a server with a pre shared key, this will fire after the communication is established and the key exchange is complete. If no shared key, this will fire
+ /// after connection is successful. Use this event to know when the client is ready for communication to avoid stepping on shared key.
+ ///
+ public event EventHandler ServerClientReadyForCommunications;
+
+ ///
+ /// A band aid event to notify user that the server has choked.
+ ///
+ public ServerHasChokedCallbackDelegate ServerHasChoked { get; set; }
+
+ ///
+ /// Delegate for ServerHasChokedCallbackDelegate
+ ///
+ public delegate void ServerHasChokedCallbackDelegate();
+
+ #endregion
+
+ #region Properties/Variables
+
+ ///
+ /// Server listen lock
+ ///
+ CCriticalSection ServerCCSection = new CCriticalSection();
+
+ ///
+ /// Queue lock
+ ///
+ CCriticalSection DequeueLock = new CCriticalSection();
+
+ ///
+ /// Receive Queue size. Defaults to 20. Will set to 20 if QueueSize property is less than 20. Use constructor or set queue size property before
+ /// calling initialize.
+ ///
+ public int ReceiveQueueSize { get; set; }
+
+ ///
+ /// Queue to temporarily store received messages with the source IP and Port info. Defaults to size 20. Use constructor or set queue size property before
+ /// calling initialize.
+ ///
+ private CrestronQueue MessageQueue;
+
+ ///
+ /// A bandaid client that monitors whether the server is reachable
+ ///
+ GenericSecureTcpIpClient_ForServer MonitorClient;
+
+ ///
+ /// Timer to operate the bandaid monitor client in a loop.
+ ///
+ CTimer MonitorClientTimer;
+
+ ///
+ ///
+ ///
+ int MonitorClientFailureCount;
+
+ ///
+ /// Gets or sets the MonitorClientMaxFailureCount
+ ///
+ public int MonitorClientMaxFailureCount { get; set; }
+
+ ///
+ /// Text representation of the Socket Status enum values for the server
+ ///
+ public string Status
{
- if (SecureServer != null)
- return SecureServer.State.ToString();
- return ServerState.SERVER_NOT_LISTENING.ToString();
-
- }
-
- }
-
- ///
- /// Bool showing if socket is connected
- ///
- public bool IsConnected
- {
- get
- {
- if (SecureServer != null)
- return (SecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED;
- return false;
-
- //return (Secure ? SecureServer != null : UnsecureServer != null) &&
- //(Secure ? (SecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED :
- // (UnsecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED);
- }
- }
-
- ///
- /// S+ helper for IsConnected
- ///
- public ushort UIsConnected
- {
- get { return (ushort)(IsConnected ? 1 : 0); }
- }
-
- ///
- /// Bool showing if socket is connected
- ///
- public bool IsListening
- {
- get
- {
- if (SecureServer != null)
- return (SecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING;
- else
- return false;
- //return (Secure ? SecureServer != null : UnsecureServer != null) &&
- //(Secure ? (SecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING :
- // (UnsecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING);
- }
- }
-
- ///
- /// S+ helper for IsConnected
- ///
- public ushort UIsListening
- {
- get { return (ushort)(IsListening ? 1 : 0); }
- }
- ///
- /// Max number of clients this server will allow for connection. Crestron max is 64. This number should be less than 65
- ///
- public ushort MaxClients { get; set; } // should be set by parameter in SIMPL+ in the MAIN method, Should not ever need to be configurable
- ///
- /// Number of clients currently connected.
- ///
- public ushort NumberOfClientsConnected
- {
- get
- {
- if (SecureServer != null)
- return (ushort)SecureServer.NumberOfClientsConnected;
- return 0;
- }
- }
-
- ///
- /// Port Server should listen on
- ///
- public int Port { get; set; }
-
- ///
- /// S+ helper for Port
- ///
- public ushort UPort
- {
- get { return Convert.ToUInt16(Port); }
- set { Port = Convert.ToInt32(value); }
- }
-
- ///
- /// Bool to show whether the server requires a preshared key. Must be set the same in the client, and if true shared keys must be identical on server/client
- ///
- public bool SharedKeyRequired { get; set; }
-
- ///
- /// S+ helper for requires shared key bool
- ///
- public ushort USharedKeyRequired
- {
- set
- {
- if (value == 1)
- SharedKeyRequired = true;
- else
- SharedKeyRequired = false;
- }
- }
-
- ///
- /// SharedKey is sent for varification to the server. Shared key can be any text (255 char limit in SIMPL+ Module), but must match the Shared Key on the Server module.
- /// If SharedKey changes while server is listening or clients are connected, disconnect and stop listening will be called
- ///
- public string SharedKey { get; set; }
-
- ///
- /// Heartbeat Required bool sets whether server disconnects client if heartbeat is not received
- ///
- public bool HeartbeatRequired { get; set; }
-
- ///
- /// S+ Helper for Heartbeat Required
- ///
- public ushort UHeartbeatRequired
- {
- set
- {
- if (value == 1)
- HeartbeatRequired = true;
- else
- HeartbeatRequired = false;
- }
- }
-
- ///
- /// Milliseconds before server expects another heartbeat. Set by property HeartbeatRequiredIntervalInSeconds which is driven from S+
- ///
- public int HeartbeatRequiredIntervalMs { get; set; }
-
- ///
- /// Simpl+ Heartbeat Analog value in seconds
- ///
- public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatRequiredIntervalMs = (value * 1000); } }
-
- ///
- /// String to Match for heartbeat. If null or empty any string will reset heartbeat timer
- ///
- public string HeartbeatStringToMatch { get; set; }
-
- //private timers for Heartbeats per client
- Dictionary HeartbeatTimerDictionary = new Dictionary();
-
- //flags to show the secure server is waiting for client at index to send the shared key
- List WaitingForSharedKey = new List();
-
- List ClientReadyAfterKeyExchange = new List();
-
- ///
- /// The connected client indexes
- ///
- public List ConnectedClientsIndexes = new List();
-
- ///
- /// Defaults to 2000
- ///
- public int BufferSize { get; set; }
-
- ///
- /// Private flag to note that the server has stopped intentionally
- ///
- private bool ServerStopped { get; set; }
-
- //Servers
- SecureTCPServer SecureServer;
-
- ///
- ///
- ///
- bool ProgramIsStopping;
-
- #endregion
-
- #region Constructors
- ///
- /// constructor S+ Does not accept a key. Use initialze with key to set the debug key on this device. If using with + make sure to set all properties manually.
- ///
- public GenericSecureTcpIpServer()
- : base("Uninitialized Secure TCP Server")
- {
- HeartbeatRequiredIntervalInSeconds = 15;
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- BufferSize = 2000;
- MonitorClientMaxFailureCount = 3;
- }
-
- ///
- /// constructor with debug key set at instantiation. Make sure to set all properties before listening.
- ///
- ///
- public GenericSecureTcpIpServer(string key)
- : base("Uninitialized Secure TCP Server")
- {
- HeartbeatRequiredIntervalInSeconds = 15;
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- BufferSize = 2000;
- MonitorClientMaxFailureCount = 3;
- Key = key;
- }
-
- ///
- /// Contstructor that sets all properties by calling the initialize method with a config object. This does set Queue size.
- ///
- ///
- public GenericSecureTcpIpServer(TcpServerConfigObject serverConfigObject)
- : base("Uninitialized Secure TCP Server")
- {
- HeartbeatRequiredIntervalInSeconds = 15;
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- BufferSize = 2000;
- MonitorClientMaxFailureCount = 3;
- Initialize(serverConfigObject);
- }
- #endregion
-
- #region Methods - Server Actions
- ///
- /// Disconnects all clients and stops the server
- ///
- public void KillServer()
- {
- ServerStopped = true;
- if (MonitorClient != null)
- {
- MonitorClient.Disconnect();
- }
- DisconnectAllClientsForShutdown();
- StopListening();
- }
-
- ///
- /// Initialize Key for device using client name from SIMPL+. Called on Listen from SIMPL+
- ///
- ///
- public void Initialize(string key)
- {
- Key = key;
- }
-
- ///
- /// Initialze the server
- ///
- ///
- public void Initialize(TcpServerConfigObject serverConfigObject)
- {
- try
- {
- if (serverConfigObject != null || string.IsNullOrEmpty(serverConfigObject.Key))
+ get
{
- Key = serverConfigObject.Key;
- MaxClients = serverConfigObject.MaxClients;
- Port = serverConfigObject.Port;
- SharedKeyRequired = serverConfigObject.SharedKeyRequired;
- SharedKey = serverConfigObject.SharedKey;
- HeartbeatRequired = serverConfigObject.HeartbeatRequired;
- HeartbeatRequiredIntervalInSeconds = serverConfigObject.HeartbeatRequiredIntervalInSeconds;
- HeartbeatStringToMatch = serverConfigObject.HeartbeatStringToMatch;
- BufferSize = serverConfigObject.BufferSize;
- ReceiveQueueSize = serverConfigObject.ReceiveQueueSize > 20 ? serverConfigObject.ReceiveQueueSize : 20;
- MessageQueue = new CrestronQueue(ReceiveQueueSize);
+ if (SecureServer != null)
+ return SecureServer.State.ToString();
+ return ServerState.SERVER_NOT_LISTENING.ToString();
+
}
- else
+
+ }
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsConnected
+ {
+ get
+ {
+ if (SecureServer != null)
+ return (SecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED;
+ return false;
+
+ //return (Secure ? SecureServer != null : UnsecureServer != null) &&
+ //(Secure ? (SecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED :
+ // (UnsecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED);
+ }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsConnected
+ {
+ get { return (ushort)(IsConnected ? 1 : 0); }
+ }
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsListening
+ {
+ get
+ {
+ if (SecureServer != null)
+ return (SecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING;
+ else
+ return false;
+ //return (Secure ? SecureServer != null : UnsecureServer != null) &&
+ //(Secure ? (SecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING :
+ // (UnsecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING);
+ }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsListening
+ {
+ get { return (ushort)(IsListening ? 1 : 0); }
+ }
+ ///
+ /// Max number of clients this server will allow for connection. Crestron max is 64. This number should be less than 65
+ ///
+ public ushort MaxClients { get; set; } // should be set by parameter in SIMPL+ in the MAIN method, Should not ever need to be configurable
+ ///
+ /// Number of clients currently connected.
+ ///
+ public ushort NumberOfClientsConnected
+ {
+ get
+ {
+ if (SecureServer != null)
+ return (ushort)SecureServer.NumberOfClientsConnected;
+ return 0;
+ }
+ }
+
+ ///
+ /// Gets or sets the Port
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// S+ helper for Port
+ ///
+ public ushort UPort
+ {
+ get { return Convert.ToUInt16(Port); }
+ set { Port = Convert.ToInt32(value); }
+ }
+
+ ///
+ /// Bool to show whether the server requires a preshared key. Must be set the same in the client, and if true shared keys must be identical on server/client
+ ///
+ public bool SharedKeyRequired { get; set; }
+
+ ///
+ /// S+ helper for requires shared key bool
+ ///
+ public ushort USharedKeyRequired
+ {
+ set
+ {
+ if (value == 1)
+ SharedKeyRequired = true;
+ else
+ SharedKeyRequired = false;
+ }
+ }
+
+ ///
+ /// Gets or sets the SharedKey
+ ///
+ public string SharedKey { get; set; }
+
+ ///
+ /// Heartbeat Required bool sets whether server disconnects client if heartbeat is not received
+ ///
+ public bool HeartbeatRequired { get; set; }
+
+ ///
+ /// S+ Helper for Heartbeat Required
+ ///
+ public ushort UHeartbeatRequired
+ {
+ set
+ {
+ if (value == 1)
+ HeartbeatRequired = true;
+ else
+ HeartbeatRequired = false;
+ }
+ }
+
+ ///
+ /// Gets or sets the HeartbeatRequiredIntervalMs
+ ///
+ public int HeartbeatRequiredIntervalMs { get; set; }
+
+ ///
+ /// Simpl+ Heartbeat Analog value in seconds
+ ///
+ public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatRequiredIntervalMs = (value * 1000); } }
+
+ ///
+ /// Gets or sets the HeartbeatStringToMatch
+ ///
+ public string HeartbeatStringToMatch { get; set; }
+
+ //private timers for Heartbeats per client
+ Dictionary HeartbeatTimerDictionary = new Dictionary();
+
+ //flags to show the secure server is waiting for client at index to send the shared key
+ List WaitingForSharedKey = new List();
+
+ List ClientReadyAfterKeyExchange = new List();
+
+ ///
+ /// The connected client indexes
+ ///
+ public List ConnectedClientsIndexes = new List();
+
+ ///
+ /// Gets or sets the BufferSize
+ ///
+ public int BufferSize { get; set; }
+
+ ///
+ /// Private flag to note that the server has stopped intentionally
+ ///
+ private bool ServerStopped { get; set; }
+
+ //Servers
+ SecureTCPServer SecureServer;
+
+ ///
+ ///
+ ///
+ bool ProgramIsStopping;
+
+ #endregion
+
+ #region Constructors
+ ///
+ /// constructor S+ Does not accept a key. Use initialze with key to set the debug key on this device. If using with + make sure to set all properties manually.
+ ///
+ public GenericSecureTcpIpServer()
+ : base("Uninitialized Secure TCP Server")
+ {
+ HeartbeatRequiredIntervalInSeconds = 15;
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ BufferSize = 2000;
+ MonitorClientMaxFailureCount = 3;
+ }
+
+ ///
+ /// constructor with debug key set at instantiation. Make sure to set all properties before listening.
+ ///
+ ///
+ public GenericSecureTcpIpServer(string key)
+ : base("Uninitialized Secure TCP Server")
+ {
+ HeartbeatRequiredIntervalInSeconds = 15;
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ BufferSize = 2000;
+ MonitorClientMaxFailureCount = 3;
+ Key = key;
+ }
+
+ ///
+ /// Contstructor that sets all properties by calling the initialize method with a config object. This does set Queue size.
+ ///
+ ///
+ public GenericSecureTcpIpServer(TcpServerConfigObject serverConfigObject)
+ : base("Uninitialized Secure TCP Server")
+ {
+ HeartbeatRequiredIntervalInSeconds = 15;
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ BufferSize = 2000;
+ MonitorClientMaxFailureCount = 3;
+ Initialize(serverConfigObject);
+ }
+ #endregion
+
+ #region Methods - Server Actions
+ ///
+ /// KillServer method
+ ///
+ public void KillServer()
+ {
+ ServerStopped = true;
+ if (MonitorClient != null)
+ {
+ MonitorClient.Disconnect();
+ }
+ DisconnectAllClientsForShutdown();
+ StopListening();
+ }
+
+ ///
+ /// Initialize Key for device using client name from SIMPL+. Called on Listen from SIMPL+
+ ///
+ ///
+ ///
+ /// Initialize method
+ ///
+ public void Initialize(string key)
+ {
+ Key = key;
+ }
+
+ ///
+ /// Initialze the server
+ ///
+ ///
+ public void Initialize(TcpServerConfigObject serverConfigObject)
+ {
+ try
+ {
+ if (serverConfigObject != null || string.IsNullOrEmpty(serverConfigObject.Key))
+ {
+ Key = serverConfigObject.Key;
+ MaxClients = serverConfigObject.MaxClients;
+ Port = serverConfigObject.Port;
+ SharedKeyRequired = serverConfigObject.SharedKeyRequired;
+ SharedKey = serverConfigObject.SharedKey;
+ HeartbeatRequired = serverConfigObject.HeartbeatRequired;
+ HeartbeatRequiredIntervalInSeconds = serverConfigObject.HeartbeatRequiredIntervalInSeconds;
+ HeartbeatStringToMatch = serverConfigObject.HeartbeatStringToMatch;
+ BufferSize = serverConfigObject.BufferSize;
+ ReceiveQueueSize = serverConfigObject.ReceiveQueueSize > 20 ? serverConfigObject.ReceiveQueueSize : 20;
+ MessageQueue = new CrestronQueue(ReceiveQueueSize);
+ }
+ else
+ {
+ ErrorLog.Error("Could not initialize server with key: {0}", serverConfigObject.Key);
+ }
+ }
+ catch
{
ErrorLog.Error("Could not initialize server with key: {0}", serverConfigObject.Key);
}
}
- catch
- {
- ErrorLog.Error("Could not initialize server with key: {0}", serverConfigObject.Key);
- }
- }
- ///
- /// Start listening on the specified port
- ///
- public void Listen()
- {
- lock (_serverLock)
+ ///
+ /// Listen method
+ ///
+ public void Listen()
{
- try
- {
- if (Port < 1 || Port > 65535)
+ ServerCCSection.Enter();
+ try
{
- this.LogError("Server '{0}': Invalid port", Key);
- ErrorLog.Warn(string.Format("Server '{0}': Invalid port", Key));
- return;
- }
- if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
- {
- this.LogError("Server '{0}': No Shared Key set", Key);
- ErrorLog.Warn(string.Format("Server '{0}': No Shared Key set", Key));
- return;
- }
+ if (Port < 1 || Port > 65535)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Server '{0}': Invalid port", Key);
+ ErrorLog.Warn(string.Format("Server '{0}': Invalid port", Key));
+ return;
+ }
+ if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Server '{0}': No Shared Key set", Key);
+ ErrorLog.Warn(string.Format("Server '{0}': No Shared Key set", Key));
+ return;
+ }
if (SecureServer == null)
@@ -430,379 +436,381 @@ public class GenericSecureTcpIpServer : Device
SocketErrorCodes status = SecureServer.WaitForConnectionAsync(IPAddress.Any, SecureConnectCallback);
if (status != SocketErrorCodes.SOCKET_OPERATION_PENDING)
{
- this.LogError("Error starting WaitForConnectionAsync {0}", status);
+ Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error starting WaitForConnectionAsync {0}", status);
}
else
{
ServerStopped = false;
}
OnServerStateChange(SecureServer.State);
- this.LogInformation("Secure Server Status: {0}, Socket Status: {1}", SecureServer.State, SecureServer.ServerSocketStatus);
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Secure Server Status: {0}, Socket Status: {1}", SecureServer.State, SecureServer.ServerSocketStatus);
+ ServerCCSection.Leave();
+ }
+ catch (Exception ex)
+ {
+ ServerCCSection.Leave();
+ ErrorLog.Error("{1} Error with Dynamic Server: {0}", ex.ToString(), Key);
+ }
}
- catch (Exception ex)
- {
- this.LogException(ex, "{1} Error with Dynamic Server: {0}", ex.ToString(), Key);
- }
- } // end lock
- }
- ///
- /// Stop Listeneing
- ///
- public void StopListening()
- {
+ ///
+ /// StopListening method
+ ///
+ public void StopListening()
+ {
try
{
- this.LogVerbose("Stopping Listener");
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Stopping Listener");
if (SecureServer != null)
{
SecureServer.Stop();
- this.LogVerbose("Server State: {0}", SecureServer.State);
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server State: {0}", SecureServer.State);
OnServerStateChange(SecureServer.State);
}
ServerStopped = true;
}
catch (Exception ex)
{
- this.LogException(ex, "Error stopping server. Error: {0}", ex.Message);
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error stopping server. Error: {0}", ex);
}
- }
+ }
- ///
- /// Disconnects Client
- ///
- ///
- public void DisconnectClient(uint client)
- {
- try
+ ///
+ /// Disconnects Client
+ ///
+ ///
+ ///
+ /// DisconnectClient method
+ ///
+ public void DisconnectClient(uint client)
{
- SecureServer.Disconnect(client);
- this.LogVerbose("Disconnected client index: {0}", client);
- }
- catch (Exception ex)
- {
- this.LogException(ex, "Error Disconnecting client index: {0}. Error: {1}", client, ex.Message);
- }
- }
- ///
- /// Disconnect All Clients
- ///
- public void DisconnectAllClientsForShutdown()
- {
- this.LogInformation("Disconnecting All Clients");
- if (SecureServer != null)
- {
- SecureServer.SocketStatusChange -= SecureServer_SocketStatusChange;
- foreach (var index in ConnectedClientsIndexes.ToList()) // copy it here so that it iterates properly
+ try
{
- var i = index;
- if (!SecureServer.ClientConnected(index))
- continue;
- try
- {
- SecureServer.Disconnect(i);
- this.LogInformation("Disconnected client index: {0}", i);
- }
- catch (Exception ex)
- {
- this.LogException(ex, "Error Disconnecting client index: {0}. Error: {1}", i, ex.Message);
- }
+ SecureServer.Disconnect(client);
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", client);
}
- this.LogInformation("Server Status: {0}", SecureServer.ServerSocketStatus);
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", client, ex);
+ }
+ }
+ ///
+ /// DisconnectAllClientsForShutdown method
+ ///
+ public void DisconnectAllClientsForShutdown()
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Disconnecting All Clients");
+ if (SecureServer != null)
+ {
+ SecureServer.SocketStatusChange -= SecureServer_SocketStatusChange;
+ foreach (var index in ConnectedClientsIndexes.ToList()) // copy it here so that it iterates properly
+ {
+ var i = index;
+ if (!SecureServer.ClientConnected(index))
+ continue;
+ try
+ {
+ SecureServer.Disconnect(i);
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", i);
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", i, ex);
+ }
+ }
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server Status: {0}", SecureServer.ServerSocketStatus);
+ }
+
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected All Clients");
+ ConnectedClientsIndexes.Clear();
+
+ if (!ProgramIsStopping)
+ {
+ OnConnectionChange();
+ OnServerStateChange(SecureServer.State); //State shows both listening and connected
+ }
+
+ // var o = new { };
}
- this.LogInformation("Disconnected All Clients");
- ConnectedClientsIndexes.Clear();
-
- if (!ProgramIsStopping)
+ ///
+ /// Broadcast text from server to all connected clients
+ ///
+ ///
+ ///
+ /// BroadcastText method
+ ///
+ public void BroadcastText(string text)
{
- OnConnectionChange();
- OnServerStateChange(SecureServer.State); //State shows both listening and connected
+ CCriticalSection CCBroadcast = new CCriticalSection();
+ CCBroadcast.Enter();
+ try
+ {
+ if (ConnectedClientsIndexes.Count > 0)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
+ foreach (uint i in ConnectedClientsIndexes)
+ {
+ if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(i)))
+ {
+ SocketErrorCodes error = SecureServer.SendDataAsync(i, b, b.Length, (x, y, z) => { });
+ if (error != SocketErrorCodes.SOCKET_OK && error != SocketErrorCodes.SOCKET_OPERATION_PENDING)
+ this.LogVerbose("{error}", error);
+ }
+ }
+ }
+ CCBroadcast.Leave();
+ }
+ catch (Exception ex)
+ {
+ CCBroadcast.Leave();
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Broadcasting messages from server. Error: {0}", ex.Message);
+ }
}
- // var o = new { };
- }
-
- ///
- /// Broadcast text from server to all connected clients
- ///
- ///
- public void BroadcastText(string text)
- {
- lock (_broadcastLock)
+ ///
+ /// Not sure this is useful in library, maybe Pro??
+ ///
+ ///
+ ///
+ ///
+ /// SendTextToClient method
+ ///
+ public void SendTextToClient(string text, uint clientIndex)
{
- try
- {
- if (ConnectedClientsIndexes.Count > 0)
+ try
{
byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
- foreach (uint i in ConnectedClientsIndexes)
+ if (SecureServer != null && SecureServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
{
- if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(i)))
- {
- SocketErrorCodes error = SecureServer.SendDataAsync(i, b, b.Length, (x, y, z) => { });
- if (error != SocketErrorCodes.SOCKET_OK && error != SocketErrorCodes.SOCKET_OPERATION_PENDING)
- this.LogVerbose("{error}", error);
- }
+ if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
+ SecureServer.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
}
}
- }
- catch (Exception ex)
- {
- this.LogException(ex, "Error Broadcasting messages from server. Error: {0}", ex.Message);
- }
- } // end lock
- }
-
- ///
- /// Not sure this is useful in library, maybe Pro??
- ///
- ///
- ///
- public void SendTextToClient(string text, uint clientIndex)
- {
- try
- {
- byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
- if (SecureServer != null && SecureServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ catch (Exception ex)
{
- if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
- SecureServer.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
+ Debug.Console(2, this, "Error sending text to client. Text: {1}. Error: {0}", ex.Message, text);
}
}
- catch (Exception ex)
- {
- this.LogException(ex, "Error sending text to client. Text: {1}. Error: {0}", ex.Message, text);
- }
- }
- //private method to check heartbeat requirements and start or reset timer
- string checkHeartbeat(uint clientIndex, string received)
- {
- try
+ //private method to check heartbeat requirements and start or reset timer
+ string checkHeartbeat(uint clientIndex, string received)
{
- if (HeartbeatRequired)
+ try
{
- if (!string.IsNullOrEmpty(HeartbeatStringToMatch))
+ if (HeartbeatRequired)
{
- var remainingText = received.Replace(HeartbeatStringToMatch, "");
- var noDelimiter = received.Trim(new char[] { '\r', '\n' });
- if (noDelimiter.Contains(HeartbeatStringToMatch))
+ if (!string.IsNullOrEmpty(HeartbeatStringToMatch))
{
- if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ var remainingText = received.Replace(HeartbeatStringToMatch, "");
+ var noDelimiter = received.Trim(new char[] { '\r', '\n' });
+ if (noDelimiter.Contains(HeartbeatStringToMatch))
{
- HeartbeatTimerDictionary[clientIndex].Stop();
- HeartbeatTimerDictionary[clientIndex].Interval = HeartbeatRequiredIntervalMs;
- HeartbeatTimerDictionary[clientIndex].Start();
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
+ else
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ Debug.Console(1, this, "Heartbeat Received: {0}, from client index: {1}", HeartbeatStringToMatch, clientIndex);
+ // Return Heartbeat
+ SendTextToClient(HeartbeatStringToMatch, clientIndex);
+ return remainingText;
}
- else
- {
- var heartbeatTimer = new Timer(HeartbeatRequiredIntervalMs) { AutoReset = false };
- heartbeatTimer.Elapsed += (s, e) => HeartbeatTimer_CallbackFunction(clientIndex);
- heartbeatTimer.Start();
- HeartbeatTimerDictionary.Add(clientIndex, heartbeatTimer);
- }
- this.LogDebug("Heartbeat Received: {0}, from client index: {1}", HeartbeatStringToMatch, clientIndex);
- // Return Heartbeat
- SendTextToClient(HeartbeatStringToMatch, clientIndex);
- return remainingText;
- }
- }
- else
- {
- if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
- {
- HeartbeatTimerDictionary[clientIndex].Stop();
- HeartbeatTimerDictionary[clientIndex].Interval = HeartbeatRequiredIntervalMs;
- HeartbeatTimerDictionary[clientIndex].Start();
}
else
{
- var heartbeatTimer = new Timer(HeartbeatRequiredIntervalMs) { AutoReset = false };
- heartbeatTimer.Elapsed += (s, e) => HeartbeatTimer_CallbackFunction(clientIndex);
- heartbeatTimer.Start();
- HeartbeatTimerDictionary.Add(clientIndex, heartbeatTimer);
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
+ else
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ Debug.Console(1, this, "Heartbeat Received: {0}, from client index: {1}", received, clientIndex);
}
- this.LogInformation("Heartbeat Received: {0}, from client index: {1}", received, clientIndex);
}
}
- }
- catch (Exception ex)
- {
- this.LogException(ex, "Error checking heartbeat: {0}", ex.Message);
- }
- return received;
- }
-
- ///
- /// Get the IP Address for the client at the specifed index
- ///
- ///
- ///
- public string GetClientIPAddress(uint clientIndex)
- {
- this.LogInformation("GetClientIPAddress Index: {0}", clientIndex);
- if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
- {
- var ipa = this.SecureServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
- this.LogInformation("GetClientIPAddress IPAddreess: {0}", ipa);
- return ipa;
-
- }
- else
- {
- return "";
- }
- }
-
- #endregion
-
- #region Methods - HeartbeatTimer Callback
-
- void HeartbeatTimer_CallbackFunction(object o)
- {
- uint clientIndex = 99999;
- string address = string.Empty;
- try
- {
- clientIndex = (uint)o;
- address = SecureServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
-
- this.LogInformation("Heartbeat not received for Client index {2} IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE {1}",
- address, string.IsNullOrEmpty(HeartbeatStringToMatch) ? "" : ("HeartbeatStringToMatch: " + HeartbeatStringToMatch), clientIndex);
-
- if (SecureServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
- SendTextToClient("Heartbeat not received by server, closing connection", clientIndex);
-
- var discoResult = SecureServer.Disconnect(clientIndex);
-
- if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ catch (Exception ex)
{
- HeartbeatTimerDictionary[clientIndex].Stop();
- HeartbeatTimerDictionary[clientIndex].Dispose();
- HeartbeatTimerDictionary.Remove(clientIndex);
+ Debug.Console(1, this, "Error checking heartbeat: {0}", ex.Message);
+ }
+ return received;
+ }
+
+ ///
+ /// Get the IP Address for the client at the specifed index
+ ///
+ ///
+ ///
+ ///
+ /// GetClientIPAddress method
+ ///
+ public string GetClientIPAddress(uint clientIndex)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress Index: {0}", clientIndex);
+ if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
+ {
+ var ipa = this.SecureServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress IPAddreess: {0}", ipa);
+ return ipa;
+
+ }
+ else
+ {
+ return "";
}
}
- catch (Exception ex)
- {
- ErrorLog.Error("{3}: Heartbeat timeout Error on Client Index: {0}, at address: {1}, error: {2}", clientIndex, address, ex.Message, Key);
- }
- }
- #endregion
+ #endregion
- #region Methods - Socket Status Changed Callbacks
- ///
- /// Secure Server Socket Status Changed Callback
- ///
- ///
- ///
- ///
- void SecureServer_SocketStatusChange(SecureTCPServer server, uint clientIndex, SocketStatus serverSocketStatus)
- {
- try
+ #region Methods - HeartbeatTimer Callback
+
+ void HeartbeatTimer_CallbackFunction(object o)
{
-
- if (serverSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ uint clientIndex = 99999;
+ string address = string.Empty;
+ try
{
- this.LogInformation("SecureServerSocketStatusChange ConnectedCLients: {0} ServerState: {1} Port: {2}", SecureServer.NumberOfClientsConnected, SecureServer.State, SecureServer.PortNumber);
-
- if (ConnectedClientsIndexes.Contains(clientIndex))
- ConnectedClientsIndexes.Remove(clientIndex);
- if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ clientIndex = (uint)o;
+ address = SecureServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Heartbeat not received for Client index {2} IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE {1}",
+ address, string.IsNullOrEmpty(HeartbeatStringToMatch) ? "" : ("HeartbeatStringToMatch: " + HeartbeatStringToMatch), clientIndex);
+
+ if (SecureServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ SendTextToClient("Heartbeat not received by server, closing connection", clientIndex);
+
+ var discoResult = SecureServer.Disconnect(clientIndex);
+ //Debug.Console(1, this, "{0}", discoResult);
+
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
{
HeartbeatTimerDictionary[clientIndex].Stop();
HeartbeatTimerDictionary[clientIndex].Dispose();
HeartbeatTimerDictionary.Remove(clientIndex);
}
- if (ClientReadyAfterKeyExchange.Contains(clientIndex))
- ClientReadyAfterKeyExchange.Remove(clientIndex);
+ }
+ catch (Exception ex)
+ {
+ ErrorLog.Error("{3}: Heartbeat timeout Error on Client Index: {0}, at address: {1}, error: {2}", clientIndex, address, ex.Message, Key);
+ }
+ }
+
+ #endregion
+
+ #region Methods - Socket Status Changed Callbacks
+ ///
+ /// Secure Server Socket Status Changed Callback
+ ///
+ ///
+ ///
+ ///
+ void SecureServer_SocketStatusChange(SecureTCPServer server, uint clientIndex, SocketStatus serverSocketStatus)
+ {
+ try
+ {
+
+
+ // Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SecureServerSocketStatusChange Index:{0} status:{1} Port:{2} IP:{3}", clientIndex, serverSocketStatus, this.SecureServer.GetPortNumberServerAcceptedConnectionFromForSpecificClient(clientIndex), this.SecureServer.GetLocalAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
+ if (serverSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SecureServerSocketStatusChange ConnectedCLients: {0} ServerState: {1} Port: {2}", SecureServer.NumberOfClientsConnected, SecureServer.State, SecureServer.PortNumber);
+
+ if (ConnectedClientsIndexes.Contains(clientIndex))
+ ConnectedClientsIndexes.Remove(clientIndex);
+ if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ {
+ HeartbeatTimerDictionary[clientIndex].Stop();
+ HeartbeatTimerDictionary[clientIndex].Dispose();
+ HeartbeatTimerDictionary.Remove(clientIndex);
+ }
+ if (ClientReadyAfterKeyExchange.Contains(clientIndex))
+ ClientReadyAfterKeyExchange.Remove(clientIndex);
if (WaitingForSharedKey.Contains(clientIndex))
WaitingForSharedKey.Remove(clientIndex);
if (SecureServer.MaxNumberOfClientSupported > SecureServer.NumberOfClientsConnected)
{
Listen();
}
- }
- }
- catch (Exception ex)
- {
- this.LogException(ex, "Error in Socket Status Change Callback. Error: {0}", ex.Message);
- }
- //Use a thread for this event so that the server state updates to listening while this event is processed. Listening must be added to the server state
- //after every client connection so that the server can check and see if it is at max clients. Due to this the event fires and server listening enum bit flag
- //is not set. Putting in a thread allows the state to update before this event processes so that the subscribers to this event get accurate isListening in the event.
- System.Threading.Tasks.Task.Run(() => onConnectionChange(clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex)));
- }
-
- #endregion
-
- #region Methods Connected Callbacks
- ///
- /// Secure TCP Client Connected to Secure Server Callback
- ///
- ///
- ///
- void SecureConnectCallback(SecureTCPServer server, uint clientIndex)
- {
- try
- {
- this.LogInformation("ConnectCallback: IPAddress: {0}. Index: {1}. Status: {2}",
- server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex),
- clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
- if (clientIndex != 0)
- {
- if (server.ClientConnected(clientIndex))
- {
-
- if (!ConnectedClientsIndexes.Contains(clientIndex))
- {
- ConnectedClientsIndexes.Add(clientIndex);
- }
- if (SharedKeyRequired)
- {
- if (!WaitingForSharedKey.Contains(clientIndex))
- {
- WaitingForSharedKey.Add(clientIndex);
- }
- byte[] b = Encoding.GetEncoding(28591).GetBytes("SharedKey:");
- server.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
- this.LogInformation("Sent Shared Key Request to client at {0}", server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
- }
- else
- {
- OnServerClientReadyForCommunications(clientIndex);
- }
- if (HeartbeatRequired)
- {
- if (!HeartbeatTimerDictionary.ContainsKey(clientIndex))
- {
- var heartbeatTimer = new Timer(HeartbeatRequiredIntervalMs) { AutoReset = false };
- heartbeatTimer.Elapsed += (s, e) => HeartbeatTimer_CallbackFunction(clientIndex);
- heartbeatTimer.Start();
- HeartbeatTimerDictionary.Add(clientIndex, heartbeatTimer);
- }
- }
-
- server.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback);
}
}
- else
+ catch (Exception ex)
{
- this.LogError("Client attempt faulty.");
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Change Callback. Error: {0}", ex);
}
+ //Use a thread for this event so that the server state updates to listening while this event is processed. Listening must be added to the server state
+ //after every client connection so that the server can check and see if it is at max clients. Due to this the event fires and server listening enum bit flag
+ //is not set. Putting in a thread allows the state to update before this event processes so that the subscribers to this event get accurate isListening in the event.
+ CrestronInvoke.BeginInvoke(o => onConnectionChange(clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex)), null);
}
- catch (Exception ex)
+
+ #endregion
+
+ #region Methods Connected Callbacks
+ ///
+ /// Secure TCP Client Connected to Secure Server Callback
+ ///
+ ///
+ ///
+ void SecureConnectCallback(SecureTCPServer server, uint clientIndex)
{
- this.LogException(ex, "Error in Socket Status Connect Callback. Error: {0}", ex.Message);
- }
+ try
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "ConnectCallback: IPAddress: {0}. Index: {1}. Status: {2}",
+ server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex),
+ clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
+ if (clientIndex != 0)
+ {
+ if (server.ClientConnected(clientIndex))
+ {
+
+ if (!ConnectedClientsIndexes.Contains(clientIndex))
+ {
+ ConnectedClientsIndexes.Add(clientIndex);
+ }
+ if (SharedKeyRequired)
+ {
+ if (!WaitingForSharedKey.Contains(clientIndex))
+ {
+ WaitingForSharedKey.Add(clientIndex);
+ }
+ byte[] b = Encoding.GetEncoding(28591).GetBytes("SharedKey:");
+ server.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Sent Shared Key Request to client at {0}", server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
+ }
+ else
+ {
+ OnServerClientReadyForCommunications(clientIndex);
+ }
+ if (HeartbeatRequired)
+ {
+ if (!HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ {
+ HeartbeatTimerDictionary.Add(clientIndex, new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs));
+ }
+ }
+
+ server.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback);
+ }
+ }
+ else
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Client attempt faulty.");
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Connect Callback. Error: {0}", ex);
+ }
// Rearm the listner
SocketErrorCodes status = server.WaitForConnectionAsync(IPAddress.Any, SecureConnectCallback);
if (status != SocketErrorCodes.SOCKET_OPERATION_PENDING)
{
- this.LogError("Socket status connect callback status {0}", status);
+ Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Socket status connect callback status {0}", status);
if (status == SocketErrorCodes.SOCKET_CONNECTION_IN_PROGRESS)
{
// There is an issue where on a failed negotiation we need to stop and start the server. This should still leave connected clients intact.
@@ -812,276 +820,279 @@ public class GenericSecureTcpIpServer : Device
}
}
- #endregion
+ #endregion
- #region Methods - Send/Receive Callbacks
- ///
- /// Secure Received Data Async Callback
- ///
- ///
- ///
- ///
- void SecureReceivedDataAsyncCallback(SecureTCPServer mySecureTCPServer, uint clientIndex, int numberOfBytesReceived)
- {
- if (numberOfBytesReceived > 0)
+ #region Methods - Send/Receive Callbacks
+ ///
+ /// Secure Received Data Async Callback
+ ///
+ ///
+ ///
+ ///
+ void SecureReceivedDataAsyncCallback(SecureTCPServer mySecureTCPServer, uint clientIndex, int numberOfBytesReceived)
{
+ if (numberOfBytesReceived > 0)
+ {
- string received = "Nothing";
- var handler = TextReceivedQueueInvoke;
- try
- {
- byte[] bytes = mySecureTCPServer.GetIncomingDataBufferForSpecificClient(clientIndex);
- received = System.Text.Encoding.GetEncoding(28591).GetString(bytes, 0, numberOfBytesReceived);
- if (WaitingForSharedKey.Contains(clientIndex))
+ string received = "Nothing";
+ var handler = TextReceivedQueueInvoke;
+ try
{
- received = received.Replace("\r", "");
- received = received.Replace("\n", "");
- if (received != SharedKey)
+ byte[] bytes = mySecureTCPServer.GetIncomingDataBufferForSpecificClient(clientIndex);
+ received = System.Text.Encoding.GetEncoding(28591).GetString(bytes, 0, numberOfBytesReceived);
+ if (WaitingForSharedKey.Contains(clientIndex))
{
- byte[] b = Encoding.GetEncoding(28591).GetBytes("Shared key did not match server. Disconnecting");
- this.LogWarning("Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received);
- mySecureTCPServer.SendData(clientIndex, b, b.Length);
- mySecureTCPServer.Disconnect(clientIndex);
-
- return;
- }
+ received = received.Replace("\r", "");
+ received = received.Replace("\n", "");
+ if (received != SharedKey)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes("Shared key did not match server. Disconnecting");
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received);
+ mySecureTCPServer.SendData(clientIndex, b, b.Length);
+ mySecureTCPServer.Disconnect(clientIndex);
+
+ return;
+ }
- WaitingForSharedKey.Remove(clientIndex);
- byte[] success = Encoding.GetEncoding(28591).GetBytes("Shared Key Match");
- mySecureTCPServer.SendDataAsync(clientIndex, success, success.Length, null);
- OnServerClientReadyForCommunications(clientIndex);
- this.LogInformation("Client with index {0} provided the shared key and successfully connected to the server", clientIndex);
- }
- else if (!string.IsNullOrEmpty(checkHeartbeat(clientIndex, received)))
- {
- onTextReceived(received, clientIndex);
- if (handler != null)
+ WaitingForSharedKey.Remove(clientIndex);
+ byte[] success = Encoding.GetEncoding(28591).GetBytes("Shared Key Match");
+ mySecureTCPServer.SendDataAsync(clientIndex, success, success.Length, null);
+ OnServerClientReadyForCommunications(clientIndex);
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Client with index {0} provided the shared key and successfully connected to the server", clientIndex);
+ }
+ else if (!string.IsNullOrEmpty(checkHeartbeat(clientIndex, received)))
{
- MessageQueue.TryToEnqueue(new GenericTcpServerCommMethodReceiveTextArgs(received, clientIndex));
+ onTextReceived(received, clientIndex);
+ if (handler != null)
+ {
+ MessageQueue.TryToEnqueue(new GenericTcpServerCommMethodReceiveTextArgs(received, clientIndex));
+ }
}
}
- }
- catch (Exception ex)
- {
- this.LogException(ex, "Error Receiving data: {0}. Error: {1}", received, ex.Message);
- }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Receiving data: {0}. Error: {1}", received, ex);
+ }
if (mySecureTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
mySecureTCPServer.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback);
- //Check to see if there is a subscription to the TextReceivedQueueInvoke event. If there is start the dequeue thread.
- if (handler != null)
- {
- if (System.Threading.Monitor.TryEnter(_dequeueLock))
- System.Threading.Tasks.Task.Run(() => DequeueEvent());
+ //Check to see if there is a subscription to the TextReceivedQueueInvoke event. If there is start the dequeue thread.
+ if (handler != null)
+ {
+ var gotLock = DequeueLock.TryEnter();
+ if (gotLock)
+ CrestronInvoke.BeginInvoke((o) => DequeueEvent());
+ }
}
- }
else
{
mySecureTCPServer.Disconnect(clientIndex);
+ }
}
- }
- ///
- /// This method gets spooled up in its own thread an protected by a lock to prevent multiple threads from running concurrently.
- /// It will dequeue items as they are enqueued automatically.
- ///
- void DequeueEvent()
- {
- try
+ ///
+ /// This method gets spooled up in its own thread an protected by a CCriticalSection to prevent multiple threads from running concurrently.
+ /// It will dequeue items as they are enqueued automatically.
+ ///
+ void DequeueEvent()
{
- while (true)
+ try
{
- // Pull from Queue and fire an event. Block indefinitely until an item can be removed, similar to a Gather.
- var message = MessageQueue.Dequeue();
- var handler = TextReceivedQueueInvoke;
+ while (true)
+ {
+ // Pull from Queue and fire an event. Block indefinitely until an item can be removed, similar to a Gather.
+ var message = MessageQueue.Dequeue();
+ var handler = TextReceivedQueueInvoke;
+ if (handler != null)
+ {
+ handler(this, message);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ this.LogException(e, "DequeueEvent error");
+ }
+ // Make sure to leave the CCritical section in case an exception above stops this thread, or we won't be able to restart it.
+ if (DequeueLock != null)
+ {
+ DequeueLock.Leave();
+ }
+ }
+
+ #endregion
+
+ #region Methods - EventHelpers/Callbacks
+
+ //Private Helper method to call the Connection Change Event
+ void onConnectionChange(uint clientIndex, SocketStatus clientStatus)
+ {
+ if (clientIndex != 0) //0 is error not valid client change
+ {
+ var handler = ClientConnectionChange;
if (handler != null)
{
- handler(this, message);
+ handler(this, new GenericTcpServerSocketStatusChangeEventArgs(SecureServer, clientIndex, clientStatus));
}
}
}
- catch (Exception e)
- {
- this.LogError(e, "DequeueEvent error");
- }
- // Make sure to release the lock in case an exception above stops this thread, or we won't be able to restart it.
- System.Threading.Monitor.Exit(_dequeueLock);
- }
-
- #endregion
-
- #region Methods - EventHelpers/Callbacks
-
- //Private Helper method to call the Connection Change Event
- void onConnectionChange(uint clientIndex, SocketStatus clientStatus)
- {
- if (clientIndex != 0) //0 is error not valid client change
+
+ //Private Helper method to call the Connection Change Event
+ void OnConnectionChange()
{
+ if (ProgramIsStopping)
+ {
+ return;
+ }
var handler = ClientConnectionChange;
if (handler != null)
{
- handler(this, new GenericTcpServerSocketStatusChangeEventArgs(SecureServer, clientIndex, clientStatus));
+ handler(this, new GenericTcpServerSocketStatusChangeEventArgs());
}
}
- }
- //Private Helper method to call the Connection Change Event
- void OnConnectionChange()
- {
- if (ProgramIsStopping)
+ //Private Helper Method to call the Text Received Event
+ void onTextReceived(string text, uint clientIndex)
{
- return;
+ var handler = TextReceived;
+ if (handler != null)
+ handler(this, new GenericTcpServerCommMethodReceiveTextArgs(text, clientIndex));
}
- var handler = ClientConnectionChange;
- if (handler != null)
- {
- handler(this, new GenericTcpServerSocketStatusChangeEventArgs());
- }
- }
- //Private Helper Method to call the Text Received Event
- void onTextReceived(string text, uint clientIndex)
- {
- var handler = TextReceived;
- if (handler != null)
- handler(this, new GenericTcpServerCommMethodReceiveTextArgs(text, clientIndex));
- }
-
- //Private Helper Method to call the Server State Change Event
- void OnServerStateChange(ServerState state)
- {
- if (ProgramIsStopping)
+ //Private Helper Method to call the Server State Change Event
+ void OnServerStateChange(ServerState state)
{
- return;
+ if (ProgramIsStopping)
+ {
+ return;
+ }
+ var handler = ServerStateChange;
+ if (handler != null)
+ {
+ handler(this, new GenericTcpServerStateChangedEventArgs(state));
+ }
}
- var handler = ServerStateChange;
- if (handler != null)
- {
- handler(this, new GenericTcpServerStateChangedEventArgs(state));
- }
- }
- ///
- /// Private Event Handler method to handle the closing of connections when the program stops
- ///
- ///
- void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
- {
- if (programEventType == eProgramStatusEventType.Stopping)
+ ///
+ /// Private Event Handler method to handle the closing of connections when the program stops
+ ///
+ ///
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping)
+ {
+ ProgramIsStopping = true;
+ // kill bandaid things
+ if (MonitorClientTimer != null)
+ MonitorClientTimer.Stop();
+ if (MonitorClient != null)
+ MonitorClient.Disconnect();
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing server");
+ KillServer();
+ }
+ }
+
+ //Private event handler method to raise the event that the server is ready to send data after a successful client shared key negotiation
+ void OnServerClientReadyForCommunications(uint clientIndex)
+ {
+ ClientReadyAfterKeyExchange.Add(clientIndex);
+ var handler = ServerClientReadyForCommunications;
+ if (handler != null)
+ handler(this, new GenericTcpServerSocketStatusChangeEventArgs(
+ this, clientIndex, SecureServer.GetServerSocketStatusForSpecificClient(clientIndex)));
+ }
+ #endregion
+
+ #region Monitor Client
+ ///
+ /// Starts the monitor client cycle. Timed wait, then call RunMonitorClient
+ ///
+ void StartMonitorClient()
{
- ProgramIsStopping = true;
- // kill bandaid things
if (MonitorClientTimer != null)
+ {
+ return;
+ }
+ MonitorClientTimer = new CTimer(o => RunMonitorClient(), 60000);
+ }
+
+ ///
+ ///
+ ///
+ void RunMonitorClient()
+ {
+ MonitorClient = new GenericSecureTcpIpClient_ForServer(Key + "-MONITOR", "127.0.0.1", Port, 2000);
+ MonitorClient.SharedKeyRequired = this.SharedKeyRequired;
+ MonitorClient.SharedKey = this.SharedKey;
+ MonitorClient.ConnectionHasHungCallback = MonitorClientHasHungCallback;
+ //MonitorClient.ConnectionChange += MonitorClient_ConnectionChange;
+ MonitorClient.ClientReadyForCommunications += MonitorClient_IsReadyForComm;
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Starting monitor check");
+
+ MonitorClient.Connect();
+ // From here MonitorCLient either connects or hangs, MonitorClient will call back
+
+ }
+
+ ///
+ ///
+ ///
+ void StopMonitorClient()
+ {
+ if (MonitorClient == null)
+ return;
+
+ MonitorClient.ClientReadyForCommunications -= MonitorClient_IsReadyForComm;
+ MonitorClient.Disconnect();
+ MonitorClient = null;
+ }
+
+ ///
+ /// On monitor connect, restart the operation
+ ///
+ void MonitorClient_IsReadyForComm(object sender, GenericTcpServerClientReadyForcommunicationsEventArgs args)
+ {
+ if (args.IsReady)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Monitor client connection success. Disconnecting in 2s");
MonitorClientTimer.Stop();
- if (MonitorClient != null)
- MonitorClient.Disconnect();
-
- this.LogInformation("Program stopping. Closing server");
- KillServer();
+ MonitorClientTimer = null;
+ MonitorClientFailureCount = 0;
+ CrestronEnvironment.Sleep(2000);
+ StopMonitorClient();
+ StartMonitorClient();
+ }
}
- }
- //Private event handler method to raise the event that the server is ready to send data after a successful client shared key negotiation
- void OnServerClientReadyForCommunications(uint clientIndex)
- {
- ClientReadyAfterKeyExchange.Add(clientIndex);
- var handler = ServerClientReadyForCommunications;
- if (handler != null)
- handler(this, new GenericTcpServerSocketStatusChangeEventArgs(
- this, clientIndex, SecureServer.GetServerSocketStatusForSpecificClient(clientIndex)));
- }
- #endregion
-
- #region Monitor Client
- ///
- /// Starts the monitor client cycle. Timed wait, then call RunMonitorClient
- ///
- void StartMonitorClient()
- {
- if (MonitorClientTimer != null)
+ ///
+ /// If the client hangs, add to counter and maybe fire the choke event
+ ///
+ void MonitorClientHasHungCallback()
{
- return;
- }
- MonitorClientTimer = new Timer(60000) { AutoReset = false };
- MonitorClientTimer.Elapsed += (s, e) => RunMonitorClient();
- MonitorClientTimer.Start();
- }
-
- ///
- ///
- ///
- void RunMonitorClient()
- {
- MonitorClient = new GenericSecureTcpIpClient_ForServer(Key + "-MONITOR", "127.0.0.1", Port, 2000);
- MonitorClient.SharedKeyRequired = this.SharedKeyRequired;
- MonitorClient.SharedKey = this.SharedKey;
- MonitorClient.ConnectionHasHungCallback = MonitorClientHasHungCallback;
- //MonitorClient.ConnectionChange += MonitorClient_ConnectionChange;
- MonitorClient.ClientReadyForCommunications += MonitorClient_IsReadyForComm;
-
- this.LogInformation("Starting monitor check");
-
- MonitorClient.Connect();
- // From here MonitorCLient either connects or hangs, MonitorClient will call back
-
- }
-
- ///
- ///
- ///
- void StopMonitorClient()
- {
- if (MonitorClient == null)
- return;
-
- MonitorClient.ClientReadyForCommunications -= MonitorClient_IsReadyForComm;
- MonitorClient.Disconnect();
- MonitorClient = null;
- }
-
- ///
- /// On monitor connect, restart the operation
- ///
- void MonitorClient_IsReadyForComm(object sender, GenericTcpServerClientReadyForcommunicationsEventArgs args)
- {
- if (args.IsReady)
- {
- this.LogInformation("Monitor client connection success. Disconnecting in 2s");
+ MonitorClientFailureCount++;
MonitorClientTimer.Stop();
MonitorClientTimer = null;
- MonitorClientFailureCount = 0;
- CrestronEnvironment.Sleep(2000);
StopMonitorClient();
- StartMonitorClient();
- }
- }
+ if (MonitorClientFailureCount < MonitorClientMaxFailureCount)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Warning, "Monitor client connection has hung {0} time{1}, maximum {2}",
+ MonitorClientFailureCount, MonitorClientFailureCount > 1 ? "s" : "", MonitorClientMaxFailureCount);
+ StartMonitorClient();
+ }
+ else
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error,
+ "\r***************************\rMonitor client connection has hung a maximum of {0} times. \r***************************",
+ MonitorClientMaxFailureCount);
- ///
- /// If the client hangs, add to counter and maybe fire the choke event
- ///
- void MonitorClientHasHungCallback()
- {
- MonitorClientFailureCount++;
- MonitorClientTimer.Stop();
- MonitorClientTimer = null;
- StopMonitorClient();
- if (MonitorClientFailureCount < MonitorClientMaxFailureCount)
- {
- this.LogWarning("Monitor client connection has hung {0} time{1}, maximum {2}",
- MonitorClientFailureCount, MonitorClientFailureCount > 1 ? "s" : "", MonitorClientMaxFailureCount);
- StartMonitorClient();
- }
- else
- {
- this.LogError(
- "\r***************************\rMonitor client connection has hung a maximum of {0} times. \r***************************",
- MonitorClientMaxFailureCount);
-
- var handler = ServerHasChoked;
- if (handler != null)
- handler();
- // Some external thing is in charge here. Expected reset of program
+ var handler = ServerHasChoked;
+ if (handler != null)
+ handler();
+ // Some external thing is in charge here. Expected reset of program
+ }
}
+ #endregion
}
- #endregion
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Comm/GenericSshClient.cs b/src/PepperDash.Core/Comm/GenericSshClient.cs
index 9d7fe001..546a2a67 100644
--- a/src/PepperDash.Core/Comm/GenericSshClient.cs
+++ b/src/PepperDash.Core/Comm/GenericSshClient.cs
@@ -1,576 +1,672 @@
using System;
+using System.Collections.Generic;
using System.Text;
-using System.Timers;
+using System.Threading;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronSockets;
+using Org.BouncyCastle.Utilities;
using PepperDash.Core.Logging;
using Renci.SshNet;
using Renci.SshNet.Common;
-namespace PepperDash.Core;
-
-///
-///
-///
-public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
+namespace PepperDash.Core
{
- private const string SPlusKey = "Uninitialized SshClient";
- ///
- /// Object to enable stream debugging
- ///
- public CommunicationStreamDebugging StreamDebugging { get; private set; }
-
- ///
- /// Event that fires when data is received. Delivers args with byte array
- ///
- public event EventHandler BytesReceived;
-
- ///
- /// Event that fires when data is received. Delivered as text.
- ///
- public event EventHandler TextReceived;
-
- ///
- /// Event when the connection status changes.
- ///
- public event EventHandler ConnectionChange;
-
- /////
- /////
- /////
- //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
-
- ///
- /// Address of server
- ///
- public string Hostname { get; set; }
-
- ///
- /// Port on server
- ///
- public int Port { get; set; }
-
- ///
- /// Username for server
- ///
- public string Username { get; set; }
-
- ///
- /// And... Password for server. That was worth documenting!
- ///
- public string Password { get; set; }
-
- ///
- /// True when the server is connected - when status == 2.
- ///
- public bool IsConnected
- {
- // returns false if no client or not connected
- get { return Client != null && ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
- }
-
- ///
- /// S+ helper for IsConnected
- ///
- public ushort UIsConnected
- {
- get { return (ushort)(IsConnected ? 1 : 0); }
- }
-
///
/// SSH Client
///
- public SocketStatus ClientStatus
+ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
{
- get { return _ClientStatus; }
- private set
+ private const string SPlusKey = "Uninitialized SshClient";
+
+ ///
+ /// Object to enable stream debugging
+ ///
+ public CommunicationStreamDebugging StreamDebugging { get; private set; }
+
+ ///
+ /// Event that fires when data is received. Delivers args with byte array
+ ///
+ public event EventHandler BytesReceived;
+
+ ///
+ /// Event that fires when data is received. Delivered as text.
+ ///
+ public event EventHandler TextReceived;
+
+ ///
+ /// Event when the connection status changes.
+ ///
+ public event EventHandler ConnectionChange;
+
+ ///
+ /// Gets or sets the Hostname
+ ///
+ public string Hostname { get; set; }
+
+ ///
+ /// Port on server
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// Gets or sets the Username
+ ///
+ public string Username { get; set; }
+
+ ///
+ /// Gets or sets the Password
+ ///
+ public string Password { get; set; }
+
+ ///
+ /// True when the server is connected - when status == 2.
+ ///
+ public bool IsConnected
{
- if (_ClientStatus == value)
- return;
- _ClientStatus = value;
- OnConnectionChange();
+ // returns false if no client or not connected
+ get { return client != null && ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
}
- }
- SocketStatus _ClientStatus;
- ///
- /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
- /// and IsConnected with be true when this == 2.
- ///
- public ushort UStatus
- {
- get { return (ushort)_ClientStatus; }
- }
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsConnected
+ {
+ get { return (ushort)(IsConnected ? 1 : 0); }
+ }
- ///
- /// Determines whether client will attempt reconnection on failure. Default is true
- ///
- public bool AutoReconnect { get; set; }
-
- ///
- /// Will be set and unset by connect and disconnect only
- ///
- public bool ConnectEnabled { get; private set; }
-
- ///
- /// S+ helper for AutoReconnect
- ///
- public ushort UAutoReconnect
- {
- get { return (ushort)(AutoReconnect ? 1 : 0); }
- set { AutoReconnect = value == 1; }
- }
-
- ///
- /// Millisecond value, determines the timeout period in between reconnect attempts.
- /// Set to 5000 by default
- ///
- public int AutoReconnectIntervalMs { get; set; }
-
- SshClient Client;
-
- ShellStream TheStream;
-
- Timer ReconnectTimer;
-
- private System.Threading.SemaphoreSlim connectLock = new System.Threading.SemaphoreSlim(1);
-
- private bool DisconnectLogged = false;
-
- ///
- /// Typical constructor.
- ///
- public GenericSshClient(string key, string hostname, int port, string username, string password) :
- base(key)
- {
- StreamDebugging = new CommunicationStreamDebugging(key);
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- Key = key;
- Hostname = hostname;
- Port = port;
- Username = username;
- Password = password;
- AutoReconnectIntervalMs = 5000;
-
- ReconnectTimer = new Timer { AutoReset = false, Enabled = false };
- ReconnectTimer.Elapsed += (s, e) =>
+ ///
+ /// Socket status change event
+ ///
+ public SocketStatus ClientStatus
+ {
+ get { lock (_stateLock) { return _ClientStatus; } }
+ private set
+ {
+ bool shouldFireEvent = false;
+ lock (_stateLock)
{
- if (ConnectEnabled)
+ if (_ClientStatus != value)
+ {
+ _ClientStatus = value;
+ shouldFireEvent = true;
+ }
+ }
+ // Fire event outside lock to avoid deadlock
+ if (shouldFireEvent)
+ OnConnectionChange();
+ }
+ }
+
+ private SocketStatus _ClientStatus;
+ private bool _ConnectEnabled;
+
+ ///
+ /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
+ /// and IsConnected with be true when this == 2.
+ ///
+ public ushort UStatus
+ {
+ get { lock (_stateLock) { return (ushort)_ClientStatus; } }
+ }
+
+ ///
+ /// Determines whether client will attempt reconnection on failure. Default is true
+ ///
+ public bool AutoReconnect { get; set; }
+
+ ///
+ /// Will be set and unset by connect and disconnect only
+ ///
+ public bool ConnectEnabled
+ {
+ get { lock (_stateLock) { return _ConnectEnabled; } }
+ private set { lock (_stateLock) { _ConnectEnabled = value; } }
+ }
+
+ ///
+ /// S+ helper for AutoReconnect
+ ///
+ public ushort UAutoReconnect
+ {
+ get { return (ushort)(AutoReconnect ? 1 : 0); }
+ set { AutoReconnect = value == 1; }
+ }
+
+ ///
+ /// Gets or sets the AutoReconnectIntervalMs
+ ///
+ public int AutoReconnectIntervalMs { get; set; }
+
+ private SshClient client;
+
+ private ShellStream shellStream;
+
+ private readonly Timer reconnectTimer;
+
+ //Lock object to prevent simulatneous connect/disconnect operations
+ //private CCriticalSection connectLock = new CCriticalSection();
+ private readonly SemaphoreSlim connectLock = new SemaphoreSlim(1);
+
+ // Thread-safety lock for state changes
+ private readonly object _stateLock = new object();
+
+ private volatile bool _isProgramStopping;
+
+ private bool disconnectLogged = false;
+
+ ///
+ /// When true, turns off echo for the SSH session
+ ///
+ public bool DisableEcho { get; set; }
+
+ ///
+ /// Typical constructor.
+ ///
+ public GenericSshClient(string key, string hostname, int port, string username, string password) :
+ base(key)
+ {
+ StreamDebugging = new CommunicationStreamDebugging(key);
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ Key = key;
+ Hostname = hostname;
+ Port = port;
+ Username = username;
+ Password = password;
+ AutoReconnectIntervalMs = 5000;
+
+ reconnectTimer = new Timer(o =>
+ {
+ if (ConnectEnabled) // Now thread-safe property access
{
Connect();
}
- };
- }
+ }, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
+ }
- ///
- /// S+ Constructor - Must set all properties before calling Connect
- ///
- public GenericSshClient()
- : base(SPlusKey)
- {
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- AutoReconnectIntervalMs = 5000;
-
- ReconnectTimer = new Timer { AutoReset = false, Enabled = false };
- ReconnectTimer.Elapsed += (s, e) =>
+ ///
+ /// S+ Constructor - Must set all properties before calling Connect
+ ///
+ public GenericSshClient()
+ : base(SPlusKey)
{
- if (ConnectEnabled)
- {
- Connect();
- }
- };
- }
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ AutoReconnectIntervalMs = 5000;
- ///
- /// Handles closing this up when the program shuts down
- ///
- void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
- {
- if (programEventType == eProgramStatusEventType.Stopping)
- {
- if (Client != null)
+ reconnectTimer = new Timer(o =>
{
+ if (ConnectEnabled) // Now thread-safe property access
+ {
+ Connect();
+ }
+ }, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
+ }
+
+ ///
+ /// Handles closing this up when the program shuts down
+ ///
+ private void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping)
+ {
+ _isProgramStopping = true;
this.LogDebug("Program stopping. Closing connection");
Disconnect();
}
}
- }
- ///
- /// Connect to the server, using the provided properties.
- ///
- public void Connect()
- {
- // Don't go unless everything is here
- if (string.IsNullOrEmpty(Hostname) || Port < 1 || Port > 65535
- || Username == null || Password == null)
+ ///
+ /// Connect method
+ ///
+ public void Connect()
{
- this.LogError("Connect failed. Check hostname, port, username and password are set or not null");
- return;
- }
-
- ConnectEnabled = true;
-
- try
- {
- connectLock.Wait();
- if (IsConnected)
+ // Don't go unless everything is here
+ if (string.IsNullOrEmpty(Hostname) || Port < 1 || Port > 65535
+ || Username == null || Password == null)
{
- this.LogDebug("Connection already connected. Exiting Connect");
+ this.LogError("Connect failed. Check hostname, port, username and password are set or not null");
+ return;
}
- else
+
+ if (_isProgramStopping)
{
- this.LogDebug("Attempting connect");
-
- // Cancel reconnect if running.
- if (ReconnectTimer != null)
- {
- ReconnectTimer.Stop();
- }
-
- // Cleanup the old client if it already exists
- if (Client != null)
- {
- this.LogDebug("Cleaning up disconnected client");
- KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
- }
-
- // This handles both password and keyboard-interactive (like on OS-X, 'nixes)
- KeyboardInteractiveAuthenticationMethod kauth = new KeyboardInteractiveAuthenticationMethod(Username);
- kauth.AuthenticationPrompt += new EventHandler(kauth_AuthenticationPrompt);
- PasswordAuthenticationMethod pauth = new PasswordAuthenticationMethod(Username, Password);
-
- this.LogDebug("Creating new SshClient");
- ConnectionInfo connectionInfo = new ConnectionInfo(Hostname, Port, Username, pauth, kauth);
- Client = new SshClient(connectionInfo);
- Client.ErrorOccurred += Client_ErrorOccurred;
-
- //Attempt to connect
- ClientStatus = SocketStatus.SOCKET_STATUS_WAITING;
- try
- {
- Client.Connect();
- TheStream = Client.CreateShellStream("PDTShell", 0, 0, 0, 0, 65534);
- if (TheStream.DataAvailable)
- {
- // empty the buffer if there is data
- string str = TheStream.Read();
- }
- TheStream.DataReceived += Stream_DataReceived;
- this.LogInformation("Connected");
- ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED;
- DisconnectLogged = false;
- }
- catch (SshConnectionException e)
- {
- var ie = e.InnerException; // The details are inside!!
-
- if (ie is SocketException)
- {
- this.LogException(ie, "CONNECTION failure: Cannot reach host");
- }
-
- if (ie is System.Net.Sockets.SocketException socketException)
- {
- this.LogException(ie, "Connection failure: Cannot reach {host} on {port}",
- Hostname, Port);
- }
- if (ie is SshAuthenticationException)
- {
- this.LogException(ie, "Authentication failure for username {userName}", Username);
- }
- else
- this.LogException(ie, "Error on connect");
-
- DisconnectLogged = true;
- KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
- if (AutoReconnect)
- {
- this.LogDebug("Checking autoreconnect: {autoReconnect}, {autoReconnectInterval}ms", AutoReconnect, AutoReconnectIntervalMs);
- ReconnectTimer.Stop();
- ReconnectTimer.Interval = AutoReconnectIntervalMs;
- ReconnectTimer.Start();
- }
- }
- catch (SshOperationTimeoutException ex)
- {
- this.LogWarning("Connection attempt timed out: {message}", ex.Message);
-
- DisconnectLogged = true;
- KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
- if (AutoReconnect)
- {
- this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
- ReconnectTimer.Stop();
- ReconnectTimer.Interval = AutoReconnectIntervalMs;
- ReconnectTimer.Start();
- }
- }
- catch (Exception e)
- {
- this.LogException(e, "Unhandled exception on connect");
- DisconnectLogged = true;
- KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
- if (AutoReconnect)
- {
- this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
- ReconnectTimer.Stop();
- ReconnectTimer.Interval = AutoReconnectIntervalMs;
- ReconnectTimer.Start();
- }
- }
+ this.LogDebug("Skipping connect because program is stopping");
+ return;
}
- }
- finally
- {
- connectLock.Release();
- }
- }
- ///
- /// Disconnect method
- ///
- public void Disconnect()
- {
- ConnectEnabled = false;
- // Stop trying reconnects, if we are
- ReconnectTimer.Stop();
+ ConnectEnabled = true;
- KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
- }
-
- ///
- /// Kills the stream, cleans up the client and sets it to null
- ///
- private void KillClient(SocketStatus status)
- {
- KillStream();
-
- try
- {
- if (Client != null)
- {
- Client.ErrorOccurred -= Client_ErrorOccurred;
- Client.Disconnect();
- Client.Dispose();
- Client = null;
- ClientStatus = status;
- this.LogDebug("Disconnected");
- }
- }
- catch (Exception ex)
- {
- this.LogException(ex, "Exception in Kill Client");
- }
- }
-
- ///
- /// Kills the stream
- ///
- void KillStream()
- {
- try
- {
- if (TheStream != null)
- {
- TheStream.DataReceived -= Stream_DataReceived;
- TheStream.Close();
- TheStream.Dispose();
- TheStream = null;
- this.LogDebug("Disconnected stream");
- }
- }
- catch (Exception ex)
- {
- this.LogException(ex, "Exception in Kill Stream:{0}");
- }
- }
-
- ///
- /// Handles the keyboard interactive authentication, should it be required.
- ///
- void kauth_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)
- {
- foreach (AuthenticationPrompt prompt in e.Prompts)
- if (prompt.Request.IndexOf("Password:", StringComparison.InvariantCultureIgnoreCase) != -1)
- prompt.Response = Password;
- }
-
- ///
- /// Handler for data receive on ShellStream. Passes data across to queue for line parsing.
- ///
- void Stream_DataReceived(object sender, ShellDataEventArgs e)
- {
- if (((ShellStream)sender).Length <= 0L)
- {
- return;
- }
- var response = ((ShellStream)sender).Read();
-
- var bytesHandler = BytesReceived;
-
- if (bytesHandler != null)
- {
- var bytes = Encoding.UTF8.GetBytes(response);
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- {
- this.LogInformation("Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
- }
- bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
- }
-
- var textHandler = TextReceived;
- if (textHandler != null)
- {
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- this.LogInformation("Received: '{0}'", ComTextHelper.GetDebugText(response));
-
- textHandler(this, new GenericCommMethodReceiveTextArgs(response));
- }
-
- }
-
-
- ///
- /// Error event handler for client events - disconnect, etc. Will forward those events via ConnectionChange
- /// event
- ///
- void Client_ErrorOccurred(object sender, ExceptionEventArgs e)
- {
- System.Threading.Tasks.Task.Run(() =>
- {
- if (e.Exception is SshConnectionException || e.Exception is System.Net.Sockets.SocketException)
- this.LogError("Disconnected by remote");
- else
- this.LogException(e.Exception, "Unhandled SSH client error");
try
{
connectLock.Wait();
- KillClient(SocketStatus.SOCKET_STATUS_BROKEN_REMOTELY);
+ if (IsConnected)
+ {
+ this.LogDebug("Connection already connected. Exiting Connect");
+ }
+ else
+ {
+ this.LogDebug("Attempting connect");
+
+ // Cancel reconnect if running.
+ StopReconnectTimer();
+
+ // Cleanup the old client if it already exists
+ if (client != null)
+ {
+ this.LogDebug("Cleaning up disconnected client");
+ KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
+ }
+
+ // This handles both password and keyboard-interactive (like on OS-X, 'nixes)
+ KeyboardInteractiveAuthenticationMethod kauth = new KeyboardInteractiveAuthenticationMethod(Username);
+ kauth.AuthenticationPrompt += new EventHandler(kauth_AuthenticationPrompt);
+ PasswordAuthenticationMethod pauth = new PasswordAuthenticationMethod(Username, Password);
+
+ this.LogDebug("Creating new SshClient");
+ ConnectionInfo connectionInfo = new ConnectionInfo(Hostname, Port, Username, pauth, kauth);
+ client = new SshClient(connectionInfo);
+ client.ErrorOccurred += Client_ErrorOccurred;
+
+ //Attempt to connect
+ ClientStatus = SocketStatus.SOCKET_STATUS_WAITING;
+ try
+ {
+ client.Connect();
+
+ var modes = new Dictionary();
+
+ if (DisableEcho)
+ {
+ modes.Add(TerminalModes.ECHO, 0);
+ }
+
+ shellStream = client.CreateShellStream("PDTShell", 0, 0, 0, 0, 65534, modes);
+ if (shellStream.DataAvailable)
+ {
+ // empty the buffer if there is data
+ shellStream.Read();
+ }
+ shellStream.DataReceived += Stream_DataReceived;
+ this.LogInformation("Connected");
+ ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED;
+ disconnectLogged = false;
+ }
+ catch (SshConnectionException e)
+ {
+ var ie = e.InnerException; // The details are inside, when present - remote can close the connection with no inner exception at all
+
+ if (ie is System.Net.Sockets.SocketException socketException)
+ {
+ this.LogError("Connection failure: Cannot reach {host} on {port}",
+ Hostname, Port);
+ this.LogVerbose(socketException, "SocketException details: ");
+ }
+ else if (ie is SshAuthenticationException)
+ {
+ this.LogError("Authentication failure for username {userName}", Username);
+ this.LogVerbose(ie, "AuthenticationException details: ");
+ }
+ else
+ {
+ this.LogError("Error on connect: {error}", ie?.Message ?? e.Message);
+ this.LogVerbose(ie ?? e, "Exception details: ");
+ }
+
+ disconnectLogged = true;
+ KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
+ if (AutoReconnect && ConnectEnabled && !_isProgramStopping)
+ {
+ this.LogDebug("Checking autoreconnect: {autoReconnect}, {autoReconnectInterval}ms", AutoReconnect, AutoReconnectIntervalMs);
+ StartReconnectTimer();
+ }
+ }
+ catch (SshOperationTimeoutException ex)
+ {
+ this.LogWarning("Connection attempt timed out: {message}", ex.Message);
+
+ disconnectLogged = true;
+ KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
+ if (AutoReconnect && ConnectEnabled && !_isProgramStopping)
+ {
+ this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
+ StartReconnectTimer();
+ }
+ }
+ catch (Exception e)
+ {
+ this.LogError("Unhandled exception on connect: {error}", e.Message);
+ this.LogVerbose(e, "Exception details: ");
+ disconnectLogged = true;
+ KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
+ if (AutoReconnect && ConnectEnabled && !_isProgramStopping)
+ {
+ this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
+ StartReconnectTimer();
+ }
+ }
+ }
}
finally
{
connectLock.Release();
}
- if (AutoReconnect && ConnectEnabled)
+ }
+
+ ///
+ /// Disconnect method
+ ///
+ public void Disconnect()
+ {
+ ConnectEnabled = false;
+ // Stop trying reconnects, if we are
+ StopReconnectTimer();
+
+ KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
+ }
+
+ ///
+ /// Kills the stream, cleans up the client and sets it to null
+ ///
+ private void KillClient(SocketStatus status)
+ {
+ KillStream();
+
+ try
{
- this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
- ReconnectTimer.Stop();
- ReconnectTimer.Interval = AutoReconnectIntervalMs;
- ReconnectTimer.Start();
+ if (client != null)
+ {
+ client.ErrorOccurred -= Client_ErrorOccurred;
+ client.Disconnect();
+ client.Dispose();
+ client = null;
+ ClientStatus = status;
+ this.LogDebug("Disconnected");
+ }
}
- });
+ catch (Exception ex)
+ {
+ this.LogException(ex, "Exception in Kill Client");
+ }
+ }
+
+ ///
+ /// Kills the stream
+ ///
+ private void KillStream()
+ {
+ try
+ {
+ if (shellStream != null)
+ {
+ shellStream.DataReceived -= Stream_DataReceived;
+ shellStream.Close();
+ shellStream.Dispose();
+ shellStream = null;
+ this.LogDebug("Disconnected stream");
+ }
+ }
+ catch (Exception ex)
+ {
+ this.LogException(ex, "Exception in Kill Stream:{0}");
+ }
+ }
+
+ ///
+ /// Handles the keyboard interactive authentication, should it be required.
+ ///
+ private void kauth_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)
+ {
+ foreach (AuthenticationPrompt prompt in e.Prompts)
+ if (prompt.Request.IndexOf("Password:", StringComparison.InvariantCultureIgnoreCase) != -1)
+ prompt.Response = Password;
+ }
+
+ ///
+ /// Handler for data receive on ShellStream. Passes data across to queue for line parsing.
+ ///
+ private void Stream_DataReceived(object sender, ShellDataEventArgs e)
+ {
+ if (((ShellStream)sender).Length <= 0L)
+ {
+ return;
+ }
+ var response = ((ShellStream)sender).Read();
+
+ var bytesHandler = BytesReceived;
+
+ if (bytesHandler != null)
+ {
+ var bytes = Encoding.UTF8.GetBytes(response);
+ this.PrintReceivedBytes(bytes);
+ bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
+ }
+
+ var textHandler = TextReceived;
+ if (textHandler != null)
+ {
+ this.PrintReceivedText(response);
+
+ textHandler(this, new GenericCommMethodReceiveTextArgs(response));
+ }
+
+ }
+
+
+ ///
+ /// Error event handler for client events - disconnect, etc. Will forward those events via ConnectionChange
+ /// event
+ ///
+ private void Client_ErrorOccurred(object sender, ExceptionEventArgs e)
+ {
+ CrestronInvoke.BeginInvoke(o =>
+ {
+ if (e.Exception is SshConnectionException || e.Exception is System.Net.Sockets.SocketException)
+ this.LogError("Disconnected by remote");
+ else
+ this.LogException(e.Exception, "Unhandled SSH client error");
+ try
+ {
+ connectLock.Wait();
+ KillClient(SocketStatus.SOCKET_STATUS_BROKEN_REMOTELY);
+ }
+ finally
+ {
+ connectLock.Release();
+ }
+ if (AutoReconnect && ConnectEnabled && !_isProgramStopping)
+ {
+ this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
+ StartReconnectTimer();
+ }
+ });
+ }
+
+ ///
+ /// Helper for ConnectionChange event
+ ///
+ private void OnConnectionChange()
+ {
+ ConnectionChange?.Invoke(this, new GenericSocketStatusChageEventArgs(this));
+ }
+
+ #region IBasicCommunication Members
+
+ ///
+ /// Sends text to the server
+ ///
+ /// The text to send
+ public void SendText(string text)
+ {
+ try
+ {
+ if (client != null && shellStream != null && IsConnected)
+ {
+ this.PrintSentText(text);
+
+ shellStream.Write(text);
+ shellStream.Flush();
+ }
+ else
+ {
+ this.LogDebug("Client is null or disconnected. Cannot Send Text");
+ }
+ }
+ catch (ObjectDisposedException)
+ {
+ this.LogError("ObjectDisposedException sending '{message}'. Restarting connection...", text.Trim());
+
+ KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
+ if (AutoReconnect && ConnectEnabled && !_isProgramStopping)
+ {
+ StartReconnectTimer();
+ }
+ }
+ catch (Exception ex)
+ {
+ this.LogException(ex, "Exception sending text: '{message}'", text);
+ }
+ }
+
+ ///
+ /// Sends Bytes to the server
+ ///
+ /// The bytes to send
+ public void SendBytes(byte[] bytes)
+ {
+ try
+ {
+ if (client != null && shellStream != null && IsConnected)
+ {
+ this.PrintSentBytes(bytes);
+
+ shellStream.Write(bytes, 0, bytes.Length);
+ shellStream.Flush();
+ }
+ else
+ {
+ this.LogDebug("Client is null or disconnected. Cannot Send Bytes");
+ }
+ }
+ catch (ObjectDisposedException ex)
+ {
+ this.LogException(ex, "ObjectDisposedException sending {message}", ComTextHelper.GetEscapedText(bytes));
+
+ KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
+ if (AutoReconnect && ConnectEnabled && !_isProgramStopping)
+ {
+ StartReconnectTimer();
+ }
+ }
+ catch (Exception ex)
+ {
+ this.LogException(ex, "Exception sending {message}", ComTextHelper.GetEscapedText(bytes));
+ }
+ }
+ #endregion
+
+ ///
+ /// Safely starts the reconnect timer with exception handling
+ ///
+ private void StartReconnectTimer()
+ {
+ try
+ {
+ reconnectTimer?.Change(AutoReconnectIntervalMs, System.Threading.Timeout.Infinite);
+ }
+ catch (ObjectDisposedException)
+ {
+ // Timer was disposed, ignore
+ this.LogDebug("Attempted to start timer but it was already disposed");
+ }
+ }
+
+ ///
+ /// Safely stops the reconnect timer with exception handling
+ ///
+ private void StopReconnectTimer()
+ {
+ try
+ {
+ reconnectTimer?.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
+ }
+ catch (ObjectDisposedException)
+ {
+ // Timer was disposed, ignore
+ this.LogDebug("Attempted to stop timer but it was already disposed");
+ }
+ }
+
+ ///
+ /// Deactivate method - properly dispose of resources
+ ///
+ public override bool Deactivate()
+ {
+ try
+ {
+ this.LogDebug("Deactivating SSH client - disposing resources");
+
+ // Stop trying reconnects
+ ConnectEnabled = false;
+ StopReconnectTimer();
+
+ // Disconnect and cleanup client
+ KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
+
+ // Dispose timer
+ try
+ {
+ reconnectTimer?.Dispose();
+ }
+ catch (ObjectDisposedException)
+ {
+ // Already disposed, ignore
+ }
+
+ // Dispose semaphore
+ try
+ {
+ connectLock?.Dispose();
+ }
+ catch (ObjectDisposedException)
+ {
+ // Already disposed, ignore
+ }
+
+ return base.Deactivate();
+ }
+ catch (Exception ex)
+ {
+ this.LogException(ex, "Error during SSH client deactivation");
+ return false;
+ }
+ }
+
}
- ///
- /// Helper for ConnectionChange event
- ///
- void OnConnectionChange()
- {
- if (ConnectionChange != null)
- ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
- }
-
- #region IBasicCommunication Members
-
- ///
- /// Sends text to the server
- ///
- ///
- public void SendText(string text)
- {
- try
- {
- if (Client != null && TheStream != null && IsConnected)
- {
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- this.LogInformation(
- "Sending {length} characters of text: '{text}'",
- text.Length,
- ComTextHelper.GetDebugText(text));
-
- TheStream.Write(text);
- TheStream.Flush();
- }
- else
- {
- this.LogDebug("Client is null or disconnected. Cannot Send Text");
- }
- }
- catch (ObjectDisposedException)
- {
- this.LogError("ObjectDisposedException sending '{message}'. Restarting connection...", text.Trim());
-
- KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
- ReconnectTimer.Stop();
- ReconnectTimer.Start();
- }
- catch (Exception ex)
- {
- this.LogException(ex, "Exception sending text: '{message}'", text);
- }
- }
-
- ///
- /// Sends Bytes to the server
- ///
- ///
- public void SendBytes(byte[] bytes)
- {
- try
- {
- if (Client != null && TheStream != null && IsConnected)
- {
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- this.LogInformation("Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
-
- TheStream.Write(bytes, 0, bytes.Length);
- TheStream.Flush();
- }
- else
- {
- this.LogDebug("Client is null or disconnected. Cannot Send Bytes");
- }
- }
- catch (ObjectDisposedException ex)
- {
- this.LogException(ex, "ObjectDisposedException sending {message}", ComTextHelper.GetEscapedText(bytes));
-
- KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
- ReconnectTimer.Stop();
- ReconnectTimer.Start();
- }
- catch (Exception ex)
- {
- this.LogException(ex, "Exception sending {message}", ComTextHelper.GetEscapedText(bytes));
- }
- }
- #endregion
-
//*****************************************************************************************************
//*****************************************************************************************************
///
- /// Fired when connection changes
+ /// Represents a SshConnectionChangeEventArgs
///
public class SshConnectionChangeEventArgs : EventArgs
{
///
/// Connection State
///
- public bool IsConnected { get; private set; }
+ public bool IsConnected { get; private set; }
///
- /// Connection Status represented as a ushort
+ /// Gets or sets the UIsConnected
///
public ushort UIsConnected { get { return (ushort)(Client.IsConnected ? 1 : 0); } }
///
- /// The client
+ /// Gets or sets the Client
///
public GenericSshClient Client { get; private set; }
///
- /// Socket Status as represented by
+ /// Gets or sets the Status
///
public ushort Status { get { return Client.UStatus; } }
@@ -584,7 +680,7 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
///
/// Connection State
/// The Client
- public SshConnectionChangeEventArgs(bool isConnected, GenericSshClient client)
+ public SshConnectionChangeEventArgs(bool isConnected, GenericSshClient client)
{
IsConnected = isConnected;
Client = client;
diff --git a/src/PepperDash.Core/Comm/GenericTcpIpClient.cs b/src/PepperDash.Core/Comm/GenericTcpIpClient.cs
index 54298cc3..d13eed3f 100644
--- a/src/PepperDash.Core/Comm/GenericTcpIpClient.cs
+++ b/src/PepperDash.Core/Comm/GenericTcpIpClient.cs
@@ -1,552 +1,569 @@
-extern alias NewtonsoftJson;
-
-using System;
+using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
-using System.Threading;
-using Timer = System.Timers.Timer;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronSockets;
-using JsonProperty = NewtonsoftJson::Newtonsoft.Json.JsonPropertyAttribute;
-using Required = NewtonsoftJson::Newtonsoft.Json.Required;
-using PepperDash.Core.Logging;
-using System.Threading.Tasks;
+using Newtonsoft.Json;
-namespace PepperDash.Core;
-
-///
-/// A class to handle basic TCP/IP communications with a server
-///
-public class GenericTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
+namespace PepperDash.Core
{
- private const string SplusKey = "Uninitialized TcpIpClient";
///
- /// Object to enable stream debugging
+ /// A class to handle basic TCP/IP communications with a server
///
- public CommunicationStreamDebugging StreamDebugging { get; private set; }
-
- ///
- /// Fires when data is received from the server and returns it as a Byte array
- ///
- public event EventHandler BytesReceived;
-
- ///
- /// Fires when data is received from the server and returns it as text
- ///
- public event EventHandler TextReceived;
-
- ///
- ///
- ///
- //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
- public event EventHandler ConnectionChange;
-
-
- private string _hostname;
-
- ///
- /// Address of server
- ///
- public string Hostname
+ public class GenericTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
{
- get
+ private const string SplusKey = "Uninitialized TcpIpClient";
+ ///
+ /// Object to enable stream debugging
+ ///
+ public CommunicationStreamDebugging StreamDebugging { get; private set; }
+
+ ///
+ /// Fires when data is received from the server and returns it as a Byte array
+ ///
+ public event EventHandler BytesReceived;
+
+ ///
+ /// Fires when data is received from the server and returns it as text
+ ///
+ public event EventHandler TextReceived;
+
+ ///
+ ///
+ ///
+ //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
+ public event EventHandler ConnectionChange;
+
+
+ private string _hostname;
+
+ ///
+ /// Address of server
+ ///
+ public string Hostname
{
- return _hostname;
+ get
+ {
+ return _hostname;
+ }
+
+ set
+ {
+ _hostname = value;
+ if (_client != null)
+ {
+ _client.AddressClientConnectedTo = _hostname;
+ }
+ }
}
- set
+ ///
+ /// Gets or sets the Port
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// Another damn S+ helper because S+ seems to treat large port nums as signed ints
+ /// which screws up things
+ ///
+ public ushort UPort
{
- _hostname = value;
+ get { return Convert.ToUInt16(Port); }
+ set { Port = Convert.ToInt32(value); }
+ }
+
+ ///
+ /// Defaults to 2000
+ ///
+ public int BufferSize { get; set; }
+
+ ///
+ /// The actual client class
+ ///
+ private TCPClient _client;
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsConnected
+ {
+ get { return _client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsConnected
+ {
+ get { return (ushort)(IsConnected ? 1 : 0); }
+ }
+
+ ///
+ /// _client socket status Read only
+ ///
+ public SocketStatus ClientStatus
+ {
+ get
+ {
+ return _client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : _client.ClientStatus;
+ }
+ }
+
+ ///
+ /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
+ /// and IsConnected would be true when this == 2.
+ ///
+ public ushort UStatus
+ {
+ get { return (ushort)ClientStatus; }
+ }
+
+ ///
+ /// Status text shows the message associated with socket status
+ ///
+ public string ClientStatusText { get { return ClientStatus.ToString(); } }
+
+ ///
+ /// Ushort representation of client status
+ ///
+ [Obsolete]
+ public ushort UClientStatus { get { return (ushort)ClientStatus; } }
+
+ ///
+ /// Connection failure reason
+ ///
+ public string ConnectionFailure { get { return ClientStatus.ToString(); } }
+
+ ///
+ /// Gets or sets the AutoReconnect
+ ///
+ public bool AutoReconnect { get; set; }
+
+ ///
+ /// S+ helper for AutoReconnect
+ ///
+ public ushort UAutoReconnect
+ {
+ get { return (ushort)(AutoReconnect ? 1 : 0); }
+ set { AutoReconnect = value == 1; }
+ }
+
+ ///
+ /// Milliseconds to wait before attempting to reconnect. Defaults to 5000
+ ///
+ public int AutoReconnectIntervalMs { get; set; }
+
+ ///
+ /// Set only when the disconnect method is called
+ ///
+ bool DisconnectCalledByUser;
+
+ ///
+ ///
+ ///
+ public bool Connected
+ {
+ get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
+ }
+
+ //Lock object to prevent simulatneous connect/disconnect operations
+ private CCriticalSection connectLock = new CCriticalSection();
+
+ // private Timer for auto reconnect
+ private CTimer RetryTimer;
+
+ ///
+ /// Constructor
+ ///
+ /// unique string to differentiate between instances
+ ///
+ ///
+ ///
+ public GenericTcpIpClient(string key, string address, int port, int bufferSize)
+ : base(key)
+ {
+ StreamDebugging = new CommunicationStreamDebugging(key);
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ AutoReconnectIntervalMs = 5000;
+ Hostname = address;
+ Port = port;
+ BufferSize = bufferSize;
+
+ RetryTimer = new CTimer(o =>
+ {
+ Reconnect();
+ }, Timeout.Infinite);
+ }
+
+ ///
+ /// Constructor
+ ///
+ ///
+ public GenericTcpIpClient(string key)
+ : base(key)
+ {
+ StreamDebugging = new CommunicationStreamDebugging(key);
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ AutoReconnectIntervalMs = 5000;
+ BufferSize = 2000;
+
+ RetryTimer = new CTimer(o =>
+ {
+ Reconnect();
+ }, Timeout.Infinite);
+ }
+
+ ///
+ /// Default constructor for S+
+ ///
+ public GenericTcpIpClient()
+ : base(SplusKey)
+ {
+ StreamDebugging = new CommunicationStreamDebugging(SplusKey);
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ AutoReconnectIntervalMs = 5000;
+ BufferSize = 2000;
+
+ RetryTimer = new CTimer(o =>
+ {
+ Reconnect();
+ }, Timeout.Infinite);
+ }
+
+ ///
+ /// Initialize method
+ ///
+ public void Initialize(string key)
+ {
+ Key = key;
+ }
+
+ ///
+ /// Handles closing this up when the program shuts down
+ ///
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping)
+ {
+ Debug.Console(1, this, "Program stopping. Closing connection");
+ Deactivate();
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// Deactivate method
+ ///
+ public override bool Deactivate()
+ {
+ RetryTimer.Stop();
+ RetryTimer.Dispose();
if (_client != null)
{
- _client.AddressClientConnectedTo = _hostname;
+ _client.SocketStatusChange -= this.Client_SocketStatusChange;
+ DisconnectClient();
}
+ return true;
}
- }
- ///
- /// Port on server
- ///
- public int Port { get; set; }
-
- ///
- /// Another damn S+ helper because S+ seems to treat large port nums as signed ints
- /// which screws up things
- ///
- public ushort UPort
- {
- get { return Convert.ToUInt16(Port); }
- set { Port = Convert.ToInt32(value); }
- }
-
- ///
- /// Defaults to 2000
- ///
- public int BufferSize { get; set; }
-
- ///
- /// The actual client class
- ///
- private TCPClient _client;
-
- ///
- /// Bool showing if socket is connected
- ///
- public bool IsConnected
- {
- get { return _client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
- }
-
- ///
- /// S+ helper for IsConnected
- ///
- public ushort UIsConnected
- {
- get { return (ushort)(IsConnected ? 1 : 0); }
- }
-
- ///
- /// _client socket status Read only
- ///
- public SocketStatus ClientStatus
- {
- get
- {
- return _client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : _client.ClientStatus;
- }
- }
-
- ///
- /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
- /// and IsConnected would be true when this == 2.
- ///
- public ushort UStatus
- {
- get { return (ushort)ClientStatus; }
- }
-
- ///
- /// Status text shows the message associated with socket status
- ///
- public string ClientStatusText { get { return ClientStatus.ToString(); } }
-
- ///
- /// Connection failure reason
- ///
- public string ConnectionFailure { get { return ClientStatus.ToString(); } }
-
- ///
- /// Gets or sets the AutoReconnect
- ///
- public bool AutoReconnect { get; set; }
-
- ///
- /// S+ helper for AutoReconnect
- ///
- public ushort UAutoReconnect
- {
- get { return (ushort)(AutoReconnect ? 1 : 0); }
- set { AutoReconnect = value == 1; }
- }
-
- ///
- /// Milliseconds to wait before attempting to reconnect. Defaults to 5000
- ///
- public int AutoReconnectIntervalMs { get; set; }
-
- ///
- /// Set only when the disconnect method is called
- ///
- bool DisconnectCalledByUser;
-
- ///
- ///
- ///
- public bool Connected
- {
- get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
- }
-
- //Lock object to prevent simulatneous connect/disconnect operations
- private readonly object _connectLock = new();
-
- // private Timer for auto reconnect
- private Timer RetryTimer;
-
- ///
- /// Constructor
- ///
- /// unique string to differentiate between instances
- ///
- ///
- ///
- public GenericTcpIpClient(string key, string address, int port, int bufferSize)
- : base(key)
- {
- StreamDebugging = new CommunicationStreamDebugging(key);
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- AutoReconnectIntervalMs = 5000;
- Hostname = address;
- Port = port;
- BufferSize = bufferSize;
-
- SetupRetryTimer();
- }
-
- ///
- /// Constructor
- ///
- ///
- public GenericTcpIpClient(string key)
- : base(key)
- {
- StreamDebugging = new CommunicationStreamDebugging(key);
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- AutoReconnectIntervalMs = 5000;
- BufferSize = 2000;
-
- SetupRetryTimer();
- }
-
- ///
- /// Default constructor for S+
- ///
- public GenericTcpIpClient()
- : base(SplusKey)
- {
- StreamDebugging = new CommunicationStreamDebugging(SplusKey);
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- AutoReconnectIntervalMs = 5000;
- BufferSize = 2000;
-
- SetupRetryTimer();
- }
-
- private void SetupRetryTimer()
- {
- RetryTimer = new Timer { AutoReset = false, Enabled = false };
- RetryTimer.Elapsed += (s, e) => Reconnect();
- }
-
-
-
- ///
- /// Just to help S+ set the key
- ///
- public void Initialize(string key)
- {
- Key = key;
- }
-
- ///
- /// Handles closing this up when the program shuts down
- ///
- void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
- {
- if (programEventType == eProgramStatusEventType.Stopping)
- {
- this.LogInformation("Program stopping. Closing connection");
- Deactivate();
- }
- }
-
- ///
- ///
- ///
- ///
- public override bool Deactivate()
- {
- RetryTimer.Stop();
- RetryTimer.Dispose();
- if (_client != null)
- {
- _client.SocketStatusChange -= this.Client_SocketStatusChange;
- DisconnectClient();
- }
- return true;
- }
-
- ///
- /// Attempts to connect to the server
- ///
- public void Connect()
- {
- if (string.IsNullOrEmpty(Hostname))
- {
- this.LogWarning("GenericTcpIpClient '{0}': No address set", Key);
- return;
- }
- if (Port < 1 || Port > 65535)
+ ///
+ /// Connect method
+ ///
+ public void Connect()
{
+ if (string.IsNullOrEmpty(Hostname))
{
- this.LogWarning("GenericTcpIpClient '{0}': Invalid port", Key);
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': No address set", Key);
return;
}
- }
-
- lock (_connectLock)
- {
- if (IsConnected)
+ if (Port < 1 || Port > 65535)
{
- this.LogInformation("Connection already connected. Exiting Connect()");
- }
- else
- {
- //Stop retry timer if running
- RetryTimer.Stop();
- _client = new TCPClient(Hostname, Port, BufferSize);
- _client.SocketStatusChange -= Client_SocketStatusChange;
- _client.SocketStatusChange += Client_SocketStatusChange;
- DisconnectCalledByUser = false;
- _client.ConnectToServerAsync(ConnectToServerCallback);
- }
- }
- }
-
- private void Reconnect()
- {
- if (_client == null)
- {
- return;
- }
- lock (_connectLock)
- {
- if (IsConnected || DisconnectCalledByUser == true)
- {
- this.LogInformation("Reconnect no longer needed. Exiting Reconnect()");
- }
- else
- {
- this.LogInformation("Attempting reconnect now");
- _client.ConnectToServerAsync(ConnectToServerCallback);
- }
- }
- }
-
- ///
- /// Attempts to disconnect the client
- ///
- public void Disconnect()
- {
- lock (_connectLock)
- {
- DisconnectCalledByUser = true;
-
- // Stop trying reconnects, if we are
- RetryTimer.Stop();
- DisconnectClient();
- }
- }
-
- ///
- /// Does the actual disconnect business
- ///
- public void DisconnectClient()
- {
- if (_client != null)
- {
- this.LogInformation("Disconnecting client");
- if (IsConnected)
- _client.DisconnectFromServer();
- }
- }
-
- ///
- /// Callback method for connection attempt
- ///
- ///
- void ConnectToServerCallback(TCPClient c)
- {
- if (c.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- this.LogInformation("Server connection result: {0}", c.ClientStatus);
- WaitAndTryReconnect();
- }
- else
- {
- this.LogInformation("Server connection result: {0}", c.ClientStatus);
- }
- }
-
- ///
- /// Disconnects, waits and attemtps to connect again
- ///
- void WaitAndTryReconnect()
- {
- Task.Run(() =>
- {
- lock (_connectLock)
- {
- if (!IsConnected && AutoReconnect && !DisconnectCalledByUser && _client != null)
{
- DisconnectClient();
- this.LogInformation("Attempting reconnect, status={0}", _client.ClientStatus);
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': Invalid port", Key);
+ return;
+ }
+ }
+
+ try
+ {
+ connectLock.Enter();
+ if (IsConnected)
+ {
+ Debug.Console(1, this, "Connection already connected. Exiting Connect()");
+ }
+ else
+ {
+ //Stop retry timer if running
RetryTimer.Stop();
- RetryTimer.Interval = AutoReconnectIntervalMs;
- RetryTimer.Start();
+ _client = new TCPClient(Hostname, Port, BufferSize);
+ _client.SocketStatusChange -= Client_SocketStatusChange;
+ _client.SocketStatusChange += Client_SocketStatusChange;
+ DisconnectCalledByUser = false;
+ _client.ConnectToServerAsync(ConnectToServerCallback);
}
}
- });
- }
-
- ///
- /// Recieves incoming data
- ///
- ///
- ///
- void Receive(TCPClient client, int numBytes)
- {
- if (client != null)
- {
- if (numBytes > 0)
+ finally
{
- var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
- var bytesHandler = BytesReceived;
- if (bytesHandler != null)
- {
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- {
- this.LogInformation("Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
- }
- bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
- }
- var textHandler = TextReceived;
- if (textHandler != null)
- {
- var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
-
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- {
- this.LogInformation("Received {1} characters of text: '{0}'", ComTextHelper.GetDebugText(str), str.Length);
- }
-
- textHandler(this, new GenericCommMethodReceiveTextArgs(str));
- }
+ connectLock.Leave();
}
- client.ReceiveDataAsync(Receive);
}
- }
- ///
- /// General send method
- ///
- public void SendText(string text)
- {
- var bytes = Encoding.GetEncoding(28591).GetBytes(text);
- // Check debug level before processing byte array
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- this.LogInformation("Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
- if (_client != null)
- _client.SendData(bytes, bytes.Length);
- }
-
- ///
- /// SendEscapedText method
- ///
- public void SendEscapedText(string text)
- {
- var unescapedText = Regex.Replace(text, @"\\x([0-9a-fA-F][0-9a-fA-F])", s =>
+ private void Reconnect()
+ {
+ if (_client == null)
{
- var hex = s.Groups[1].Value;
- return ((char)Convert.ToByte(hex, 16)).ToString();
+ return;
+ }
+ try
+ {
+ connectLock.Enter();
+ if (IsConnected || DisconnectCalledByUser == true)
+ {
+ Debug.Console(1, this, "Reconnect no longer needed. Exiting Reconnect()");
+ }
+ else
+ {
+ Debug.Console(1, this, "Attempting reconnect now");
+ _client.ConnectToServerAsync(ConnectToServerCallback);
+ }
+ }
+ finally
+ {
+ connectLock.Leave();
+ }
+ }
+
+ ///
+ /// Disconnect method
+ ///
+ public void Disconnect()
+ {
+ try
+ {
+ connectLock.Enter();
+ DisconnectCalledByUser = true;
+
+ // Stop trying reconnects, if we are
+ RetryTimer.Stop();
+ DisconnectClient();
+ }
+ finally
+ {
+ connectLock.Leave();
+ }
+ }
+
+ ///
+ /// DisconnectClient method
+ ///
+ public void DisconnectClient()
+ {
+ if (_client != null)
+ {
+ Debug.Console(1, this, "Disconnecting client");
+ if (IsConnected)
+ _client.DisconnectFromServer();
+ }
+ }
+
+ ///
+ /// Callback method for connection attempt
+ ///
+ ///
+ void ConnectToServerCallback(TCPClient c)
+ {
+ if (c.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ Debug.Console(0, this, "Server connection result: {0}", c.ClientStatus);
+ WaitAndTryReconnect();
+ }
+ else
+ {
+ Debug.Console(1, this, "Server connection result: {0}", c.ClientStatus);
+ }
+ }
+
+ ///
+ /// Disconnects, waits and attemtps to connect again
+ ///
+ void WaitAndTryReconnect()
+ {
+ CrestronInvoke.BeginInvoke(o =>
+ {
+ try
+ {
+ connectLock.Enter();
+ if (!IsConnected && AutoReconnect && !DisconnectCalledByUser && _client != null)
+ {
+ DisconnectClient();
+ Debug.Console(1, this, "Attempting reconnect, status={0}", _client.ClientStatus);
+ RetryTimer.Reset(AutoReconnectIntervalMs);
+ }
+ }
+ finally
+ {
+ connectLock.Leave();
+ }
});
- SendText(unescapedText);
- }
-
- ///
- /// Sends Bytes to the server
- ///
- ///
- public void SendBytes(byte[] bytes)
- {
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- this.LogInformation("Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
- if (_client != null)
- _client.SendData(bytes, bytes.Length);
- }
-
- ///
- /// Socket Status Change Handler
- ///
- ///
- ///
- void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)
- {
- if (clientSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- this.LogDebug("Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
- WaitAndTryReconnect();
- }
- else
- {
- this.LogDebug("Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
- _client.ReceiveDataAsync(Receive);
}
- var handler = ConnectionChange;
- if (handler != null)
- ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
+ ///
+ /// Recieves incoming data
+ ///
+ ///
+ ///
+ void Receive(TCPClient client, int numBytes)
+ {
+ if (client != null)
+ {
+ if (numBytes > 0)
+ {
+ var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
+ var bytesHandler = BytesReceived;
+ if (bytesHandler != null)
+ {
+ this.PrintReceivedBytes(bytes);
+ bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
+ }
+ var textHandler = TextReceived;
+ if (textHandler != null)
+ {
+ var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
+
+ this.PrintReceivedText(str);
+
+ textHandler(this, new GenericCommMethodReceiveTextArgs(str));
+ }
+ }
+ client.ReceiveDataAsync(Receive);
+ }
+ }
+
+ ///
+ /// SendText method
+ ///
+ public void SendText(string text)
+ {
+ var bytes = Encoding.GetEncoding(28591).GetBytes(text);
+ // Check debug level before processing byte array
+ this.PrintSentText(text);
+ if (_client != null)
+ _client.SendData(bytes, bytes.Length);
+ }
+
+ ///
+ /// SendEscapedText method
+ ///
+ public void SendEscapedText(string text)
+ {
+ var unescapedText = Regex.Replace(text, @"\\x([0-9a-fA-F][0-9a-fA-F])", s =>
+ {
+ var hex = s.Groups[1].Value;
+ return ((char)Convert.ToByte(hex, 16)).ToString();
+ });
+ SendText(unescapedText);
+ }
+
+ ///
+ /// Sends Bytes to the server
+ ///
+ ///
+ ///
+ /// SendBytes method
+ ///
+ public void SendBytes(byte[] bytes)
+ {
+ this.PrintSentBytes(bytes);
+ if (_client != null)
+ _client.SendData(bytes, bytes.Length);
+ }
+
+ ///
+ /// Socket Status Change Handler
+ ///
+ ///
+ ///
+ void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)
+ {
+ if (clientSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ Debug.Console(0, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
+ WaitAndTryReconnect();
+ }
+ else
+ {
+ Debug.Console(1, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
+ _client.ReceiveDataAsync(Receive);
+ }
+
+ var handler = ConnectionChange;
+ if (handler != null)
+ ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
+ }
}
-}
-
-///
-/// Configuration properties for TCP/SSH Connections
-///
-public class TcpSshPropertiesConfig
-{
- ///
- /// Address to connect to
- ///
- [JsonProperty(Required = Required.Always)]
- public string Address { get; set; }
///
- /// Port to connect to
+ /// Represents a TcpSshPropertiesConfig
///
- [JsonProperty(Required = Required.Always)]
- public int Port { get; set; }
-
- ///
- /// Username credential
- ///
- public string Username { get; set; }
- ///
- /// Passord credential
- ///
- public string Password { get; set; }
-
- ///
- /// Defaults to 32768
- ///
- public int BufferSize { get; set; }
-
- ///
- /// Gets or sets the AutoReconnect
- ///
- public bool AutoReconnect { get; set; }
-
- ///
- /// Gets or sets the AutoReconnectIntervalMs
- ///
- public int AutoReconnectIntervalMs { get; set; }
-
- ///
- /// When true, turns off echo for the SSH session
- ///
- [JsonProperty("disableSshEcho")]
- public bool DisableSshEcho { get; set; }
-
- ///
- /// Default constructor
- ///
- public TcpSshPropertiesConfig()
+ public class TcpSshPropertiesConfig
{
- BufferSize = 32768;
- AutoReconnect = true;
- AutoReconnectIntervalMs = 5000;
- Username = "";
- Password = "";
- }
+ ///
+ /// Address to connect to
+ ///
+ [JsonProperty(Required = Required.Always)]
+ public string Address { get; set; }
+ ///
+ /// Port to connect to
+ ///
+ [JsonProperty(Required = Required.Always)]
+ public int Port { get; set; }
+
+ ///
+ /// Username credential
+ ///
+ public string Username { get; set; }
+ ///
+ /// Gets or sets the Password
+ ///
+ public string Password { get; set; }
+
+ ///
+ /// Defaults to 32768
+ ///
+ public int BufferSize { get; set; }
+
+ ///
+ /// Gets or sets the AutoReconnect
+ ///
+ public bool AutoReconnect { get; set; }
+
+ ///
+ /// Gets or sets the AutoReconnectIntervalMs
+ ///
+ public int AutoReconnectIntervalMs { get; set; }
+
+ ///
+ /// When true, turns off echo for the SSH session
+ ///
+ [JsonProperty("disableSshEcho")]
+ public bool DisableSshEcho { get; set; }
+
+ ///
+ /// Default constructor
+ ///
+ public TcpSshPropertiesConfig()
+ {
+ BufferSize = 32768;
+ AutoReconnect = true;
+ AutoReconnectIntervalMs = 5000;
+ Username = "";
+ Password = "";
+ DisableSshEcho = false;
+ }
+ }
}
diff --git a/src/PepperDash.Core/Comm/GenericTcpIpClient_ForServer.cs b/src/PepperDash.Core/Comm/GenericTcpIpClient_ForServer.cs
index 08259f6e..6d4958ca 100644
--- a/src/PepperDash.Core/Comm/GenericTcpIpClient_ForServer.cs
+++ b/src/PepperDash.Core/Comm/GenericTcpIpClient_ForServer.cs
@@ -11,781 +11,765 @@ PepperDash Technology Corporation reserves all rights under applicable laws.
------------------------------------ */
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Text;
-using System.Timers;
+using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronSockets;
using PepperDash.Core.Logging;
-namespace PepperDash.Core;
-
-///
-/// Generic TCP/IP client for server
-///
-public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
+namespace PepperDash.Core
{
///
- /// Band aid delegate for choked server
+ /// Generic TCP/IP client for server
///
- internal delegate void ConnectionHasHungCallbackDelegate();
-
- #region Events
-
- //public event EventHandler BytesReceived;
-
- ///
- /// Notifies of text received
- ///
- public event EventHandler TextReceived;
-
- ///
- /// Notifies of socket status change
- ///
- public event EventHandler ConnectionChange;
-
-
- ///
- /// This is something of a band-aid callback. If the client times out during the connection process, because the server
- /// is stuck, this will fire. It is intended to be used by the Server class monitor client, to help
- /// keep a watch on the server and reset it if necessary.
- ///
- internal ConnectionHasHungCallbackDelegate ConnectionHasHungCallback;
-
- ///
- /// For a client with a pre shared key, this will fire after the communication is established and the key exchange is complete. If you require
- /// a key and subscribe to the socket change event and try to send data on a connection the data sent will interfere with the key exchange and disconnect.
- ///
- public event EventHandler ClientReadyForCommunications;
-
- #endregion
-
- #region Properties & Variables
-
- ///
- /// Address of server
- ///
- public string Hostname { get; set; }
-
- ///
- /// Port on server
- ///
- public int Port { get; set; }
-
- ///
- /// S+ helper
- ///
- public ushort UPort
+ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
{
- get { return Convert.ToUInt16(Port); }
- set { Port = Convert.ToInt32(value); }
- }
+ ///
+ /// Band aid delegate for choked server
+ ///
+ internal delegate void ConnectionHasHungCallbackDelegate();
- ///
- /// Bool to show whether the server requires a preshared key. This is used in the DynamicTCPServer class
- ///
- public bool SharedKeyRequired { get; set; }
+ #region Events
- ///
- /// S+ helper for requires shared key bool
- ///
- public ushort USharedKeyRequired
- {
- set
+ //public event EventHandler BytesReceived;
+
+ ///
+ /// Notifies of text received
+ ///
+ public event EventHandler TextReceived;
+
+ ///
+ /// Notifies of socket status change
+ ///
+ public event EventHandler ConnectionChange;
+
+
+ ///
+ /// This is something of a band-aid callback. If the client times out during the connection process, because the server
+ /// is stuck, this will fire. It is intended to be used by the Server class monitor client, to help
+ /// keep a watch on the server and reset it if necessary.
+ ///
+ internal ConnectionHasHungCallbackDelegate ConnectionHasHungCallback;
+
+ ///
+ /// For a client with a pre shared key, this will fire after the communication is established and the key exchange is complete. If you require
+ /// a key and subscribe to the socket change event and try to send data on a connection the data sent will interfere with the key exchange and disconnect.
+ ///
+ public event EventHandler ClientReadyForCommunications;
+
+ #endregion
+
+ #region Properties & Variables
+
+ ///
+ /// Address of server
+ ///
+ public string Hostname { get; set; }
+
+ ///
+ /// Gets or sets the Port
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// S+ helper
+ ///
+ public ushort UPort
{
- if (value == 1)
- SharedKeyRequired = true;
- else
- SharedKeyRequired = false;
- }
- }
-
- ///
- /// SharedKey is sent for varification to the server. Shared key can be any text (255 char limit in SIMPL+ Module), but must match the Shared Key on the Server module
- ///
- public string SharedKey { get; set; }
-
- ///
- /// flag to show the client is waiting for the server to send the shared key
- ///
- private bool WaitingForSharedKeyResponse { get; set; }
-
- ///
- /// Defaults to 2000
- ///
- public int BufferSize { get; set; }
-
- ///
- /// Semaphore on connect method
- ///
- bool IsTryingToConnect;
-
- ///
- /// Bool showing if socket is connected
- ///
- public bool IsConnected
- {
- get
- {
- if (Client != null)
- return Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED;
- else
- return false;
- }
- }
-
- ///
- /// S+ helper for IsConnected
- ///
- public ushort UIsConnected
- {
- get { return (ushort)(IsConnected ? 1 : 0); }
- }
-
- ///
- /// Bool showing if socket is ready for communication after shared key exchange
- ///
- public bool IsReadyForCommunication { get; set; }
-
- ///
- /// S+ helper for IsReadyForCommunication
- ///
- public ushort UIsReadyForCommunication
- {
- get { return (ushort)(IsReadyForCommunication ? 1 : 0); }
- }
-
- ///
- /// Client socket status Read only
- ///
- public SocketStatus ClientStatus
- {
- get
- {
- if (Client != null)
- return Client.ClientStatus;
- else
- return SocketStatus.SOCKET_STATUS_NO_CONNECT;
- }
- }
-
- ///
- /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
- /// and IsConnected would be true when this == 2.
- ///
- public ushort UStatus
- {
- get { return (ushort)ClientStatus; }
- }
-
- ///
- /// Status text shows the message associated with socket status
- ///
- public string ClientStatusText { get { return ClientStatus.ToString(); } }
-
- ///
- /// bool to track if auto reconnect should be set on the socket
- ///
- public bool AutoReconnect { get; set; }
-
- ///
- /// S+ helper for AutoReconnect
- ///
- public ushort UAutoReconnect
- {
- get { return (ushort)(AutoReconnect ? 1 : 0); }
- set { AutoReconnect = value == 1; }
- }
- ///
- /// Milliseconds to wait before attempting to reconnect. Defaults to 5000
- ///
- public int AutoReconnectIntervalMs { get; set; }
-
- ///
- /// Flag Set only when the disconnect method is called.
- ///
- bool DisconnectCalledByUser;
-
- ///
- /// private Timer for auto reconnect
- ///
- Timer RetryTimer;
-
-
- ///
- ///
- ///
- public bool HeartbeatEnabled { get; set; }
-
- ///
- ///
- ///
- public ushort UHeartbeatEnabled
- {
- get { return (ushort)(HeartbeatEnabled ? 1 : 0); }
- set { HeartbeatEnabled = value == 1; }
- }
-
- ///
- ///
- ///
- public string HeartbeatString = "heartbeat";
-
- ///
- ///
- ///
- public int HeartbeatInterval = 50000;
-
- Timer HeartbeatSendTimer;
- Timer HeartbeatAckTimer;
- ///
- /// Used to force disconnection on a dead connect attempt
- ///
- Timer ConnectFailTimer;
- Timer WaitForSharedKey;
- private int ConnectionCount;
- ///
- /// Internal secure client
- ///
- TCPClient Client;
-
- bool ProgramIsStopping;
-
- #endregion
-
- #region Constructors
-
- ///
- /// Constructor
- ///
- ///
- ///
- ///
- ///
- public GenericTcpIpClient_ForServer(string key, string address, int port, int bufferSize)
- : base(key)
- {
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- Hostname = address;
- Port = port;
- BufferSize = bufferSize;
- AutoReconnectIntervalMs = 5000;
-
- }
-
- ///
- /// Constructor for S+
- ///
- public GenericTcpIpClient_ForServer()
- : base("Uninitialized DynamicTcpClient")
- {
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- AutoReconnectIntervalMs = 5000;
- BufferSize = 2000;
- }
- #endregion
-
- #region Methods
-
- ///
- /// Just to help S+ set the key
- ///
- public void Initialize(string key)
- {
- Key = key;
- }
-
- ///
- /// Handles closing this up when the program shuts down
- ///
- void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
- {
- if (programEventType == eProgramStatusEventType.Stopping || programEventType == eProgramStatusEventType.Paused)
- {
- this.LogInformation("Program stopping. Closing Client connection");
- ProgramIsStopping = true;
- Disconnect();
+ get { return Convert.ToUInt16(Port); }
+ set { Port = Convert.ToInt32(value); }
}
- }
+ ///
+ /// Bool to show whether the server requires a preshared key. This is used in the DynamicTCPServer class
+ ///
+ public bool SharedKeyRequired { get; set; }
- ///
- /// Connect Method. Will return if already connected. Will write errors if missing address, port, or unique key/name.
- ///
- public void Connect()
- {
- ConnectionCount++;
- this.LogDebug("Attempting connect Count:{0}", ConnectionCount);
-
-
- if (IsConnected)
+ ///
+ /// S+ helper for requires shared key bool
+ ///
+ public ushort USharedKeyRequired
{
- this.LogInformation("Already connected. Ignoring.");
- return;
- }
- if (IsTryingToConnect)
- {
- this.LogInformation("Already trying to connect. Ignoring.");
- return;
- }
- try
- {
- IsTryingToConnect = true;
- if (RetryTimer != null)
+ set
{
- RetryTimer.Stop();
- RetryTimer = null;
- }
- if (string.IsNullOrEmpty(Hostname))
- {
- this.LogWarning("DynamicTcpClient: No address set");
- return;
- }
- if (Port < 1 || Port > 65535)
- {
- this.LogWarning("DynamicTcpClient: Invalid port");
- return;
- }
- if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
- {
- this.LogWarning("DynamicTcpClient: No Shared Key set");
- return;
- }
-
- // clean up previous client
- if (Client != null)
- {
- Cleanup();
- }
- DisconnectCalledByUser = false;
-
- Client = new TCPClient(Hostname, Port, BufferSize);
- Client.SocketStatusChange += Client_SocketStatusChange;
- if(HeartbeatEnabled)
- Client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);
- Client.AddressClientConnectedTo = Hostname;
- Client.PortNumber = Port;
- // SecureClient = c;
-
- //var timeOfConnect = DateTime.Now.ToString("HH:mm:ss.fff");
-
- ConnectFailTimer = new Timer(30000) { AutoReset = false };
- ConnectFailTimer.Elapsed += (s, e) =>
- {
- this.LogError("Connect attempt has not finished after 30sec Count:{0}", ConnectionCount);
- if (IsTryingToConnect)
- {
- IsTryingToConnect = false;
-
- //if (ConnectionHasHungCallback != null)
- //{
- // ConnectionHasHungCallback();
- //}
- //SecureClient.DisconnectFromServer();
- //CheckClosedAndTryReconnect();
- }
- };
- ConnectFailTimer.Start();
-
- this.LogDebug("Making Connection Count:{0}", ConnectionCount);
- Client.ConnectToServerAsync(o =>
- {
- this.LogDebug("ConnectToServerAsync Count:{0} Ran!", ConnectionCount);
-
- if (ConnectFailTimer != null)
- {
- ConnectFailTimer.Stop();
- }
- IsTryingToConnect = false;
-
- if (o.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- this.LogVerbose("Client connected to {0} on port {1}", o.AddressClientConnectedTo, o.LocalPortNumberOfClient);
- o.ReceiveDataAsync(Receive);
-
- if (SharedKeyRequired)
- {
- WaitingForSharedKeyResponse = true;
- WaitForSharedKey = new Timer(15000) { AutoReset = false };
- WaitForSharedKey.Elapsed += (s, e) =>
- {
- this.LogWarning("Shared key exchange timer expired. IsReadyForCommunication={0}", IsReadyForCommunication);
- // This is the only case where we should call DisconectFromServer...Event handeler will trigger the cleanup
- o.DisconnectFromServer();
- //CheckClosedAndTryReconnect();
- //OnClientReadyForcommunications(false); // Should send false event
- };
- WaitForSharedKey.Start();
- }
- else
- {
- //CLient connected and shared key is not required so just raise the ready for communication event. if Shared key
- //required this is called by the shared key being negotiated
- if (IsReadyForCommunication == false)
- {
- OnClientReadyForcommunications(true); // Key not required
- }
- }
- }
+ if (value == 1)
+ SharedKeyRequired = true;
else
- {
- this.LogWarning("Connect attempt failed {0}", o.ClientStatus);
- CheckClosedAndTryReconnect();
- }
- });
- }
- catch (Exception ex)
- {
- this.LogException(ex, "Client connection exception: {0}", ex.Message);
- this.LogVerbose("Stack Trace: {0}", ex.StackTrace);
- IsTryingToConnect = false;
- CheckClosedAndTryReconnect();
- }
- }
-
- ///
- ///
- ///
- public void Disconnect()
- {
- this.LogVerbose("Disconnect Called");
-
- DisconnectCalledByUser = true;
- if (IsConnected)
- {
- Client.DisconnectFromServer();
-
- }
- if (RetryTimer != null)
- {
- RetryTimer.Stop();
- RetryTimer = null;
- }
- Cleanup();
- }
-
- ///
- /// Internal call to close up client. ALWAYS use this when disconnecting.
- ///
- void Cleanup()
- {
- IsTryingToConnect = false;
-
- if (Client != null)
- {
- //SecureClient.DisconnectFromServer();
- this.LogVerbose("Disconnecting Client {0}", DisconnectCalledByUser ? ", Called by user" : "");
- Client.SocketStatusChange -= Client_SocketStatusChange;
- Client.Dispose();
- Client = null;
- }
- if (ConnectFailTimer != null)
- {
- ConnectFailTimer.Stop();
- ConnectFailTimer.Dispose();
- ConnectFailTimer = null;
- }
- }
-
-
- /// ff
- /// Called from Connect failure or Socket Status change if
- /// auto reconnect and socket disconnected (Not disconnected by user)
- ///
- void CheckClosedAndTryReconnect()
- {
- if (Client != null)
- {
- this.LogVerbose("Cleaning up remotely closed/failed connection.");
- Cleanup();
- }
- if (!DisconnectCalledByUser && AutoReconnect)
- {
- var halfInterval = AutoReconnectIntervalMs / 2;
- var rndTime = new Random().Next(-halfInterval, halfInterval) + AutoReconnectIntervalMs;
- this.LogVerbose("Attempting reconnect in {0} ms, randomized", rndTime);
- if (RetryTimer != null)
- {
- RetryTimer.Stop();
- RetryTimer = null;
- }
- RetryTimer = new Timer(rndTime) { AutoReset = false };
- RetryTimer.Elapsed += (s, e) => Connect();
- RetryTimer.Start();
- }
- }
-
- ///
- /// Receive callback
- ///
- ///
- ///
- void Receive(TCPClient client, int numBytes)
- {
- if (numBytes > 0)
- {
- string str = string.Empty;
-
- try
- {
- var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
- str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
- this.LogVerbose("Client Received:\r--------\r{0}\r--------", str);
- if (!string.IsNullOrEmpty(checkHeartbeat(str)))
- {
- if (SharedKeyRequired && str == "SharedKey:")
- {
- this.LogVerbose("Server asking for shared key, sending");
- SendText(SharedKey + "\n");
- }
- else if (SharedKeyRequired && str == "Shared Key Match")
- {
- StopWaitForSharedKeyTimer();
- this.LogVerbose("Shared key confirmed. Ready for communication");
- OnClientReadyForcommunications(true); // Successful key exchange
- }
- else
- {
- //var bytesHandler = BytesReceived;
- //if (bytesHandler != null)
- // bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
- var textHandler = TextReceived;
- if (textHandler != null)
- textHandler(this, new GenericTcpServerCommMethodReceiveTextArgs(str));
- }
- }
- }
- catch (Exception ex)
- {
- this.LogException(ex, "Error receiving data: {1}. Error: {0}", ex.Message, str);
- this.LogVerbose("Stack Trace: {0}", ex.StackTrace);
- }
- }
- if (client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
- client.ReceiveDataAsync(Receive);
- }
-
- void HeartbeatStart()
- {
- if (HeartbeatEnabled)
- {
- this.LogVerbose("Starting Heartbeat");
- if (HeartbeatSendTimer == null)
- {
-
- HeartbeatSendTimer = new Timer(HeartbeatInterval) { AutoReset = true };
- HeartbeatSendTimer.Elapsed += (s, e) => SendHeartbeat(null);
- HeartbeatSendTimer.Start();
- }
- if (HeartbeatAckTimer == null)
- {
- HeartbeatAckTimer = new Timer(HeartbeatInterval * 2) { AutoReset = true };
- HeartbeatAckTimer.Elapsed += (s, e) => HeartbeatAckTimerFail(null);
- HeartbeatAckTimer.Start();
+ SharedKeyRequired = false;
}
}
- }
- void HeartbeatStop()
- {
+ ///
+ /// Gets or sets the SharedKey
+ ///
+ public string SharedKey { get; set; }
- if (HeartbeatSendTimer != null)
+ ///
+ /// flag to show the client is waiting for the server to send the shared key
+ ///
+ private bool WaitingForSharedKeyResponse { get; set; }
+
+ ///
+ /// Gets or sets the BufferSize
+ ///
+ public int BufferSize { get; set; }
+
+ ///
+ /// Semaphore on connect method
+ ///
+ bool IsTryingToConnect;
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsConnected
{
- this.LogVerbose("Stoping Heartbeat Send");
- HeartbeatSendTimer.Stop();
- HeartbeatSendTimer = null;
- }
- if (HeartbeatAckTimer != null)
- {
- this.LogVerbose("Stoping Heartbeat Ack");
- HeartbeatAckTimer.Stop();
- HeartbeatAckTimer = null;
- }
-
- }
- void SendHeartbeat(object notused)
- {
- this.SendText(HeartbeatString);
- this.LogVerbose("Sending Heartbeat");
-
- }
-
- //private method to check heartbeat requirements and start or reset timer
- string checkHeartbeat(string received)
- {
- try
- {
- if (HeartbeatEnabled)
+ get
{
- if (!string.IsNullOrEmpty(HeartbeatString))
- {
- var remainingText = received.Replace(HeartbeatString, "");
- var noDelimiter = received.Trim(new char[] { '\r', '\n' });
- if (noDelimiter.Contains(HeartbeatString))
- {
- if (HeartbeatAckTimer != null)
- {
- HeartbeatAckTimer.Stop();
- HeartbeatAckTimer.Interval = HeartbeatInterval * 2;
- HeartbeatAckTimer.Start();
- }
- else
- {
- HeartbeatAckTimer = new Timer(HeartbeatInterval * 2) { AutoReset = true };
- HeartbeatAckTimer.Elapsed += (s, e) => HeartbeatAckTimerFail(null);
- HeartbeatAckTimer.Start();
- }
- this.LogVerbose("Heartbeat Received: {0}, from Server", HeartbeatString);
- return remainingText;
- }
- }
+ if (Client != null)
+ return Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED;
+ else
+ return false;
}
}
- catch (Exception ex)
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsConnected
{
- this.LogException(ex, "Error checking heartbeat: {0}", ex.Message);
- this.LogVerbose("Stack Trace: {0}", ex.StackTrace);
+ get { return (ushort)(IsConnected ? 1 : 0); }
}
- return received;
- }
+ ///
+ /// Bool showing if socket is ready for communication after shared key exchange
+ ///
+ public bool IsReadyForCommunication { get; set; }
-
- void HeartbeatAckTimerFail(object o)
- {
- try
+ ///
+ /// S+ helper for IsReadyForCommunication
+ ///
+ public ushort UIsReadyForCommunication
{
+ get { return (ushort)(IsReadyForCommunication ? 1 : 0); }
+ }
+
+ ///
+ /// Client socket status Read only
+ ///
+ public SocketStatus ClientStatus
+ {
+ get
+ {
+ if (Client != null)
+ return Client.ClientStatus;
+ else
+ return SocketStatus.SOCKET_STATUS_NO_CONNECT;
+ }
+ }
+
+ ///
+ /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
+ /// and IsConnected would be true when this == 2.
+ ///
+ public ushort UStatus
+ {
+ get { return (ushort)ClientStatus; }
+ }
+
+ ///
+ /// Status text shows the message associated with socket status
+ ///
+ public string ClientStatusText { get { return ClientStatus.ToString(); } }
+
+ ///
+ /// bool to track if auto reconnect should be set on the socket
+ ///
+ public bool AutoReconnect { get; set; }
+
+ ///
+ /// S+ helper for AutoReconnect
+ ///
+ public ushort UAutoReconnect
+ {
+ get { return (ushort)(AutoReconnect ? 1 : 0); }
+ set { AutoReconnect = value == 1; }
+ }
+ ///
+ /// Milliseconds to wait before attempting to reconnect. Defaults to 5000
+ ///
+ public int AutoReconnectIntervalMs { get; set; }
+
+ ///
+ /// Flag Set only when the disconnect method is called.
+ ///
+ bool DisconnectCalledByUser;
+
+ ///
+ /// private Timer for auto reconnect
+ ///
+ CTimer RetryTimer;
+
+
+ ///
+ ///
+ ///
+ public bool HeartbeatEnabled { get; set; }
+
+ ///
+ ///
+ ///
+ public ushort UHeartbeatEnabled
+ {
+ get { return (ushort)(HeartbeatEnabled ? 1 : 0); }
+ set { HeartbeatEnabled = value == 1; }
+ }
+
+ ///
+ ///
+ ///
+ public string HeartbeatString = "heartbeat";
+
+ ///
+ ///
+ ///
+ public int HeartbeatInterval = 50000;
+
+ CTimer HeartbeatSendTimer;
+ CTimer HeartbeatAckTimer;
+ ///
+ /// Used to force disconnection on a dead connect attempt
+ ///
+ CTimer ConnectFailTimer;
+ CTimer WaitForSharedKey;
+ private int ConnectionCount;
+ ///
+ /// Internal secure client
+ ///
+ TCPClient Client;
+
+ bool ProgramIsStopping;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ ///
+ ///
+ public GenericTcpIpClient_ForServer(string key, string address, int port, int bufferSize)
+ : base(key)
+ {
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ Hostname = address;
+ Port = port;
+ BufferSize = bufferSize;
+ AutoReconnectIntervalMs = 5000;
+
+ }
+
+ ///
+ /// Constructor for S+
+ ///
+ public GenericTcpIpClient_ForServer()
+ : base("Uninitialized DynamicTcpClient")
+ {
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ AutoReconnectIntervalMs = 5000;
+ BufferSize = 2000;
+ }
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Initialize method
+ ///
+ public void Initialize(string key)
+ {
+ Key = key;
+ }
+
+ ///
+ /// Handles closing this up when the program shuts down
+ ///
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping || programEventType == eProgramStatusEventType.Paused)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing Client connection");
+ ProgramIsStopping = true;
+ Disconnect();
+ }
+
+ }
+
+ ///
+ /// Connect method
+ ///
+ public void Connect()
+ {
+ ConnectionCount++;
+ Debug.Console(2, this, "Attempting connect Count:{0}", ConnectionCount);
+
if (IsConnected)
{
- this.LogWarning("Heartbeat not received from Server...DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE");
- SendText("Heartbeat not received by server, closing connection");
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Already connected. Ignoring.");
+ return;
+ }
+ if (IsTryingToConnect)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Already trying to connect. Ignoring.");
+ return;
+ }
+ try
+ {
+ IsTryingToConnect = true;
+ if (RetryTimer != null)
+ {
+ RetryTimer.Stop();
+ RetryTimer = null;
+ }
+ if (string.IsNullOrEmpty(Hostname))
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: No address set");
+ return;
+ }
+ if (Port < 1 || Port > 65535)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: Invalid port");
+ return;
+ }
+ if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: No Shared Key set");
+ return;
+ }
+
+ // clean up previous client
+ if (Client != null)
+ {
+ Cleanup();
+ }
+ DisconnectCalledByUser = false;
+
+ Client = new TCPClient(Hostname, Port, BufferSize);
+ Client.SocketStatusChange += Client_SocketStatusChange;
+ if(HeartbeatEnabled)
+ Client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);
+ Client.AddressClientConnectedTo = Hostname;
+ Client.PortNumber = Port;
+ // SecureClient = c;
+
+ //var timeOfConnect = DateTime.Now.ToString("HH:mm:ss.fff");
+
+ ConnectFailTimer = new CTimer(o =>
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Connect attempt has not finished after 30sec Count:{0}", ConnectionCount);
+ if (IsTryingToConnect)
+ {
+ IsTryingToConnect = false;
+
+ //if (ConnectionHasHungCallback != null)
+ //{
+ // ConnectionHasHungCallback();
+ //}
+ //SecureClient.DisconnectFromServer();
+ //CheckClosedAndTryReconnect();
+ }
+ }, 30000);
+
+ Debug.Console(2, this, "Making Connection Count:{0}", ConnectionCount);
+ Client.ConnectToServerAsync(o =>
+ {
+ Debug.Console(2, this, "ConnectToServerAsync Count:{0} Ran!", ConnectionCount);
+
+ if (ConnectFailTimer != null)
+ {
+ ConnectFailTimer.Stop();
+ }
+ IsTryingToConnect = false;
+
+ if (o.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ Debug.Console(2, this, "Client connected to {0} on port {1}", o.AddressClientConnectedTo, o.LocalPortNumberOfClient);
+ o.ReceiveDataAsync(Receive);
+
+ if (SharedKeyRequired)
+ {
+ WaitingForSharedKeyResponse = true;
+ WaitForSharedKey = new CTimer(timer =>
+ {
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Shared key exchange timer expired. IsReadyForCommunication={0}", IsReadyForCommunication);
+ // Debug.Console(1, this, "Connect attempt failed {0}", c.ClientStatus);
+ // This is the only case where we should call DisconectFromServer...Event handeler will trigger the cleanup
+ o.DisconnectFromServer();
+ //CheckClosedAndTryReconnect();
+ //OnClientReadyForcommunications(false); // Should send false event
+ }, 15000);
+ }
+ else
+ {
+ //CLient connected and shared key is not required so just raise the ready for communication event. if Shared key
+ //required this is called by the shared key being negotiated
+ if (IsReadyForCommunication == false)
+ {
+ OnClientReadyForcommunications(true); // Key not required
+ }
+ }
+ }
+ else
+ {
+ Debug.Console(1, this, "Connect attempt failed {0}", o.ClientStatus);
+ CheckClosedAndTryReconnect();
+ }
+ });
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Client connection exception: {0}", ex.Message);
+ IsTryingToConnect = false;
CheckClosedAndTryReconnect();
}
+ }
+
+ ///
+ /// Disconnect method
+ ///
+ public void Disconnect()
+ {
+ this.LogVerbose("Disconnect Called");
+
+ DisconnectCalledByUser = true;
+ if (IsConnected)
+ {
+ Client.DisconnectFromServer();
+
+ }
+ if (RetryTimer != null)
+ {
+ RetryTimer.Stop();
+ RetryTimer = null;
+ }
+ Cleanup();
+ }
+
+ ///
+ /// Internal call to close up client. ALWAYS use this when disconnecting.
+ ///
+ void Cleanup()
+ {
+ IsTryingToConnect = false;
+
+ if (Client != null)
+ {
+ //SecureClient.DisconnectFromServer();
+ Debug.Console(2, this, "Disconnecting Client {0}", DisconnectCalledByUser ? ", Called by user" : "");
+ Client.SocketStatusChange -= Client_SocketStatusChange;
+ Client.Dispose();
+ Client = null;
+ }
+ if (ConnectFailTimer != null)
+ {
+ ConnectFailTimer.Stop();
+ ConnectFailTimer.Dispose();
+ ConnectFailTimer = null;
+ }
+ }
+
+
+ /// ff
+ /// Called from Connect failure or Socket Status change if
+ /// auto reconnect and socket disconnected (Not disconnected by user)
+ ///
+ void CheckClosedAndTryReconnect()
+ {
+ if (Client != null)
+ {
+ Debug.Console(2, this, "Cleaning up remotely closed/failed connection.");
+ Cleanup();
+ }
+ if (!DisconnectCalledByUser && AutoReconnect)
+ {
+ var halfInterval = AutoReconnectIntervalMs / 2;
+ var rndTime = new Random().Next(-halfInterval, halfInterval) + AutoReconnectIntervalMs;
+ Debug.Console(2, this, "Attempting reconnect in {0} ms, randomized", rndTime);
+ if (RetryTimer != null)
+ {
+ RetryTimer.Stop();
+ RetryTimer = null;
+ }
+ RetryTimer = new CTimer(o => Connect(), rndTime);
+ }
+ }
+
+ ///
+ /// Receive callback
+ ///
+ ///
+ ///
+ void Receive(TCPClient client, int numBytes)
+ {
+ if (numBytes > 0)
+ {
+ string str = string.Empty;
+
+ try
+ {
+ var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
+ str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
+ Debug.Console(2, this, "Client Received:\r--------\r{0}\r--------", str);
+ if (!string.IsNullOrEmpty(checkHeartbeat(str)))
+ {
+ if (SharedKeyRequired && str == "SharedKey:")
+ {
+ Debug.Console(2, this, "Server asking for shared key, sending");
+ SendText(SharedKey + "\n");
+ }
+ else if (SharedKeyRequired && str == "Shared Key Match")
+ {
+ StopWaitForSharedKeyTimer();
+ Debug.Console(2, this, "Shared key confirmed. Ready for communication");
+ OnClientReadyForcommunications(true); // Successful key exchange
+ }
+ else
+ {
+ //var bytesHandler = BytesReceived;
+ //if (bytesHandler != null)
+ // bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
+ var textHandler = TextReceived;
+ if (textHandler != null)
+ textHandler(this, new GenericTcpServerCommMethodReceiveTextArgs(str));
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(1, this, "Error receiving data: {1}. Error: {0}", ex.Message, str);
+ }
+ }
+ if (client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ client.ReceiveDataAsync(Receive);
+ }
+
+ void HeartbeatStart()
+ {
+ if (HeartbeatEnabled)
+ {
+ Debug.Console(2, this, "Starting Heartbeat");
+ if (HeartbeatSendTimer == null)
+ {
+
+ HeartbeatSendTimer = new CTimer(this.SendHeartbeat, null, HeartbeatInterval, HeartbeatInterval);
+ }
+ if (HeartbeatAckTimer == null)
+ {
+ HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
+ }
+ }
}
- catch (Exception ex)
+ void HeartbeatStop()
{
- this.LogException(ex, "Heartbeat timeout Error on Client: {0}, {1}", Key, ex.Message);
- this.LogVerbose("Stack Trace: {0}", ex.StackTrace);
- }
- }
- ///
- ///
- ///
- void StopWaitForSharedKeyTimer()
- {
- if (WaitForSharedKey != null)
+ if (HeartbeatSendTimer != null)
+ {
+ Debug.Console(2, this, "Stoping Heartbeat Send");
+ HeartbeatSendTimer.Stop();
+ HeartbeatSendTimer = null;
+ }
+ if (HeartbeatAckTimer != null)
+ {
+ Debug.Console(2, this, "Stoping Heartbeat Ack");
+ HeartbeatAckTimer.Stop();
+ HeartbeatAckTimer = null;
+ }
+
+ }
+ void SendHeartbeat(object notused)
{
- WaitForSharedKey.Stop();
- WaitForSharedKey = null;
- }
- }
+ this.SendText(HeartbeatString);
+ Debug.Console(2, this, "Sending Heartbeat");
- ///
- /// General send method
- ///
- public void SendText(string text)
- {
- if (!string.IsNullOrEmpty(text))
+ }
+
+ //private method to check heartbeat requirements and start or reset timer
+ string checkHeartbeat(string received)
{
try
{
- var bytes = Encoding.GetEncoding(28591).GetBytes(text);
- if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ if (HeartbeatEnabled)
{
- Client.SendDataAsync(bytes, bytes.Length, (c, n) =>
+ if (!string.IsNullOrEmpty(HeartbeatString))
{
- // HOW IN THE HELL DO WE CATCH AN EXCEPTION IN SENDING?????
- if (n <= 0)
+ var remainingText = received.Replace(HeartbeatString, "");
+ var noDelimiter = received.Trim(new char[] { '\r', '\n' });
+ if (noDelimiter.Contains(HeartbeatString))
{
- this.LogWarning("[{0}] Sent zero bytes. Was there an error?", this.Key);
+ if (HeartbeatAckTimer != null)
+ {
+ HeartbeatAckTimer.Reset(HeartbeatInterval * 2);
+ }
+ else
+ {
+ HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
+ }
+ Debug.Console(2, this, "Heartbeat Received: {0}, from Server", HeartbeatString);
+ return remainingText;
}
- });
+ }
}
}
catch (Exception ex)
{
- this.LogException(ex, "Error sending text: {1}. Error: {0}", ex.Message, text);
- this.LogVerbose("Stack Trace: {0}", ex.StackTrace);
+ Debug.Console(1, this, "Error checking heartbeat: {0}", ex.Message);
}
+ return received;
}
- }
- ///
- ///
- ///
- public void SendBytes(byte[] bytes)
- {
- if (bytes.Length > 0)
+
+
+ void HeartbeatAckTimerFail(object o)
{
try
{
- if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
- Client.SendData(bytes, bytes.Length);
+
+ if (IsConnected)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "Heartbeat not received from Server...DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE");
+ SendText("Heartbeat not received by server, closing connection");
+ CheckClosedAndTryReconnect();
+ }
+
}
catch (Exception ex)
{
- this.LogException(ex, "Error sending bytes. Error: {0}", ex.Message);
- this.LogVerbose("Stack Trace: {0}", ex.StackTrace);
+ ErrorLog.Error("Heartbeat timeout Error on Client: {0}, {1}", Key, ex);
}
}
- }
- ///
- /// SocketStatusChange Callback
- ///
- ///
- ///
- void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)
- {
- if (ProgramIsStopping)
+ ///
+ ///
+ ///
+ void StopWaitForSharedKeyTimer()
{
- ProgramIsStopping = false;
- return;
- }
- try
- {
- this.LogVerbose("Socket status change: {0} ({1})", client.ClientStatus, (ushort)(client.ClientStatus));
-
- OnConnectionChange();
-
- // The client could be null or disposed by this time...
- if (Client == null || Client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ if (WaitForSharedKey != null)
{
- HeartbeatStop();
- OnClientReadyForcommunications(false); // socket has gone low
- CheckClosedAndTryReconnect();
+ WaitForSharedKey.Stop();
+ WaitForSharedKey = null;
}
}
- catch (Exception ex)
+
+ ///
+ /// SendText method
+ ///
+ public void SendText(string text)
{
- this.LogException(ex, "Error in socket status change callback. Error: {0}", ex.Message);
- this.LogVerbose("Stack Trace: {0}", ex.StackTrace);
+ if (!string.IsNullOrEmpty(text))
+ {
+ try
+ {
+ var bytes = Encoding.GetEncoding(28591).GetBytes(text);
+ if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ Client.SendDataAsync(bytes, bytes.Length, (c, n) =>
+ {
+ // HOW IN THE HELL DO WE CATCH AN EXCEPTION IN SENDING?????
+ if (n <= 0)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "[{0}] Sent zero bytes. Was there an error?", this.Key);
+ }
+ });
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, "Error sending text: {1}. Error: {0}", ex.Message, text);
+ }
+ }
}
- }
- ///
- /// Helper for ConnectionChange event
- ///
- void OnConnectionChange()
- {
- var handler = ConnectionChange;
- if (handler != null)
- ConnectionChange(this, new GenericTcpServerSocketStatusChangeEventArgs(this, Client.ClientStatus));
- }
+ ///
+ /// SendBytes method
+ ///
+ public void SendBytes(byte[] bytes)
+ {
+ if (bytes.Length > 0)
+ {
+ try
+ {
+ if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ Client.SendData(bytes, bytes.Length);
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, "Error sending bytes. Error: {0}", ex.Message);
+ }
+ }
+ }
- ///
- /// Helper to fire ClientReadyForCommunications event
- ///
- void OnClientReadyForcommunications(bool isReady)
- {
- IsReadyForCommunication = isReady;
- if (this.IsReadyForCommunication) { HeartbeatStart(); }
- var handler = ClientReadyForCommunications;
- if (handler != null)
- handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));
+ ///
+ /// SocketStatusChange Callback
+ ///
+ ///
+ ///
+ void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)
+ {
+ if (ProgramIsStopping)
+ {
+ ProgramIsStopping = false;
+ return;
+ }
+ try
+ {
+ Debug.Console(2, this, "Socket status change: {0} ({1})", client.ClientStatus, (ushort)(client.ClientStatus));
+
+ OnConnectionChange();
+
+ // The client could be null or disposed by this time...
+ if (Client == null || Client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ HeartbeatStop();
+ OnClientReadyForcommunications(false); // socket has gone low
+ CheckClosedAndTryReconnect();
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Error in socket status change callback. Error: {0}\r\r{1}", ex, ex.InnerException);
+ }
+ }
+
+ ///
+ /// Helper for ConnectionChange event
+ ///
+ void OnConnectionChange()
+ {
+ var handler = ConnectionChange;
+ if (handler != null)
+ ConnectionChange(this, new GenericTcpServerSocketStatusChangeEventArgs(this, Client.ClientStatus));
+ }
+
+ ///
+ /// Helper to fire ClientReadyForCommunications event
+ ///
+ void OnClientReadyForcommunications(bool isReady)
+ {
+ IsReadyForCommunication = isReady;
+ if (this.IsReadyForCommunication) { HeartbeatStart(); }
+ var handler = ClientReadyForCommunications;
+ if (handler != null)
+ handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));
+ }
+ #endregion
}
- #endregion
-}
+
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Comm/GenericTcpIpServer.cs b/src/PepperDash.Core/Comm/GenericTcpIpServer.cs
index 3d5d5a0c..5cec96df 100644
--- a/src/PepperDash.Core/Comm/GenericTcpIpServer.cs
+++ b/src/PepperDash.Core/Comm/GenericTcpIpServer.cs
@@ -13,798 +13,795 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
-using System.Timers;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronSockets;
using PepperDash.Core.Logging;
-namespace PepperDash.Core;
-
-///
-/// Generic TCP/IP server device
-///
-public class GenericTcpIpServer : Device
+namespace PepperDash.Core
{
- #region Events
///
- /// Event for Receiving text
+ /// Generic TCP/IP server device
///
- public event EventHandler TextReceived;
-
- ///
- /// Event for client connection socket status change
- ///
- public event EventHandler ClientConnectionChange;
-
- ///
- /// Event for Server State Change
- ///
- public event EventHandler ServerStateChange;
-
- ///
- /// For a server with a pre shared key, this will fire after the communication is established and the key exchange is complete. If no shared key, this will fire
- /// after connection is successful. Use this event to know when the client is ready for communication to avoid stepping on shared key.
- ///
- public event EventHandler ServerClientReadyForCommunications;
-
- ///
- /// A band aid event to notify user that the server has choked.
- ///
- public ServerHasChokedCallbackDelegate ServerHasChoked { get; set; }
-
- ///
- ///
- ///
- public delegate void ServerHasChokedCallbackDelegate();
-
- #endregion
-
- #region Properties/Variables
-
- ///
- /// Server listen lock
- ///
- object _serverLock = new();
-
- ///
- /// Broadcast lock
- ///
- private readonly object _broadcastLock = new();
-
-
- ///
- /// A bandaid client that monitors whether the server is reachable
- ///
- GenericTcpIpClient_ForServer MonitorClient;
-
- ///
- /// Timer to operate the bandaid monitor client in a loop.
- ///
- Timer MonitorClientTimer;
-
- ///
- ///
- ///
- int MonitorClientFailureCount;
-
- ///
- /// 3 by default
- ///
- public int MonitorClientMaxFailureCount { get; set; }
-
- ///
- /// Text representation of the Socket Status enum values for the server
- ///
- public string Status
+ public class GenericTcpIpServer : Device
{
- get
+ #region Events
+ ///
+ /// Event for Receiving text
+ ///
+ public event EventHandler TextReceived;
+
+ ///
+ /// Event for client connection socket status change
+ ///
+ public event EventHandler ClientConnectionChange;
+
+ ///
+ /// Event for Server State Change
+ ///
+ public event EventHandler ServerStateChange;
+
+ ///
+ /// For a server with a pre shared key, this will fire after the communication is established and the key exchange is complete. If no shared key, this will fire
+ /// after connection is successful. Use this event to know when the client is ready for communication to avoid stepping on shared key.
+ ///
+ public event EventHandler ServerClientReadyForCommunications;
+
+ ///
+ /// A band aid event to notify user that the server has choked.
+ ///
+ public ServerHasChokedCallbackDelegate ServerHasChoked { get; set; }
+
+ ///
+ /// Delegate for ServerHasChokedCallbackDelegate
+ ///
+ public delegate void ServerHasChokedCallbackDelegate();
+
+ #endregion
+
+ #region Properties/Variables
+
+ ///
+ ///
+ ///
+ CCriticalSection ServerCCSection = new CCriticalSection();
+
+
+ ///
+ /// A bandaid client that monitors whether the server is reachable
+ ///
+ GenericTcpIpClient_ForServer MonitorClient;
+
+ ///
+ /// Timer to operate the bandaid monitor client in a loop.
+ ///
+ CTimer MonitorClientTimer;
+
+ ///
+ ///
+ ///
+ int MonitorClientFailureCount;
+
+ ///
+ /// Gets or sets the MonitorClientMaxFailureCount
+ ///
+ public int MonitorClientMaxFailureCount { get; set; }
+
+ ///
+ /// Text representation of the Socket Status enum values for the server
+ ///
+ public string Status
{
- if (myTcpServer != null)
- return myTcpServer.State.ToString();
- return ServerState.SERVER_NOT_LISTENING.ToString();
+ get
+ {
+ if (myTcpServer != null)
+ return myTcpServer.State.ToString();
+ return ServerState.SERVER_NOT_LISTENING.ToString();
+
+ }
}
- }
-
- ///
- /// Bool showing if socket is connected
- ///
- public bool IsConnected
- {
- get
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsConnected
{
- if (myTcpServer != null)
- return (myTcpServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED;
- return false;
-
- //return (Secure ? SecureServer != null : UnsecureServer != null) &&
- //(Secure ? (SecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED :
- // (UnsecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED);
- }
- }
-
- ///
- /// S+ helper for IsConnected
- ///
- public ushort UIsConnected
- {
- get { return (ushort)(IsConnected ? 1 : 0); }
- }
-
- ///
- /// Bool showing if socket is connected
- ///
- public bool IsListening
- {
- get
- {
- if (myTcpServer != null)
- return (myTcpServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING;
- else
+ get
+ {
+ if (myTcpServer != null)
+ return (myTcpServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED;
return false;
- //return (Secure ? SecureServer != null : UnsecureServer != null) &&
- //(Secure ? (SecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING :
- // (UnsecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING);
- }
- }
- ///
- /// S+ helper for IsConnected
- ///
- public ushort UIsListening
- {
- get { return (ushort)(IsListening ? 1 : 0); }
- }
-
- ///
- /// The maximum number of clients.
- /// Should be set by parameter in SIMPL+ in the MAIN method, Should not ever need to be configurable
- ///
- public ushort MaxClients { get; set; }
-
- ///
- /// Number of clients currently connected.
- ///
- public ushort NumberOfClientsConnected
- {
- get
- {
- if (myTcpServer != null)
- return (ushort)myTcpServer.NumberOfClientsConnected;
- return 0;
- }
- }
-
- ///
- /// Port Server should listen on
- ///
- public int Port { get; set; }
-
- ///
- /// S+ helper for Port
- ///
- public ushort UPort
- {
- get { return Convert.ToUInt16(Port); }
- set { Port = Convert.ToInt32(value); }
- }
-
- ///
- /// Bool to show whether the server requires a preshared key. Must be set the same in the client, and if true shared keys must be identical on server/client
- ///
- public bool SharedKeyRequired { get; set; }
-
- ///
- /// S+ helper for requires shared key bool
- ///
- public ushort USharedKeyRequired
- {
- set
- {
- if (value == 1)
- SharedKeyRequired = true;
- else
- SharedKeyRequired = false;
- }
- }
-
- ///
- /// SharedKey is sent for varification to the server. Shared key can be any text (255 char limit in SIMPL+ Module), but must match the Shared Key on the Server module.
- /// If SharedKey changes while server is listening or clients are connected, disconnect and stop listening will be called
- ///
- public string SharedKey { get; set; }
-
- ///
- /// Heartbeat Required bool sets whether server disconnects client if heartbeat is not received
- ///
- public bool HeartbeatRequired { get; set; }
-
- ///
- /// S+ Helper for Heartbeat Required
- ///
- public ushort UHeartbeatRequired
- {
- set
- {
- if (value == 1)
- HeartbeatRequired = true;
- else
- HeartbeatRequired = false;
- }
- }
-
- ///
- /// Milliseconds before server expects another heartbeat. Set by property HeartbeatRequiredIntervalInSeconds which is driven from S+
- ///
- public int HeartbeatRequiredIntervalMs { get; set; }
-
- ///
- /// Simpl+ Heartbeat Analog value in seconds
- ///
- public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatRequiredIntervalMs = (value * 1000); } }
-
- ///
- /// String to Match for heartbeat. If null or empty any string will reset heartbeat timer
- ///
- public string HeartbeatStringToMatch { get; set; }
-
- //private timers for Heartbeats per client
- Dictionary HeartbeatTimerDictionary = new Dictionary();
-
- //flags to show the secure server is waiting for client at index to send the shared key
- List WaitingForSharedKey = new List();
-
- List ClientReadyAfterKeyExchange = new List();
-
- ///
- /// The connected client indexes
- ///
- public List ConnectedClientsIndexes = new List();
-
- ///
- /// Defaults to 2000
- ///
- public int BufferSize { get; set; }
-
- ///
- /// Private flag to note that the server has stopped intentionally
- ///
- private bool ServerStopped { get; set; }
-
- //Servers
- TCPServer myTcpServer;
-
- ///
- ///
- ///
- bool ProgramIsStopping;
-
- #endregion
-
- #region Constructors
- ///
- /// constructor S+ Does not accept a key. Use initialze with key to set the debug key on this device. If using with + make sure to set all properties manually.
- ///
- public GenericTcpIpServer()
- : base("Uninitialized Dynamic TCP Server")
- {
- HeartbeatRequiredIntervalInSeconds = 15;
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- BufferSize = 2000;
- MonitorClientMaxFailureCount = 3;
- }
-
- ///
- /// constructor with debug key set at instantiation. Make sure to set all properties before listening.
- ///
- ///
- public GenericTcpIpServer(string key)
- : base("Uninitialized Dynamic TCP Server")
- {
- HeartbeatRequiredIntervalInSeconds = 15;
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- BufferSize = 2000;
- MonitorClientMaxFailureCount = 3;
- Key = key;
- }
-
- ///
- /// Contstructor that sets all properties by calling the initialize method with a config object.
- ///
- ///
- public GenericTcpIpServer(TcpServerConfigObject serverConfigObject)
- : base("Uninitialized Dynamic TCP Server")
- {
- HeartbeatRequiredIntervalInSeconds = 15;
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- BufferSize = 2000;
- MonitorClientMaxFailureCount = 3;
- Initialize(serverConfigObject);
- }
- #endregion
-
- #region Methods - Server Actions
- ///
- /// Disconnects all clients and stops the server
- ///
- public void KillServer()
- {
- ServerStopped = true;
- if (MonitorClient != null)
- {
- MonitorClient.Disconnect();
- }
- DisconnectAllClientsForShutdown();
- StopListening();
- }
-
- ///
- /// Initialize Key for device using client name from SIMPL+. Called on Listen from SIMPL+
- ///
- ///
- public void Initialize(string key)
- {
- Key = key;
- }
-
- ///
- /// Initialze with server configuration object
- ///
- ///
- public void Initialize(TcpServerConfigObject serverConfigObject)
- {
- try
- {
- if (serverConfigObject != null || string.IsNullOrEmpty(serverConfigObject.Key))
- {
- Key = serverConfigObject.Key;
- MaxClients = serverConfigObject.MaxClients;
- Port = serverConfigObject.Port;
- SharedKeyRequired = serverConfigObject.SharedKeyRequired;
- SharedKey = serverConfigObject.SharedKey;
- HeartbeatRequired = serverConfigObject.HeartbeatRequired;
- HeartbeatRequiredIntervalInSeconds = serverConfigObject.HeartbeatRequiredIntervalInSeconds;
- HeartbeatStringToMatch = serverConfigObject.HeartbeatStringToMatch;
- BufferSize = serverConfigObject.BufferSize;
-
- }
- else
- {
- this.LogError("Could not initialize server with key: {0}", serverConfigObject.Key);
+ //return (Secure ? SecureServer != null : UnsecureServer != null) &&
+ //(Secure ? (SecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED :
+ // (UnsecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED);
}
}
- catch
- {
- this.LogError("Could not initialize server with key: {0}", serverConfigObject.Key);
- }
- }
- ///
- /// Start listening on the specified port
- ///
- public void Listen()
- {
- lock (_serverLock)
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsConnected
{
- try
+ get { return (ushort)(IsConnected ? 1 : 0); }
+ }
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsListening
{
- if (Port < 1 || Port > 65535)
+ get
{
- this.LogError("Server '{0}': Invalid port", Key);
- return;
+ if (myTcpServer != null)
+ return (myTcpServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING;
+ else
+ return false;
+ //return (Secure ? SecureServer != null : UnsecureServer != null) &&
+ //(Secure ? (SecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING :
+ // (UnsecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING);
}
- if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsListening
+ {
+ get { return (ushort)(IsListening ? 1 : 0); }
+ }
+
+ ///
+ /// The maximum number of clients.
+ /// Should be set by parameter in SIMPL+ in the MAIN method, Should not ever need to be configurable
+ ///
+ public ushort MaxClients { get; set; }
+
+ ///
+ /// Number of clients currently connected.
+ ///
+ public ushort NumberOfClientsConnected
+ {
+ get
{
- this.LogError("Server '{0}': No Shared Key set", Key);
- return;
+ if (myTcpServer != null)
+ return (ushort)myTcpServer.NumberOfClientsConnected;
+ return 0;
}
- if (IsListening)
- return;
+ }
- if (myTcpServer == null)
+ ///
+ /// Gets or sets the Port
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// S+ helper for Port
+ ///
+ public ushort UPort
+ {
+ get { return Convert.ToUInt16(Port); }
+ set { Port = Convert.ToInt32(value); }
+ }
+
+ ///
+ /// Bool to show whether the server requires a preshared key. Must be set the same in the client, and if true shared keys must be identical on server/client
+ ///
+ public bool SharedKeyRequired { get; set; }
+
+ ///
+ /// S+ helper for requires shared key bool
+ ///
+ public ushort USharedKeyRequired
+ {
+ set
{
- myTcpServer = new TCPServer(Port, MaxClients);
- if(HeartbeatRequired)
- myTcpServer.SocketSendOrReceiveTimeOutInMs = (this.HeartbeatRequiredIntervalMs * 5);
-
- // myTcpServer.HandshakeTimeout = 30;
+ if (value == 1)
+ SharedKeyRequired = true;
+ else
+ SharedKeyRequired = false;
}
- else
+ }
+
+ ///
+ /// Gets or sets the SharedKey
+ ///
+ public string SharedKey { get; set; }
+
+ ///
+ /// Heartbeat Required bool sets whether server disconnects client if heartbeat is not received
+ ///
+ public bool HeartbeatRequired { get; set; }
+
+ ///
+ /// S+ Helper for Heartbeat Required
+ ///
+ public ushort UHeartbeatRequired
+ {
+ set
{
- KillServer();
- myTcpServer.PortNumber = Port;
+ if (value == 1)
+ HeartbeatRequired = true;
+ else
+ HeartbeatRequired = false;
}
-
- myTcpServer.SocketStatusChange -= TcpServer_SocketStatusChange;
- myTcpServer.SocketStatusChange += TcpServer_SocketStatusChange;
-
- ServerStopped = false;
- myTcpServer.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
- OnServerStateChange(myTcpServer.State);
- this.LogInformation("TCP Server Status: {0}, Socket Status: {1}", myTcpServer.State, myTcpServer.ServerSocketStatus);
-
- // StartMonitorClient();
}
- catch (Exception ex)
- {
- this.LogException(ex, "Error with Dynamic Server: {0}", ex.Message);
- }
- } // end lock
- }
- ///
- /// Stop Listening
- ///
- public void StopListening()
- {
- try
+ ///
+ /// Gets or sets the HeartbeatRequiredIntervalMs
+ ///
+ public int HeartbeatRequiredIntervalMs { get; set; }
+
+ ///
+ /// Simpl+ Heartbeat Analog value in seconds
+ ///
+ public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatRequiredIntervalMs = (value * 1000); } }
+
+ ///
+ /// Gets or sets the HeartbeatStringToMatch
+ ///
+ public string HeartbeatStringToMatch { get; set; }
+
+ //private timers for Heartbeats per client
+ Dictionary HeartbeatTimerDictionary = new Dictionary();
+
+ //flags to show the secure server is waiting for client at index to send the shared key
+ List WaitingForSharedKey = new List();
+
+ List ClientReadyAfterKeyExchange = new List();
+
+ ///
+ /// The connected client indexes
+ ///
+ public List ConnectedClientsIndexes = new List();
+
+ ///
+ /// Gets or sets the BufferSize
+ ///
+ public int BufferSize { get; set; }
+
+ ///
+ /// Private flag to note that the server has stopped intentionally
+ ///
+ private bool ServerStopped { get; set; }
+
+ //Servers
+ TCPServer myTcpServer;
+
+ ///
+ ///
+ ///
+ bool ProgramIsStopping;
+
+ #endregion
+
+ #region Constructors
+ ///
+ /// constructor S+ Does not accept a key. Use initialze with key to set the debug key on this device. If using with + make sure to set all properties manually.
+ ///
+ public GenericTcpIpServer()
+ : base("Uninitialized Dynamic TCP Server")
{
- this.LogDebug("Stopping Listener");
- if (myTcpServer != null)
+ HeartbeatRequiredIntervalInSeconds = 15;
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ BufferSize = 2000;
+ MonitorClientMaxFailureCount = 3;
+ }
+
+ ///
+ /// constructor with debug key set at instantiation. Make sure to set all properties before listening.
+ ///
+ ///
+ public GenericTcpIpServer(string key)
+ : base("Uninitialized Dynamic TCP Server")
+ {
+ HeartbeatRequiredIntervalInSeconds = 15;
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ BufferSize = 2000;
+ MonitorClientMaxFailureCount = 3;
+ Key = key;
+ }
+
+ ///
+ /// Contstructor that sets all properties by calling the initialize method with a config object.
+ ///
+ ///
+ public GenericTcpIpServer(TcpServerConfigObject serverConfigObject)
+ : base("Uninitialized Dynamic TCP Server")
+ {
+ HeartbeatRequiredIntervalInSeconds = 15;
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ BufferSize = 2000;
+ MonitorClientMaxFailureCount = 3;
+ Initialize(serverConfigObject);
+ }
+ #endregion
+
+ #region Methods - Server Actions
+ ///
+ /// KillServer method
+ ///
+ public void KillServer()
+ {
+ ServerStopped = true;
+ if (MonitorClient != null)
{
- myTcpServer.Stop();
- this.LogDebug("Server State: {0}", myTcpServer.State);
- OnServerStateChange(myTcpServer.State);
+ MonitorClient.Disconnect();
}
- ServerStopped = true;
+ DisconnectAllClientsForShutdown();
+ StopListening();
}
- catch (Exception ex)
- {
- this.LogException(ex, "Error stopping server. Error: {0}", ex.Message);
- }
- }
- ///
- /// Disconnects Client
- ///
- ///
- public void DisconnectClient(uint client)
- {
- try
+ ///
+ /// Initialize Key for device using client name from SIMPL+. Called on Listen from SIMPL+
+ ///
+ ///
+ ///
+ /// Initialize method
+ ///
+ public void Initialize(string key)
{
- myTcpServer.Disconnect(client);
- this.LogVerbose("Disconnected client index: {0}", client);
+ Key = key;
}
- catch (Exception ex)
+
+ ///
+ /// Initialze with server configuration object
+ ///
+ ///
+ public void Initialize(TcpServerConfigObject serverConfigObject)
{
- this.LogException(ex, "Error Disconnecting client index: {0}. Error: {1}", client, ex.Message);
- }
- }
- ///
- /// Disconnect All Clients
- ///
- public void DisconnectAllClientsForShutdown()
- {
- this.LogInformation("Disconnecting All Clients");
- if (myTcpServer != null)
- {
- myTcpServer.SocketStatusChange -= TcpServer_SocketStatusChange;
- foreach (var index in ConnectedClientsIndexes.ToList()) // copy it here so that it iterates properly
+ try
{
- var i = index;
- if (!myTcpServer.ClientConnected(index))
- continue;
- try
+ if (serverConfigObject != null || string.IsNullOrEmpty(serverConfigObject.Key))
{
- myTcpServer.Disconnect(i);
- this.LogVerbose("Disconnected client index: {0}", i);
- }
- catch (Exception ex)
- {
- this.LogException(ex, "Error Disconnecting client index: {0}. Error: {1}", i, ex.Message);
- }
- }
- this.LogVerbose("Server Status: {0}", myTcpServer.ServerSocketStatus);
- }
+ Key = serverConfigObject.Key;
+ MaxClients = serverConfigObject.MaxClients;
+ Port = serverConfigObject.Port;
+ SharedKeyRequired = serverConfigObject.SharedKeyRequired;
+ SharedKey = serverConfigObject.SharedKey;
+ HeartbeatRequired = serverConfigObject.HeartbeatRequired;
+ HeartbeatRequiredIntervalInSeconds = serverConfigObject.HeartbeatRequiredIntervalInSeconds;
+ HeartbeatStringToMatch = serverConfigObject.HeartbeatStringToMatch;
+ BufferSize = serverConfigObject.BufferSize;
- this.LogInformation("Disconnected All Clients");
- ConnectedClientsIndexes.Clear();
-
- if (!ProgramIsStopping)
- {
- OnConnectionChange();
- OnServerStateChange(myTcpServer.State); //State shows both listening and connected
- }
-
- // var o = new { };
- }
-
- ///
- /// Broadcast text from server to all connected clients
- ///
- ///
- public void BroadcastText(string text)
- {
- lock (_broadcastLock)
- {
- try
- {
- if (ConnectedClientsIndexes.Count > 0)
- {
- byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
- foreach (uint i in ConnectedClientsIndexes)
- {
- if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(i)))
- {
- SocketErrorCodes error = myTcpServer.SendDataAsync(i, b, b.Length, (x, y, z) => { });
- if (error != SocketErrorCodes.SOCKET_OK && error != SocketErrorCodes.SOCKET_OPERATION_PENDING)
- this.LogError("{error}",error.ToString());
- }
- }
- }
- }
- catch (Exception ex)
- {
- this.LogException(ex, "Error Broadcasting messages from server. Error: {0}", ex.Message);
- }
- } // end lock
- }
-
- ///
- /// Not sure this is useful in library, maybe Pro??
- ///
- ///
- ///
- public void SendTextToClient(string text, uint clientIndex)
- {
- try
- {
- byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
- if (myTcpServer != null && myTcpServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
- myTcpServer.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
- }
- }
- catch (Exception ex)
- {
- this.LogException(ex, "Error sending text to client. Text: {1}. Error: {0}", ex.Message, text);
- }
- }
-
- //private method to check heartbeat requirements and start or reset timer
- string checkHeartbeat(uint clientIndex, string received)
- {
- try
- {
- if (HeartbeatRequired)
- {
- if (!string.IsNullOrEmpty(HeartbeatStringToMatch))
- {
- var remainingText = received.Replace(HeartbeatStringToMatch, "");
- var noDelimiter = received.Trim(new char[] { '\r', '\n' });
- if (noDelimiter.Contains(HeartbeatStringToMatch))
- {
- if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
- {
- HeartbeatTimerDictionary[clientIndex].Stop();
- HeartbeatTimerDictionary[clientIndex].Interval = HeartbeatRequiredIntervalMs;
- HeartbeatTimerDictionary[clientIndex].Start();
- }
- else
- {
- var heartbeatTimer = new Timer(HeartbeatRequiredIntervalMs) { AutoReset = false };
- heartbeatTimer.Elapsed += (s, e) => HeartbeatTimer_CallbackFunction(clientIndex);
- heartbeatTimer.Start();
- HeartbeatTimerDictionary.Add(clientIndex, heartbeatTimer);
- }
- this.LogVerbose("Heartbeat Received: {0}, from client index: {1}", HeartbeatStringToMatch, clientIndex);
- // Return Heartbeat
- SendTextToClient(HeartbeatStringToMatch, clientIndex);
- return remainingText;
- }
}
else
{
- if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ ErrorLog.Error("Could not initialize server with key: {0}", serverConfigObject.Key);
+ }
+ }
+ catch
+ {
+ ErrorLog.Error("Could not initialize server with key: {0}", serverConfigObject.Key);
+ }
+ }
+
+ ///
+ /// Listen method
+ ///
+ public void Listen()
+ {
+ ServerCCSection.Enter();
+ try
+ {
+ if (Port < 1 || Port > 65535)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Server '{0}': Invalid port", Key);
+ ErrorLog.Warn(string.Format("Server '{0}': Invalid port", Key));
+ return;
+ }
+ if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Server '{0}': No Shared Key set", Key);
+ ErrorLog.Warn(string.Format("Server '{0}': No Shared Key set", Key));
+ return;
+ }
+ if (IsListening)
+ return;
+
+ if (myTcpServer == null)
+ {
+ myTcpServer = new TCPServer(Port, MaxClients);
+ if(HeartbeatRequired)
+ myTcpServer.SocketSendOrReceiveTimeOutInMs = (this.HeartbeatRequiredIntervalMs * 5);
+
+ // myTcpServer.HandshakeTimeout = 30;
+ }
+ else
+ {
+ KillServer();
+ myTcpServer.PortNumber = Port;
+ }
+
+ myTcpServer.SocketStatusChange -= TcpServer_SocketStatusChange;
+ myTcpServer.SocketStatusChange += TcpServer_SocketStatusChange;
+
+ ServerStopped = false;
+ myTcpServer.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
+ OnServerStateChange(myTcpServer.State);
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "TCP Server Status: {0}, Socket Status: {1}", myTcpServer.State, myTcpServer.ServerSocketStatus);
+
+ // StartMonitorClient();
+
+
+ ServerCCSection.Leave();
+ }
+ catch (Exception ex)
+ {
+ ServerCCSection.Leave();
+ ErrorLog.Error("{1} Error with Dynamic Server: {0}", ex.ToString(), Key);
+ }
+ }
+
+ ///
+ /// StopListening method
+ ///
+ public void StopListening()
+ {
+ try
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Stopping Listener");
+ if (myTcpServer != null)
+ {
+ myTcpServer.Stop();
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server State: {0}", myTcpServer.State);
+ OnServerStateChange(myTcpServer.State);
+ }
+ ServerStopped = true;
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error stopping server. Error: {0}", ex);
+ }
+ }
+
+ ///
+ /// Disconnects Client
+ ///
+ ///
+ ///
+ /// DisconnectClient method
+ ///
+ public void DisconnectClient(uint client)
+ {
+ try
+ {
+ myTcpServer.Disconnect(client);
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", client);
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", client, ex);
+ }
+ }
+ ///
+ /// DisconnectAllClientsForShutdown method
+ ///
+ public void DisconnectAllClientsForShutdown()
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Disconnecting All Clients");
+ if (myTcpServer != null)
+ {
+ myTcpServer.SocketStatusChange -= TcpServer_SocketStatusChange;
+ foreach (var index in ConnectedClientsIndexes.ToList()) // copy it here so that it iterates properly
+ {
+ var i = index;
+ if (!myTcpServer.ClientConnected(index))
+ continue;
+ try
{
- HeartbeatTimerDictionary[clientIndex].Stop();
- HeartbeatTimerDictionary[clientIndex].Interval = HeartbeatRequiredIntervalMs;
- HeartbeatTimerDictionary[clientIndex].Start();
+ myTcpServer.Disconnect(i);
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", i);
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", i, ex);
+ }
+ }
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server Status: {0}", myTcpServer.ServerSocketStatus);
+ }
+
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected All Clients");
+ ConnectedClientsIndexes.Clear();
+
+ if (!ProgramIsStopping)
+ {
+ OnConnectionChange();
+ OnServerStateChange(myTcpServer.State); //State shows both listening and connected
+ }
+
+ // var o = new { };
+ }
+
+ ///
+ /// Broadcast text from server to all connected clients
+ ///
+ ///
+ ///
+ /// BroadcastText method
+ ///
+ public void BroadcastText(string text)
+ {
+ CCriticalSection CCBroadcast = new CCriticalSection();
+ CCBroadcast.Enter();
+ try
+ {
+ if (ConnectedClientsIndexes.Count > 0)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
+ foreach (uint i in ConnectedClientsIndexes)
+ {
+ if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(i)))
+ {
+ SocketErrorCodes error = myTcpServer.SendDataAsync(i, b, b.Length, (x, y, z) => { });
+ if (error != SocketErrorCodes.SOCKET_OK && error != SocketErrorCodes.SOCKET_OPERATION_PENDING)
+ this.LogError("{error}",error.ToString());
+ }
+ }
+ }
+ CCBroadcast.Leave();
+ }
+ catch (Exception ex)
+ {
+ CCBroadcast.Leave();
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Broadcasting messages from server. Error: {0}", ex.Message);
+ }
+ }
+
+ ///
+ /// Not sure this is useful in library, maybe Pro??
+ ///
+ ///
+ ///
+ ///
+ /// SendTextToClient method
+ ///
+ public void SendTextToClient(string text, uint clientIndex)
+ {
+ try
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
+ if (myTcpServer != null && myTcpServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
+ myTcpServer.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, "Error sending text to client. Text: {1}. Error: {0}", ex.Message, text);
+ }
+ }
+
+ //private method to check heartbeat requirements and start or reset timer
+ string checkHeartbeat(uint clientIndex, string received)
+ {
+ try
+ {
+ if (HeartbeatRequired)
+ {
+ if (!string.IsNullOrEmpty(HeartbeatStringToMatch))
+ {
+ var remainingText = received.Replace(HeartbeatStringToMatch, "");
+ var noDelimiter = received.Trim(new char[] { '\r', '\n' });
+ if (noDelimiter.Contains(HeartbeatStringToMatch))
+ {
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
+ else
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ Debug.Console(1, this, "Heartbeat Received: {0}, from client index: {1}", HeartbeatStringToMatch, clientIndex);
+ // Return Heartbeat
+ SendTextToClient(HeartbeatStringToMatch, clientIndex);
+ return remainingText;
+ }
}
else
{
- var heartbeatTimer = new Timer(HeartbeatRequiredIntervalMs) { AutoReset = false };
- heartbeatTimer.Elapsed += (s, e) => HeartbeatTimer_CallbackFunction(clientIndex);
- heartbeatTimer.Start();
- HeartbeatTimerDictionary.Add(clientIndex, heartbeatTimer);
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
+ else
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ Debug.Console(1, this, "Heartbeat Received: {0}, from client index: {1}", received, clientIndex);
}
- this.LogVerbose("Heartbeat Received: {0}, from client index: {1}", received, clientIndex);
}
}
- }
- catch (Exception ex)
- {
- this.LogException(ex, "Error checking heartbeat: {0}", ex.Message);
- }
- return received;
- }
-
- ///
- /// Gets the IP address based on the client index
- ///
- ///
- /// IP address of the client
- public string GetClientIPAddress(uint clientIndex)
- {
- this.LogVerbose("GetClientIPAddress Index: {0}", clientIndex);
- if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
- {
- var ipa = this.myTcpServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
- this.LogVerbose("GetClientIPAddress IPAddreess: {0}", ipa);
- return ipa;
-
- }
- else
- {
- return "";
- }
- }
-
- #endregion
-
- #region Methods - HeartbeatTimer Callback
-
- void HeartbeatTimer_CallbackFunction(object o)
- {
- uint clientIndex = 99999;
- string address = string.Empty;
- try
- {
- clientIndex = (uint)o;
- address = myTcpServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
-
- this.LogWarning("Heartbeat not received for Client index {2} IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE {1}",
- address, string.IsNullOrEmpty(HeartbeatStringToMatch) ? "" : ("HeartbeatStringToMatch: " + HeartbeatStringToMatch), clientIndex);
-
- if (myTcpServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
- SendTextToClient("Heartbeat not received by server, closing connection", clientIndex);
-
- var discoResult = myTcpServer.Disconnect(clientIndex);
-
- if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ catch (Exception ex)
{
- HeartbeatTimerDictionary[clientIndex].Stop();
- HeartbeatTimerDictionary[clientIndex].Dispose();
- HeartbeatTimerDictionary.Remove(clientIndex);
+ Debug.Console(1, this, "Error checking heartbeat: {0}", ex.Message);
+ }
+ return received;
+ }
+
+ ///
+ /// Gets the IP address based on the client index
+ ///
+ ///
+ /// IP address of the client
+ ///
+ /// GetClientIPAddress method
+ ///
+ public string GetClientIPAddress(uint clientIndex)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress Index: {0}", clientIndex);
+ if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
+ {
+ var ipa = this.myTcpServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress IPAddreess: {0}", ipa);
+ return ipa;
+
+ }
+ else
+ {
+ return "";
}
}
- catch (Exception ex)
+
+ #endregion
+
+ #region Methods - HeartbeatTimer Callback
+
+ void HeartbeatTimer_CallbackFunction(object o)
{
- this.LogException(ex, "Heartbeat timeout Error on Client Index: {0}, at address: {1}, error: {2}", clientIndex, address, ex.Message);
- this.LogVerbose("Stack Trace:\r{0}", ex.StackTrace);
- }
- }
-
- #endregion
-
- #region Methods - Socket Status Changed Callbacks
- ///
- /// Secure Server Socket Status Changed Callback
- ///
- ///
- ///
- ///
- void TcpServer_SocketStatusChange(TCPServer server, uint clientIndex, SocketStatus serverSocketStatus)
- {
- try
- {
-
- this.LogInformation("SecureServerSocketStatusChange Index:{0} status:{1} Port:{2} IP:{3}", clientIndex, serverSocketStatus, this.myTcpServer.GetPortNumberServerAcceptedConnectionFromForSpecificClient(clientIndex), this.myTcpServer.GetLocalAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
- if (serverSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ uint clientIndex = 99999;
+ string address = string.Empty;
+ try
{
- if (ConnectedClientsIndexes.Contains(clientIndex))
- ConnectedClientsIndexes.Remove(clientIndex);
- if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ clientIndex = (uint)o;
+ address = myTcpServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Heartbeat not received for Client index {2} IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE {1}",
+ address, string.IsNullOrEmpty(HeartbeatStringToMatch) ? "" : ("HeartbeatStringToMatch: " + HeartbeatStringToMatch), clientIndex);
+
+ if (myTcpServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ SendTextToClient("Heartbeat not received by server, closing connection", clientIndex);
+
+ var discoResult = myTcpServer.Disconnect(clientIndex);
+ //Debug.Console(1, this, "{0}", discoResult);
+
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
{
HeartbeatTimerDictionary[clientIndex].Stop();
HeartbeatTimerDictionary[clientIndex].Dispose();
HeartbeatTimerDictionary.Remove(clientIndex);
}
- if (ClientReadyAfterKeyExchange.Contains(clientIndex))
- ClientReadyAfterKeyExchange.Remove(clientIndex);
+ }
+ catch (Exception ex)
+ {
+ ErrorLog.Error("{3}: Heartbeat timeout Error on Client Index: {0}, at address: {1}, error: {2}", clientIndex, address, ex.Message, Key);
+ }
+ }
+
+ #endregion
+
+ #region Methods - Socket Status Changed Callbacks
+ ///
+ /// Secure Server Socket Status Changed Callback
+ ///
+ ///
+ ///
+ ///
+ void TcpServer_SocketStatusChange(TCPServer server, uint clientIndex, SocketStatus serverSocketStatus)
+ {
+ try
+ {
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SecureServerSocketStatusChange Index:{0} status:{1} Port:{2} IP:{3}", clientIndex, serverSocketStatus, this.myTcpServer.GetPortNumberServerAcceptedConnectionFromForSpecificClient(clientIndex), this.myTcpServer.GetLocalAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
+ if (serverSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ if (ConnectedClientsIndexes.Contains(clientIndex))
+ ConnectedClientsIndexes.Remove(clientIndex);
+ if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ {
+ HeartbeatTimerDictionary[clientIndex].Stop();
+ HeartbeatTimerDictionary[clientIndex].Dispose();
+ HeartbeatTimerDictionary.Remove(clientIndex);
+ }
+ if (ClientReadyAfterKeyExchange.Contains(clientIndex))
+ ClientReadyAfterKeyExchange.Remove(clientIndex);
if (WaitingForSharedKey.Contains(clientIndex))
WaitingForSharedKey.Remove(clientIndex);
- }
- }
- catch (Exception ex)
- {
- this.LogException(ex, "Error in Socket Status Change Callback. Error: {0}", ex);
- }
- onConnectionChange(clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
- }
-
- #endregion
-
- #region Methods Connected Callbacks
- ///
- /// Secure TCP Client Connected to Secure Server Callback
- ///
- ///
- ///
- void TcpConnectCallback(TCPServer server, uint clientIndex)
- {
- try
- {
- this.LogDebug("ConnectCallback: IPAddress: {0}. Index: {1}. Status: {2}",
- server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex),
- clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
- if (clientIndex != 0)
- {
- if (server.ClientConnected(clientIndex))
- {
-
- if (!ConnectedClientsIndexes.Contains(clientIndex))
- {
- ConnectedClientsIndexes.Add(clientIndex);
- }
- if (SharedKeyRequired)
- {
- if (!WaitingForSharedKey.Contains(clientIndex))
- {
- WaitingForSharedKey.Add(clientIndex);
- }
- byte[] b = Encoding.GetEncoding(28591).GetBytes("SharedKey:");
- server.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
- this.LogDebug("Sent Shared Key Request to client at {0}", server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
- }
- else
- {
- this.LogDebug("Client at index {0} is ready for communications", clientIndex);
- OnServerClientReadyForCommunications(clientIndex);
- }
- if (HeartbeatRequired)
- {
- if (!HeartbeatTimerDictionary.ContainsKey(clientIndex))
- {
- var heartbeatTimer = new Timer(HeartbeatRequiredIntervalMs) { AutoReset = false };
- heartbeatTimer.Elapsed += (s, e) => HeartbeatTimer_CallbackFunction(clientIndex);
- heartbeatTimer.Start();
- HeartbeatTimerDictionary.Add(clientIndex, heartbeatTimer);
- }
- }
-
- server.ReceiveDataAsync(clientIndex, TcpServerReceivedDataAsyncCallback);
}
}
- else
+ catch (Exception ex)
{
- this.LogError("Client attempt faulty.");
- if (!ServerStopped)
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Change Callback. Error: {0}", ex);
+ }
+ onConnectionChange(clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
+ }
+
+ #endregion
+
+ #region Methods Connected Callbacks
+ ///
+ /// Secure TCP Client Connected to Secure Server Callback
+ ///
+ ///
+ ///
+ void TcpConnectCallback(TCPServer server, uint clientIndex)
+ {
+ try
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "ConnectCallback: IPAddress: {0}. Index: {1}. Status: {2}",
+ server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex),
+ clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
+ if (clientIndex != 0)
{
- server.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
- return;
+ if (server.ClientConnected(clientIndex))
+ {
+
+ if (!ConnectedClientsIndexes.Contains(clientIndex))
+ {
+ ConnectedClientsIndexes.Add(clientIndex);
+ }
+ if (SharedKeyRequired)
+ {
+ if (!WaitingForSharedKey.Contains(clientIndex))
+ {
+ WaitingForSharedKey.Add(clientIndex);
+ }
+ byte[] b = Encoding.GetEncoding(28591).GetBytes("SharedKey:");
+ server.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Sent Shared Key Request to client at {0}", server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
+ }
+ else
+ {
+ OnServerClientReadyForCommunications(clientIndex);
+ }
+ if (HeartbeatRequired)
+ {
+ if (!HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ {
+ HeartbeatTimerDictionary.Add(clientIndex, new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs));
+ }
+ }
+
+ server.ReceiveDataAsync(clientIndex, TcpServerReceivedDataAsyncCallback);
+ }
+ }
+ else
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Client attempt faulty.");
+ if (!ServerStopped)
+ {
+ server.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
+ return;
+ }
}
}
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Connect Callback. Error: {0}", ex);
+ }
+ //Debug.Console(1, this, Debug.ErrorLogLevel, "((((((Server State bitfield={0}; maxclient={1}; ServerStopped={2}))))))",
+ // server.State,
+ // MaxClients,
+ // ServerStopped);
+ if ((server.State & ServerState.SERVER_LISTENING) != ServerState.SERVER_LISTENING && MaxClients > 1 && !ServerStopped)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Waiting for next connection");
+ server.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
+
+ }
}
- catch (Exception ex)
+
+ #endregion
+
+ #region Methods - Send/Receive Callbacks
+ ///
+ /// Secure Received Data Async Callback
+ ///
+ ///
+ ///
+ ///
+ void TcpServerReceivedDataAsyncCallback(TCPServer myTCPServer, uint clientIndex, int numberOfBytesReceived)
{
- this.LogException(ex, "Error in Socket Status Connect Callback. Error: {0}", ex.Message);
- this.LogVerbose("Stack Trace:\r{0}", ex.StackTrace);
- }
- // server.State,
- // MaxClients,
- // ServerStopped);
- if ((server.State & ServerState.SERVER_LISTENING) != ServerState.SERVER_LISTENING && MaxClients > 1 && !ServerStopped)
- {
- this.LogDebug("Waiting for next connection");
- server.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
-
- }
- }
-
- #endregion
-
- #region Methods - Send/Receive Callbacks
- ///
- /// Secure Received Data Async Callback
- ///
- ///
- ///
- ///
- void TcpServerReceivedDataAsyncCallback(TCPServer myTCPServer, uint clientIndex, int numberOfBytesReceived)
- {
if (numberOfBytesReceived > 0)
{
string received = "Nothing";
@@ -819,7 +816,7 @@ public class GenericTcpIpServer : Device
if (received != SharedKey)
{
byte[] b = Encoding.GetEncoding(28591).GetBytes("Shared key did not match server. Disconnecting");
- this.LogWarning("Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received);
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received);
myTCPServer.SendData(clientIndex, b, b.Length);
myTCPServer.Disconnect(clientIndex);
return;
@@ -829,7 +826,7 @@ public class GenericTcpIpServer : Device
byte[] success = Encoding.GetEncoding(28591).GetBytes("Shared Key Match");
myTCPServer.SendDataAsync(clientIndex, success, success.Length, null);
OnServerClientReadyForCommunications(clientIndex);
- this.LogDebug("Client with index {0} provided the shared key and successfully connected to the server", clientIndex);
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Client with index {0} provided the shared key and successfully connected to the server", clientIndex);
}
else if (!string.IsNullOrEmpty(checkHeartbeat(clientIndex, received)))
@@ -837,7 +834,7 @@ public class GenericTcpIpServer : Device
}
catch (Exception ex)
{
- this.LogException(ex, "Error Receiving data: {0}. Error: {1}", received, ex);
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Receiving data: {0}. Error: {1}", received, ex);
}
if (myTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
myTCPServer.ReceiveDataAsync(clientIndex, TcpServerReceivedDataAsyncCallback);
@@ -848,182 +845,181 @@ public class GenericTcpIpServer : Device
myTCPServer.Disconnect();
}
- }
+ }
- #endregion
+ #endregion
- #region Methods - EventHelpers/Callbacks
+ #region Methods - EventHelpers/Callbacks
- //Private Helper method to call the Connection Change Event
- void onConnectionChange(uint clientIndex, SocketStatus clientStatus)
- {
- if (clientIndex != 0) //0 is error not valid client change
+ //Private Helper method to call the Connection Change Event
+ void onConnectionChange(uint clientIndex, SocketStatus clientStatus)
{
+ if (clientIndex != 0) //0 is error not valid client change
+ {
+ var handler = ClientConnectionChange;
+ if (handler != null)
+ {
+ handler(this, new GenericTcpServerSocketStatusChangeEventArgs(myTcpServer, clientIndex, clientStatus));
+ }
+ }
+ }
+
+ //Private Helper method to call the Connection Change Event
+ void OnConnectionChange()
+ {
+ if (ProgramIsStopping)
+ {
+ return;
+ }
var handler = ClientConnectionChange;
if (handler != null)
{
- handler(this, new GenericTcpServerSocketStatusChangeEventArgs(myTcpServer, clientIndex, clientStatus));
+ handler(this, new GenericTcpServerSocketStatusChangeEventArgs());
}
}
- }
- //Private Helper method to call the Connection Change Event
- void OnConnectionChange()
- {
- if (ProgramIsStopping)
+ //Private Helper Method to call the Text Received Event
+ void onTextReceived(string text, uint clientIndex)
{
- return;
+ var handler = TextReceived;
+ if (handler != null)
+ handler(this, new GenericTcpServerCommMethodReceiveTextArgs(text, clientIndex));
}
- var handler = ClientConnectionChange;
- if (handler != null)
- {
- handler(this, new GenericTcpServerSocketStatusChangeEventArgs());
- }
- }
- //Private Helper Method to call the Text Received Event
- void onTextReceived(string text, uint clientIndex)
- {
- var handler = TextReceived;
- if (handler != null)
- handler(this, new GenericTcpServerCommMethodReceiveTextArgs(text, clientIndex));
- }
-
- //Private Helper Method to call the Server State Change Event
- void OnServerStateChange(ServerState state)
- {
- if (ProgramIsStopping)
+ //Private Helper Method to call the Server State Change Event
+ void OnServerStateChange(ServerState state)
{
- return;
+ if (ProgramIsStopping)
+ {
+ return;
+ }
+ var handler = ServerStateChange;
+ if (handler != null)
+ {
+ handler(this, new GenericTcpServerStateChangedEventArgs(state));
+ }
}
- var handler = ServerStateChange;
- if (handler != null)
- {
- handler(this, new GenericTcpServerStateChangedEventArgs(state));
- }
- }
- ///
- /// Private Event Handler method to handle the closing of connections when the program stops
- ///
- ///
- void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
- {
- if (programEventType == eProgramStatusEventType.Stopping)
+ ///
+ /// Private Event Handler method to handle the closing of connections when the program stops
+ ///
+ ///
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping)
+ {
+ ProgramIsStopping = true;
+ // kill bandaid things
+ if (MonitorClientTimer != null)
+ MonitorClientTimer.Stop();
+ if (MonitorClient != null)
+ MonitorClient.Disconnect();
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing server");
+ KillServer();
+ }
+ }
+
+ //Private event handler method to raise the event that the server is ready to send data after a successful client shared key negotiation
+ void OnServerClientReadyForCommunications(uint clientIndex)
+ {
+ ClientReadyAfterKeyExchange.Add(clientIndex);
+ var handler = ServerClientReadyForCommunications;
+ if (handler != null)
+ handler(this, new GenericTcpServerSocketStatusChangeEventArgs(
+ this, clientIndex, myTcpServer.GetServerSocketStatusForSpecificClient(clientIndex)));
+ }
+ #endregion
+
+ #region Monitor Client
+ ///
+ /// Starts the monitor client cycle. Timed wait, then call RunMonitorClient
+ ///
+ void StartMonitorClient()
{
- ProgramIsStopping = true;
- // kill bandaid things
if (MonitorClientTimer != null)
+ {
+ return;
+ }
+ MonitorClientTimer = new CTimer(o => RunMonitorClient(), 60000);
+ }
+
+ ///
+ ///
+ ///
+ void RunMonitorClient()
+ {
+ MonitorClient = new GenericTcpIpClient_ForServer(Key + "-MONITOR", "127.0.0.1", Port, 2000);
+ MonitorClient.SharedKeyRequired = this.SharedKeyRequired;
+ MonitorClient.SharedKey = this.SharedKey;
+ MonitorClient.ConnectionHasHungCallback = MonitorClientHasHungCallback;
+ //MonitorClient.ConnectionChange += MonitorClient_ConnectionChange;
+ MonitorClient.ClientReadyForCommunications += MonitorClient_IsReadyForComm;
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Starting monitor check");
+
+ MonitorClient.Connect();
+ // From here MonitorCLient either connects or hangs, MonitorClient will call back
+
+ }
+
+ ///
+ ///
+ ///
+ void StopMonitorClient()
+ {
+ if (MonitorClient == null)
+ return;
+
+ MonitorClient.ClientReadyForCommunications -= MonitorClient_IsReadyForComm;
+ MonitorClient.Disconnect();
+ MonitorClient = null;
+ }
+
+ ///
+ /// On monitor connect, restart the operation
+ ///
+ void MonitorClient_IsReadyForComm(object sender, GenericTcpServerClientReadyForcommunicationsEventArgs args)
+ {
+ if (args.IsReady)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Monitor client connection success. Disconnecting in 2s");
MonitorClientTimer.Stop();
- if (MonitorClient != null)
- MonitorClient.Disconnect();
-
- this.LogInformation("Program stopping. Closing server");
- KillServer();
+ MonitorClientTimer = null;
+ MonitorClientFailureCount = 0;
+ CrestronEnvironment.Sleep(2000);
+ StopMonitorClient();
+ StartMonitorClient();
+ }
}
- }
- //Private event handler method to raise the event that the server is ready to send data after a successful client shared key negotiation
- void OnServerClientReadyForCommunications(uint clientIndex)
- {
- ClientReadyAfterKeyExchange.Add(clientIndex);
- var handler = ServerClientReadyForCommunications;
- if (handler != null)
- handler(this, new GenericTcpServerSocketStatusChangeEventArgs(
- this, clientIndex, myTcpServer.GetServerSocketStatusForSpecificClient(clientIndex)));
- }
- #endregion
-
- #region Monitor Client
- ///
- /// Starts the monitor client cycle. Timed wait, then call RunMonitorClient
- ///
- void StartMonitorClient()
- {
- if (MonitorClientTimer != null)
+ ///
+ /// If the client hangs, add to counter and maybe fire the choke event
+ ///
+ void MonitorClientHasHungCallback()
{
- return;
- }
- MonitorClientTimer = new Timer(60000) { AutoReset = false };
- MonitorClientTimer.Elapsed += (s, e) => RunMonitorClient();
- MonitorClientTimer.Start();
- }
-
- ///
- ///
- ///
- void RunMonitorClient()
- {
- MonitorClient = new GenericTcpIpClient_ForServer(Key + "-MONITOR", "127.0.0.1", Port, 2000);
- MonitorClient.SharedKeyRequired = this.SharedKeyRequired;
- MonitorClient.SharedKey = this.SharedKey;
- MonitorClient.ConnectionHasHungCallback = MonitorClientHasHungCallback;
- //MonitorClient.ConnectionChange += MonitorClient_ConnectionChange;
- MonitorClient.ClientReadyForCommunications += MonitorClient_IsReadyForComm;
-
- this.LogDebug("Starting monitor check");
-
- MonitorClient.Connect();
- // From here MonitorCLient either connects or hangs, MonitorClient will call back
-
- }
-
- ///
- ///
- ///
- void StopMonitorClient()
- {
- if (MonitorClient == null)
- return;
-
- MonitorClient.ClientReadyForCommunications -= MonitorClient_IsReadyForComm;
- MonitorClient.Disconnect();
- MonitorClient = null;
- }
-
- ///
- /// On monitor connect, restart the operation
- ///
- void MonitorClient_IsReadyForComm(object sender, GenericTcpServerClientReadyForcommunicationsEventArgs args)
- {
- if (args.IsReady)
- {
- this.LogInformation("Monitor client connection success. Disconnecting in 2s");
+ MonitorClientFailureCount++;
MonitorClientTimer.Stop();
MonitorClientTimer = null;
- MonitorClientFailureCount = 0;
- CrestronEnvironment.Sleep(2000);
StopMonitorClient();
- StartMonitorClient();
- }
- }
+ if (MonitorClientFailureCount < MonitorClientMaxFailureCount)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Warning, "Monitor client connection has hung {0} time{1}, maximum {2}",
+ MonitorClientFailureCount, MonitorClientFailureCount > 1 ? "s" : "", MonitorClientMaxFailureCount);
+ StartMonitorClient();
+ }
+ else
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error,
+ "\r***************************\rMonitor client connection has hung a maximum of {0} times.\r***************************",
+ MonitorClientMaxFailureCount);
- ///
- /// If the client hangs, add to counter and maybe fire the choke event
- ///
- void MonitorClientHasHungCallback()
- {
- MonitorClientFailureCount++;
- MonitorClientTimer.Stop();
- MonitorClientTimer = null;
- StopMonitorClient();
- if (MonitorClientFailureCount < MonitorClientMaxFailureCount)
- {
- this.LogWarning("Monitor client connection has hung {0} time{1}, maximum {2}",
- MonitorClientFailureCount, MonitorClientFailureCount > 1 ? "s" : "", MonitorClientMaxFailureCount);
- StartMonitorClient();
- }
- else
- {
- this.LogError(
- "\r***************************\rMonitor client connection has hung a maximum of {0} times.\r***************************",
- MonitorClientMaxFailureCount);
-
- var handler = ServerHasChoked;
- if (handler != null)
- handler();
- // Some external thing is in charge here. Expected reset of program
+ var handler = ServerHasChoked;
+ if (handler != null)
+ handler();
+ // Some external thing is in charge here. Expected reset of program
+ }
}
+ #endregion
}
- #endregion
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Comm/GenericUdpClient.cs b/src/PepperDash.Core/Comm/GenericUdpClient.cs
new file mode 100644
index 00000000..41792537
--- /dev/null
+++ b/src/PepperDash.Core/Comm/GenericUdpClient.cs
@@ -0,0 +1,463 @@
+using System;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronSockets;
+using ThreadingTimeout = System.Threading.Timeout;
+using NetSocketException = System.Net.Sockets.SocketException;
+
+namespace PepperDash.Core
+{
+ ///
+ /// A class to handle basic UDP communications to a remote endpoint
+ ///
+ public class GenericUdpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
+ {
+ private const string SplusKey = "Uninitialized UdpClient";
+
+ private readonly object stateLock = new object();
+ private readonly Timer reconnectTimer;
+
+ private UdpClient client;
+ private CancellationTokenSource receiveCancellationTokenSource;
+ private bool connectEnabled;
+ private bool connectionRefusedLogged;
+ private SocketStatus clientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;
+
+ ///
+ /// Object to enable stream debugging
+ ///
+ public CommunicationStreamDebugging StreamDebugging { get; private set; }
+
+ ///
+ /// Fires when data is received from the remote endpoint and returns it as a byte array
+ ///
+ public event EventHandler BytesReceived;
+
+ ///
+ /// Fires when data is received from the remote endpoint and returns it as text
+ ///
+ public event EventHandler TextReceived;
+
+ ///
+ /// Fires when the socket status changes
+ ///
+ public event EventHandler ConnectionChange;
+
+ ///
+ /// Address of remote endpoint
+ ///
+ public string Hostname { get; set; }
+
+ ///
+ /// Port on remote endpoint
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// Another S+ helper because large port numbers can be treated as signed ints
+ ///
+ public ushort UPort
+ {
+ get { return Convert.ToUInt16(Port); }
+ set { Port = Convert.ToInt32(value); }
+ }
+
+ ///
+ /// Defaults to 2000
+ ///
+ public int BufferSize { get; set; }
+
+ ///
+ /// True when the local socket is created and associated with the configured remote endpoint
+ ///
+ public bool IsConnected
+ {
+ get { return ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsConnected
+ {
+ get { return (ushort)(IsConnected ? 1 : 0); }
+ }
+
+ ///
+ /// The current socket status of the client
+ ///
+ public SocketStatus ClientStatus
+ {
+ get
+ {
+ lock (stateLock)
+ {
+ return clientStatus;
+ }
+ }
+ private set
+ {
+ var shouldFireEvent = false;
+
+ lock (stateLock)
+ {
+ if (clientStatus != value)
+ {
+ clientStatus = value;
+ shouldFireEvent = true;
+ }
+ }
+
+ if (shouldFireEvent)
+ ConnectionChange?.Invoke(this, new GenericSocketStatusChageEventArgs(this));
+ }
+ }
+
+ ///
+ /// Ushort representation of client status
+ ///
+ public ushort UStatus
+ {
+ get { return (ushort)ClientStatus; }
+ }
+
+ ///
+ /// Gets or sets the AutoReconnect
+ ///
+ public bool AutoReconnect { get; set; }
+
+ ///
+ /// S+ helper for AutoReconnect
+ ///
+ public ushort UAutoReconnect
+ {
+ get { return (ushort)(AutoReconnect ? 1 : 0); }
+ set { AutoReconnect = value == 1; }
+ }
+
+ ///
+ /// Milliseconds to wait before attempting to reconnect. Defaults to 5000
+ ///
+ public int AutoReconnectIntervalMs { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ public GenericUdpClient(string key, string address, int port, int bufferSize)
+ : base(key)
+ {
+ StreamDebugging = new CommunicationStreamDebugging(key);
+ CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
+ AutoReconnectIntervalMs = 5000;
+ Hostname = address;
+ Port = port;
+ BufferSize = bufferSize;
+
+ reconnectTimer = new Timer(o =>
+ {
+ if (connectEnabled)
+ Connect();
+ }, null, ThreadingTimeout.Infinite, ThreadingTimeout.Infinite);
+ }
+
+ ///
+ /// Constructor for S+
+ ///
+ public GenericUdpClient()
+ : base(SplusKey)
+ {
+ StreamDebugging = new CommunicationStreamDebugging(SplusKey);
+ CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
+ AutoReconnectIntervalMs = 5000;
+ BufferSize = 2000;
+
+ reconnectTimer = new Timer(o =>
+ {
+ if (connectEnabled)
+ Connect();
+ }, null, ThreadingTimeout.Infinite, ThreadingTimeout.Infinite);
+ }
+
+ ///
+ /// Initialize method
+ ///
+ public void Initialize(string key)
+ {
+ Key = key;
+ }
+
+ private void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping)
+ {
+ Debug.Console(1, this, "Program stopping. Closing connection");
+ Deactivate();
+ }
+ }
+
+ ///
+ /// Deactivate method
+ ///
+ public override bool Deactivate()
+ {
+ Disconnect();
+ return true;
+ }
+
+ ///
+ /// Connect method
+ ///
+ public void Connect()
+ {
+ if (string.IsNullOrEmpty(Hostname))
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpClient '{0}': No address set", Key);
+ ClientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;
+ return;
+ }
+
+ if (Port < 1 || Port > 65535)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpClient '{0}': Invalid port", Key);
+ ClientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;
+ return;
+ }
+
+ var hostname = Hostname;
+ var port = Port;
+ var bufferSize = BufferSize;
+ UdpClient newClient = null;
+ CancellationTokenSource newReceiveCancellationTokenSource = null;
+ CancellationToken startReceiveToken = default(CancellationToken);
+ var shouldStartReceive = false;
+
+ lock (stateLock)
+ {
+ connectEnabled = true;
+
+ if (client != null)
+ return;
+ }
+
+ try
+ {
+ newReceiveCancellationTokenSource = new CancellationTokenSource();
+ newClient = new UdpClient();
+ newClient.Client.ReceiveBufferSize = bufferSize;
+ newClient.Client.SendBufferSize = bufferSize;
+ newClient.Connect(hostname, port);
+
+ lock (stateLock)
+ {
+ if (!connectEnabled || client != null)
+ {
+ newClient.Close();
+ newReceiveCancellationTokenSource.Cancel();
+ newReceiveCancellationTokenSource.Dispose();
+ return;
+ }
+
+ receiveCancellationTokenSource = newReceiveCancellationTokenSource;
+ client = newClient;
+ ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED;
+ reconnectTimer.Change(ThreadingTimeout.Infinite, ThreadingTimeout.Infinite);
+ startReceiveToken = receiveCancellationTokenSource.Token;
+ shouldStartReceive = true;
+ }
+
+ if (shouldStartReceive)
+ StartReceive(startReceiveToken);
+ }
+ catch (Exception ex)
+ {
+ Debug.LogMessage(ex, "Error connecting UDP client {0}", this, Key);
+
+ if (newClient != null)
+ newClient.Close();
+
+ if (newReceiveCancellationTokenSource != null)
+ {
+ newReceiveCancellationTokenSource.Cancel();
+ newReceiveCancellationTokenSource.Dispose();
+ }
+
+ lock (stateLock)
+ {
+ if (connectEnabled && client == null)
+ {
+ ClientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;
+ StartReconnectTimer();
+ }
+ }
+ }
+ }
+
+ ///
+ /// Disconnect method
+ ///
+ public void Disconnect()
+ {
+ lock (stateLock)
+ {
+ connectEnabled = false;
+ reconnectTimer.Change(ThreadingTimeout.Infinite, ThreadingTimeout.Infinite);
+ CleanupClient();
+ ClientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;
+ }
+ }
+
+ ///
+ /// SendText method
+ ///
+ public void SendText(string text)
+ {
+ this.PrintSentText(text);
+
+ var bytes = Encoding.GetEncoding(28591).GetBytes(text);
+ SendBytes(bytes);
+ }
+
+ ///
+ /// SendBytes method
+ ///
+ public void SendBytes(byte[] bytes)
+ {
+ if (bytes == null)
+ return;
+
+ try
+ {
+ this.PrintSentBytes(bytes);
+
+ if (!IsConnected || client == null)
+ Connect();
+
+ var udpClient = client;
+ if (!IsConnected || udpClient == null)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpClient '{0}': Cannot send bytes because the client is not connected", Key);
+ return;
+ }
+
+ udpClient.Send(bytes, bytes.Length);
+ }
+ catch (Exception ex)
+ {
+ Debug.LogMessage(ex, "Error sending UDP bytes for {0}", this, Key);
+ HandleDisconnected();
+ }
+ }
+
+ private void StartReceive(CancellationToken token)
+ {
+ Task.Run(async () =>
+ {
+ while (!token.IsCancellationRequested)
+ {
+ try
+ {
+ var udpClient = client;
+ if (udpClient == null)
+ return;
+
+ var result = await udpClient.ReceiveAsync().ConfigureAwait(false);
+ var bytes = result.Buffer;
+ if (bytes == null || bytes.Length == 0)
+ continue;
+
+ connectionRefusedLogged = false;
+
+ var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
+
+ this.PrintReceivedBytes(bytes);
+ this.PrintReceivedText(text);
+
+ BytesReceived?.Invoke(this, new GenericCommMethodReceiveBytesArgs(bytes));
+ TextReceived?.Invoke(this, new GenericCommMethodReceiveTextArgs(text));
+ }
+ catch (ObjectDisposedException)
+ {
+ return;
+ }
+ catch (InvalidOperationException)
+ {
+ return;
+ }
+ catch (NetSocketException ex)
+ {
+ if (ex.SocketErrorCode == SocketError.ConnectionRefused)
+ {
+ if (!connectionRefusedLogged)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning,
+ "GenericUdpClient '{0}': Remote endpoint refused UDP traffic or is no longer listening",
+ Key);
+ connectionRefusedLogged = true;
+ }
+
+ HandleDisconnected();
+ return;
+ }
+
+ Debug.LogMessage(ex, "UDP receive error for {0}", this, Key);
+
+ if (AutoReconnect)
+ {
+ HandleDisconnected();
+ return;
+ }
+
+ continue;
+ }
+ catch (Exception ex)
+ {
+ Debug.LogMessage(ex, "Unexpected UDP receive error for {0}", this, Key);
+
+ if (AutoReconnect)
+ {
+ HandleDisconnected();
+ return;
+ }
+
+ continue;
+ }
+ }
+ }, token);
+ }
+
+ private void HandleDisconnected()
+ {
+ lock (stateLock)
+ {
+ CleanupClient();
+ ClientStatus = SocketStatus.SOCKET_STATUS_NO_CONNECT;
+ StartReconnectTimer();
+ }
+ }
+
+ private void StartReconnectTimer()
+ {
+ if (AutoReconnect && connectEnabled)
+ reconnectTimer.Change(AutoReconnectIntervalMs, ThreadingTimeout.Infinite);
+ }
+
+ private void CleanupClient()
+ {
+ if (receiveCancellationTokenSource != null)
+ {
+ receiveCancellationTokenSource.Cancel();
+ receiveCancellationTokenSource.Dispose();
+ receiveCancellationTokenSource = null;
+ }
+
+ if (client != null)
+ {
+ client.Close();
+ client = null;
+ }
+ }
+ }
+}
diff --git a/src/PepperDash.Core/Comm/GenericUdpServer.cs b/src/PepperDash.Core/Comm/GenericUdpServer.cs
index 2d18d741..a713872a 100644
--- a/src/PepperDash.Core/Comm/GenericUdpServer.cs
+++ b/src/PepperDash.Core/Comm/GenericUdpServer.cs
@@ -1,357 +1,371 @@
-extern alias NewtonsoftJson;
-
using System;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronSockets;
-using JsonProperty = NewtonsoftJson::Newtonsoft.Json.JsonPropertyAttribute;
+using Newtonsoft.Json;
using PepperDash.Core.Logging;
-using Required = NewtonsoftJson::Newtonsoft.Json.Required;
-namespace PepperDash.Core;
-
-///
-/// Generic UDP Server device
-///
-public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
+namespace PepperDash.Core
{
- private const string SplusKey = "Uninitialized Udp Server";
///
- /// Object to enable stream debugging
+ /// Generic UDP Server device
///
- public CommunicationStreamDebugging StreamDebugging { get; private set; }
- ///
- ///
- ///
- public event EventHandler BytesReceived;
+ public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
+ {
+ private const string SplusKey = "Uninitialized Udp Server";
+ ///
+ /// Object to enable stream debugging
+ ///
+ public CommunicationStreamDebugging StreamDebugging { get; private set; }
+ ///
+ ///
+ ///
+ public event EventHandler BytesReceived;
- ///
- ///
- ///
- public event EventHandler TextReceived;
+ ///
+ ///
+ ///
+ public event EventHandler TextReceived;
- ///
- /// This event will fire when a message is dequeued that includes the source IP and Port info if needed to determine the source of the received data.
- ///
+ ///
+ /// This event will fire when a message is dequeued that includes the source IP and Port info if needed to determine the source of the received data.
+ ///
public event EventHandler DataRecievedExtra;
- ///
- ///
- ///
- public event EventHandler ConnectionChange;
+ ///
+ ///
+ ///
+ public event EventHandler ConnectionChange;
- ///
- ///
- ///
- public event EventHandler UpdateConnectionStatus;
+ ///
+ ///
+ ///
+ public event EventHandler UpdateConnectionStatus;
- ///
- ///
- ///
- public SocketStatus ClientStatus
- {
- get
+ ///
+ ///
+ ///
+ public SocketStatus ClientStatus
{
- return Server.ServerStatus;
+ get
+ {
+ return Server.ServerStatus;
+ }
}
+
+ ///
+ ///
+ ///
+ public ushort UStatus
+ {
+ get { return (ushort)Server.ServerStatus; }
+ }
+
+ ///
+ /// Address of server
+ ///
+ public string Hostname { get; set; }
+
+
+ ///
+ /// Port on server
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// Another damn S+ helper because S+ seems to treat large port nums as signed ints
+ /// which screws up things
+ ///
+ public ushort UPort
+ {
+ get { return Convert.ToUInt16(Port); }
+ set { Port = Convert.ToInt32(value); }
+ }
+
+ ///
+ /// Indicates that the UDP Server is enabled
+ ///
+ public bool IsConnected
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// Numeric value indicating
+ ///
+ public ushort UIsConnected
+ {
+ get { return IsConnected ? (ushort)1 : (ushort)0; }
+ }
+
+ ///
+ /// Defaults to 2000
+ ///
+ public int BufferSize { get; set; }
+
+ ///
+ /// The server
+ ///
+ public UDPServer Server { get; private set; }
+
+ ///
+ /// Constructor for S+. Make sure to set key, address, port, and buffersize using init method
+ ///
+ public GenericUdpServer()
+ : base(SplusKey)
+ {
+ StreamDebugging = new CommunicationStreamDebugging(SplusKey);
+ BufferSize = 5000;
+
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public GenericUdpServer(string key, string address, int port, int bufferSize)
+ : base(key)
+ {
+ StreamDebugging = new CommunicationStreamDebugging(key);
+ Hostname = address;
+ Port = port;
+ BufferSize = bufferSize;
+
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
+ }
+
+ ///
+ /// Call from S+ to initialize values
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// Initialize method
+ ///
+ public void Initialize(string key, string address, ushort port)
+ {
+ Key = key;
+ Hostname = address;
+ UPort = port;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
+ {
+ // Re-enable the server if the link comes back up and the status should be connected
+ if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp
+ && IsConnected)
+ {
+ Connect();
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType != eProgramStatusEventType.Stopping)
+ return;
+
+ Debug.Console(1, this, "Program stopping. Disabling Server");
+ Disconnect();
+ }
+
+ ///
+ /// Connect method
+ ///
+ public void Connect()
+ {
+ if (Server == null)
+ {
+ try
+ {
+ var address = IPAddress.Parse(Hostname);
+
+ Server = new UDPServer(address, Port, BufferSize);
+
+ }
+ catch (Exception ex)
+ {
+ this.LogError("Error parsing IP Address '{ipAddress}': message: {message}", Hostname, ex.Message);
+ this.LogInformation("Creating UDPServer with default buffersize");
+
+ Server = new UDPServer();
+ }
+
+ }
+
+ if (string.IsNullOrEmpty(Hostname))
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': No address set", Key);
+ return;
+ }
+ if (Port < 1 || Port > 65535)
+ {
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': Invalid port", Key);
+ return;
+ }
+ }
+
+ var status = Server.EnableUDPServer(Hostname, Port);
+
+ Debug.Console(2, this, "SocketErrorCode: {0}", status);
+ if (status == SocketErrorCodes.SOCKET_OK)
+ IsConnected = true;
+
+ var handler = UpdateConnectionStatus;
+ if (handler != null)
+ handler(this, new GenericUdpConnectedEventArgs(UIsConnected));
+
+ // Start receiving data
+ Server.ReceiveDataAsync(Receive);
+ }
+
+ ///
+ /// Disconnect method
+ ///
+ public void Disconnect()
+ {
+ if (Server != null)
+ Server.DisableUDPServer();
+
+ IsConnected = false;
+
+ var handler = UpdateConnectionStatus;
+ if (handler != null)
+ handler(this, new GenericUdpConnectedEventArgs(UIsConnected));
+ }
+
+
+ ///
+ /// Recursive method to receive data
+ ///
+ ///
+ ///
+ void Receive(UDPServer server, int numBytes)
+ {
+ Debug.Console(2, this, "Received {0} bytes", numBytes);
+
+ try
+ {
+ if (numBytes <= 0)
+ return;
+
+ var sourceIp = Server.IPAddressLastMessageReceivedFrom;
+ var sourcePort = Server.IPPortLastMessageReceivedFrom;
+ var bytes = server.IncomingDataBuffer.Take(numBytes).ToArray();
+ var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
+
+ var dataRecivedExtra = DataRecievedExtra;
+ if (dataRecivedExtra != null)
+ dataRecivedExtra(this, new GenericUdpReceiveTextExtraArgs(str, sourceIp, sourcePort, bytes));
+
+ Debug.Console(2, this, "Bytes: {0}", bytes.ToString());
+ var bytesHandler = BytesReceived;
+ if (bytesHandler != null)
+ {
+ this.PrintReceivedBytes(bytes);
+ bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
+ }
+ var textHandler = TextReceived;
+ if (textHandler != null)
+ {
+ this.PrintReceivedText(str);
+ textHandler(this, new GenericCommMethodReceiveTextArgs(str));
+ }
+ }
+ catch (Exception ex)
+ {
+ this.LogException(ex, "GenericUdpServer Receive error");
+ }
+ finally
+ {
+ server.ReceiveDataAsync(Receive);
+ }
+ }
+
+ ///
+ /// General send method
+ ///
+ ///
+ ///
+ /// SendText method
+ ///
+ public void SendText(string text)
+ {
+ var bytes = Encoding.GetEncoding(28591).GetBytes(text);
+
+ if (IsConnected && Server != null)
+ {
+ this.PrintSentText(text);
+
+ Server.SendData(bytes, bytes.Length);
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// SendBytes method
+ ///
+ public void SendBytes(byte[] bytes)
+ {
+ this.PrintSentBytes(bytes);
+
+ if (IsConnected && Server != null)
+ Server.SendData(bytes, bytes.Length);
+ }
+
}
///
/// Represents a GenericUdpReceiveTextExtraArgs
///
- public ushort UStatus
+ public class GenericUdpReceiveTextExtraArgs : EventArgs
{
- get { return (ushort)Server.ServerStatus; }
- }
-
- ///
- /// Address of server
- ///
- public string Hostname { get; set; }
-
-
- ///
- /// Port on server
- ///
- public int Port { get; set; }
-
- ///
- /// Another damn S+ helper because S+ seems to treat large port nums as signed ints
- /// which screws up things
- ///
- public ushort UPort
- {
- get { return Convert.ToUInt16(Port); }
- set { Port = Convert.ToInt32(value); }
- }
-
- ///
- /// Indicates that the UDP Server is enabled
- ///
- public bool IsConnected
- {
- get;
- private set;
- }
-
- ///
- /// Numeric value indicating
- ///
- public ushort UIsConnected
- {
- get { return IsConnected ? (ushort)1 : (ushort)0; }
- }
-
- ///
- /// Defaults to 2000
- ///
- public int BufferSize { get; set; }
-
- ///
- /// The server
- ///
- public UDPServer Server { get; private set; }
-
- ///
- /// Constructor for S+. Make sure to set key, address, port, and buffersize using init method
- ///
- public GenericUdpServer()
- : base(SplusKey)
- {
- StreamDebugging = new CommunicationStreamDebugging(SplusKey);
- BufferSize = 5000;
-
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
- }
-
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public GenericUdpServer(string key, string address, int port, int buffefSize)
- : base(key)
- {
- StreamDebugging = new CommunicationStreamDebugging(key);
- Hostname = address;
- Port = port;
- BufferSize = buffefSize;
-
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
- }
-
- ///
- /// Call from S+ to initialize values
- ///
- ///
- ///
- ///
- public void Initialize(string key, string address, ushort port)
- {
- Key = key;
- Hostname = address;
- UPort = port;
- }
-
- ///
- ///
- ///
- ///
- void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
- {
- // Re-enable the server if the link comes back up and the status should be connected
- if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp
- && IsConnected)
- {
- Connect();
- }
- }
-
- ///
- ///
- ///
- ///
- void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
- {
- if (programEventType != eProgramStatusEventType.Stopping)
- return;
-
- this.LogInformation("Program stopping. Disabling Server");
- Disconnect();
- }
-
- ///
- /// Enables the UDP Server
- ///
- public void Connect()
- {
- if (Server == null)
- {
- Server = new UDPServer();
- }
-
- if (string.IsNullOrEmpty(Hostname))
- {
- this.LogWarning("GenericUdpServer '{0}': No address set", Key);
- return;
- }
- if (Port < 1 || Port > 65535)
- {
- {
- this.LogWarning("GenericUdpServer '{0}': Invalid port", Key);
- return;
- }
- }
-
- var status = Server.EnableUDPServer(Hostname, Port);
-
- this.LogVerbose("SocketErrorCode: {0}", status);
- if (status == SocketErrorCodes.SOCKET_OK)
- IsConnected = true;
-
- var handler = UpdateConnectionStatus;
- if (handler != null)
- handler(this, new GenericUdpConnectedEventArgs(UIsConnected));
-
- // Start receiving data
- Server.ReceiveDataAsync(Receive);
- }
-
- ///
- /// Disabled the UDP Server
- ///
- public void Disconnect()
- {
- if(Server != null)
- Server.DisableUDPServer();
-
- IsConnected = false;
-
- var handler = UpdateConnectionStatus;
- if (handler != null)
- handler(this, new GenericUdpConnectedEventArgs(UIsConnected));
- }
-
-
- ///
- /// Recursive method to receive data
- ///
- ///
- ///
- void Receive(UDPServer server, int numBytes)
- {
- this.LogVerbose("Received {0} bytes", numBytes);
-
- try
- {
- if (numBytes <= 0)
- return;
-
- var sourceIp = Server.IPAddressLastMessageReceivedFrom;
- var sourcePort = Server.IPPortLastMessageReceivedFrom;
- var bytes = server.IncomingDataBuffer.Take(numBytes).ToArray();
- var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
-
- var dataRecivedExtra = DataRecievedExtra;
- if (dataRecivedExtra != null)
- dataRecivedExtra(this, new GenericUdpReceiveTextExtraArgs(str, sourceIp, sourcePort, bytes));
-
- this.LogVerbose("Bytes: {0}", bytes.ToString());
- var bytesHandler = BytesReceived;
- if (bytesHandler != null)
- {
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- {
- this.LogInformation("Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
- }
- bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
- }
- var textHandler = TextReceived;
- if (textHandler != null)
- {
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- this.LogInformation("Received {1} characters of text: '{0}'", ComTextHelper.GetDebugText(str), str.Length);
- textHandler(this, new GenericCommMethodReceiveTextArgs(str));
- }
- }
- catch (Exception ex)
- {
- this.LogException(ex, "GenericUdpServer Receive error");
- }
- finally
- {
- server.ReceiveDataAsync(Receive);
- }
- }
-
- ///
- /// General send method
- ///
- ///
- public void SendText(string text)
- {
- var bytes = Encoding.GetEncoding(28591).GetBytes(text);
-
- if (IsConnected && Server != null)
- {
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- this.LogVerbose("Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
-
- Server.SendData(bytes, bytes.Length);
- }
- }
-
- ///
- ///
- ///
- ///
- public void SendBytes(byte[] bytes)
- {
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- this.LogInformation("Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
-
- if (IsConnected && Server != null)
- Server.SendData(bytes, bytes.Length);
- }
-
-}
-
-///
-///
-///
- public class GenericUdpReceiveTextExtraArgs : EventArgs
- {
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string Text { get; private set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string IpAddress { get; private set; }
- ///
- ///
- ///
- public int Port { get; private set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
+ public int Port { get; private set; }
+ ///
+ ///
+ ///
public byte[] Bytes { get; private set; }
- ///
- ///
- ///
- ///
- ///
- ///
- ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public GenericUdpReceiveTextExtraArgs(string text, string ipAddress, int port, byte[] bytes)
{
Text = text;
@@ -366,33 +380,34 @@ public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
public GenericUdpReceiveTextExtraArgs() { }
}
-///
-///
-///
-public class UdpServerPropertiesConfig
-{
///
///
///
- [JsonProperty(Required = Required.Always)]
- public string Address { get; set; }
-
- ///
- ///
- ///
- [JsonProperty(Required = Required.Always)]
- public int Port { get; set; }
-
- ///
- /// Defaults to 32768
- ///
- public int BufferSize { get; set; }
-
- ///
- ///
- ///
- public UdpServerPropertiesConfig()
+ public class UdpServerPropertiesConfig
{
- BufferSize = 32768;
+ ///
+ ///
+ ///
+ [JsonProperty(Required = Required.Always)]
+ public string Address { get; set; }
+
+ ///
+ ///
+ ///
+ [JsonProperty(Required = Required.Always)]
+ public int Port { get; set; }
+
+ ///
+ /// Defaults to 32768
+ ///
+ public int BufferSize { get; set; }
+
+ ///
+ ///
+ ///
+ public UdpServerPropertiesConfig()
+ {
+ BufferSize = 32768;
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Comm/TcpClientConfigObject.cs b/src/PepperDash.Core/Comm/TcpClientConfigObject.cs
index 2aec5af8..226473bf 100644
--- a/src/PepperDash.Core/Comm/TcpClientConfigObject.cs
+++ b/src/PepperDash.Core/Comm/TcpClientConfigObject.cs
@@ -1,60 +1,59 @@
-extern alias NewtonsoftJson;
+using Newtonsoft.Json;
-using JsonProperty = NewtonsoftJson::Newtonsoft.Json.JsonPropertyAttribute;
-
-namespace PepperDash.Core;
-
-///
-/// Client config object for TCP client with server that inherits from TcpSshPropertiesConfig and adds properties for shared key and heartbeat
-///
-public class TcpClientConfigObject
+namespace PepperDash.Core
{
///
- /// TcpSsh Properties
+ /// Represents a TcpClientConfigObject
///
- [JsonProperty("control")]
- public ControlPropertiesConfig Control { get; set; }
+ public class TcpClientConfigObject
+ {
+ ///
+ /// TcpSsh Properties
+ ///
+ [JsonProperty("control")]
+ public ControlPropertiesConfig Control { get; set; }
- ///
- /// Bool value for secure. Currently not implemented in TCP sockets as they are not dynamic
- ///
- [JsonProperty("secure")]
- public bool Secure { get; set; }
+ ///
+ /// Bool value for secure. Currently not implemented in TCP sockets as they are not dynamic
+ ///
+ [JsonProperty("secure")]
+ public bool Secure { get; set; }
- ///
- /// Require a shared key that both server and client negotiate. If negotiation fails server disconnects the client
- ///
- [JsonProperty("sharedKeyRequired")]
- public bool SharedKeyRequired { get; set; }
+ ///
+ /// Require a shared key that both server and client negotiate. If negotiation fails server disconnects the client
+ ///
+ [JsonProperty("sharedKeyRequired")]
+ public bool SharedKeyRequired { get; set; }
- ///
- /// The shared key that must match on the server and client
- ///
- [JsonProperty("sharedKey")]
- public string SharedKey { get; set; }
+ ///
+ /// The shared key that must match on the server and client
+ ///
+ [JsonProperty("sharedKey")]
+ public string SharedKey { get; set; }
- ///
- /// Require a heartbeat on the client/server connection that will cause the server/client to disconnect if the heartbeat is not received.
- /// heartbeats do not raise received events.
- ///
- [JsonProperty("heartbeatRequired")]
- public bool HeartbeatRequired { get; set; }
+ ///
+ /// Require a heartbeat on the client/server connection that will cause the server/client to disconnect if the heartbeat is not received.
+ /// heartbeats do not raise received events.
+ ///
+ [JsonProperty("heartbeatRequired")]
+ public bool HeartbeatRequired { get; set; }
- ///
- /// The interval in seconds for the heartbeat from the client. If not received client is disconnected
- ///
- [JsonProperty("heartbeatRequiredIntervalInSeconds")]
- public ushort HeartbeatRequiredIntervalInSeconds { get; set; }
+ ///
+ /// The interval in seconds for the heartbeat from the client. If not received client is disconnected
+ ///
+ [JsonProperty("heartbeatRequiredIntervalInSeconds")]
+ public ushort HeartbeatRequiredIntervalInSeconds { get; set; }
- ///
- /// HeartbeatString that will be checked against the message received. defaults to heartbeat if no string is provided.
- ///
- [JsonProperty("heartbeatStringToMatch")]
- public string HeartbeatStringToMatch { get; set; }
+ ///
+ /// HeartbeatString that will be checked against the message received. defaults to heartbeat if no string is provided.
+ ///
+ [JsonProperty("heartbeatStringToMatch")]
+ public string HeartbeatStringToMatch { get; set; }
- ///
- /// Receive Queue size must be greater than 20 or defaults to 20
- ///
- [JsonProperty("receiveQueueSize")]
- public int ReceiveQueueSize { get; set; }
+ ///
+ /// Receive Queue size must be greater than 20 or defaults to 20
+ ///
+ [JsonProperty("receiveQueueSize")]
+ public int ReceiveQueueSize { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Comm/TcpServerConfigObject.cs b/src/PepperDash.Core/Comm/TcpServerConfigObject.cs
index b60f486f..043cf58d 100644
--- a/src/PepperDash.Core/Comm/TcpServerConfigObject.cs
+++ b/src/PepperDash.Core/Comm/TcpServerConfigObject.cs
@@ -4,56 +4,57 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Core;
-
-///
-/// Tcp Server Config object with properties for a tcp server with shared key and heartbeat capabilities
-///
-public class TcpServerConfigObject
+namespace PepperDash.Core
{
///
- /// Uique key
+ /// Tcp Server Config object with properties for a tcp server with shared key and heartbeat capabilities
///
- public string Key { get; set; }
- ///
- /// Max Clients that the server will allow to connect.
- ///
- public ushort MaxClients { get; set; }
- ///
- /// Bool value for secure. Currently not implemented in TCP sockets as they are not dynamic
- ///
- public bool Secure { get; set; }
- ///
- /// Port for the server to listen on
- ///
- public int Port { get; set; }
- ///
- /// Require a shared key that both server and client negotiate. If negotiation fails server disconnects the client
- ///
- public bool SharedKeyRequired { get; set; }
- ///
- /// The shared key that must match on the server and client
- ///
- public string SharedKey { get; set; }
- ///
- /// Require a heartbeat on the client/server connection that will cause the server/client to disconnect if the heartbeat is not received.
- /// heartbeats do not raise received events.
- ///
- public bool HeartbeatRequired { get; set; }
- ///
- /// The interval in seconds for the heartbeat from the client. If not received client is disconnected
- ///
- public ushort HeartbeatRequiredIntervalInSeconds { get; set; }
- ///
- /// HeartbeatString that will be checked against the message received. defaults to heartbeat if no string is provided.
- ///
- public string HeartbeatStringToMatch { get; set; }
- ///
- /// Client buffer size. See Crestron help. defaults to 2000 if not greater than 2000
- ///
- public int BufferSize { get; set; }
- ///
- /// Receive Queue size must be greater than 20 or defaults to 20
- ///
- public int ReceiveQueueSize { get; set; }
+ public class TcpServerConfigObject
+ {
+ ///
+ /// Uique key
+ ///
+ public string Key { get; set; }
+ ///
+ /// Max Clients that the server will allow to connect.
+ ///
+ public ushort MaxClients { get; set; }
+ ///
+ /// Bool value for secure. Currently not implemented in TCP sockets as they are not dynamic
+ ///
+ public bool Secure { get; set; }
+ ///
+ /// Port for the server to listen on
+ ///
+ public int Port { get; set; }
+ ///
+ /// Require a shared key that both server and client negotiate. If negotiation fails server disconnects the client
+ ///
+ public bool SharedKeyRequired { get; set; }
+ ///
+ /// The shared key that must match on the server and client
+ ///
+ public string SharedKey { get; set; }
+ ///
+ /// Require a heartbeat on the client/server connection that will cause the server/client to disconnect if the heartbeat is not received.
+ /// heartbeats do not raise received events.
+ ///
+ public bool HeartbeatRequired { get; set; }
+ ///
+ /// The interval in seconds for the heartbeat from the client. If not received client is disconnected
+ ///
+ public ushort HeartbeatRequiredIntervalInSeconds { get; set; }
+ ///
+ /// HeartbeatString that will be checked against the message received. defaults to heartbeat if no string is provided.
+ ///
+ public string HeartbeatStringToMatch { get; set; }
+ ///
+ /// Client buffer size. See Crestron help. defaults to 2000 if not greater than 2000
+ ///
+ public int BufferSize { get; set; }
+ ///
+ /// Receive Queue size must be greater than 20 or defaults to 20
+ ///
+ public int ReceiveQueueSize { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Comm/eControlMethods.cs b/src/PepperDash.Core/Comm/eControlMethods.cs
index db1bcb19..a1a30734 100644
--- a/src/PepperDash.Core/Comm/eControlMethods.cs
+++ b/src/PepperDash.Core/Comm/eControlMethods.cs
@@ -4,83 +4,89 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Core;
-
-///
-/// Crestron Control Methods for a comm object
-///
-public enum eControlMethod
+namespace PepperDash.Core
{
///
- ///
+ /// Crestron Control Methods for a comm object
///
- None = 0,
- ///
- /// RS232/422/485
- ///
- Com,
- ///
- /// Crestron IpId (most Crestron ethernet devices)
- ///
- IpId,
- ///
- /// Crestron IpIdTcp (HD-MD series, etc.)
- ///
- IpidTcp,
- ///
- /// Crestron IR control
- ///
- IR,
- ///
- /// SSH client
- ///
- Ssh,
- ///
- /// TCP/IP client
- ///
- Tcpip,
- ///
- /// Telnet
- ///
- Telnet,
- ///
- /// Crestnet device
- ///
- Cresnet,
- ///
- /// CEC Control, via a DM HDMI port
- ///
- Cec,
- ///
- /// UDP Server
- ///
- Udp,
- ///
- /// HTTP client
- ///
- Http,
- ///
- /// HTTPS client
- ///
- Https,
- ///
- /// Websocket client
- ///
- Ws,
- ///
- /// Secure Websocket client
- ///
- Wss,
- ///
- /// Secure TCP/IP
- ///
- SecureTcpIp,
- ///
- /// Crestron COM bridge
- ///
- ComBridge,
- ///
- /// Crestron Infinet EX device
- ///
- InfinetEx
+ public enum eControlMethod
+ {
+ ///
+ ///
+ ///
+ None = 0,
+ ///
+ /// RS232/422/485
+ ///
+ Com = 1,
+ ///
+ /// Crestron IpId (most Crestron ethernet devices)
+ ///
+ IpId = 2,
+ ///
+ /// Crestron IpIdTcp (HD-MD series, etc.)
+ ///
+ IpidTcp = 3,
+ ///
+ /// Crestron IR control
+ ///
+ IR = 4,
+ ///
+ /// SSH client
+ ///
+ Ssh = 5,
+ ///
+ /// TCP/IP client
+ ///
+ Tcpip = 6,
+ ///
+ /// Telnet
+ ///
+ Telnet = 7,
+ ///
+ /// Crestnet device
+ ///
+ Cresnet = 8,
+ ///
+ /// CEC Control, via a DM HDMI port
+ ///
+ Cec = 9,
+ ///
+ /// UDP Server
+ ///
+ Udp = 10,
+
+ ///
+ /// HTTP client
+ ///
+ Http = 11,
+ ///
+ /// HTTPS client
+ ///
+ Https = 12,
+ ///
+ /// Websocket client
+ ///
+ Ws = 13,
+ ///
+ /// Secure Websocket client
+ ///
+ Wss = 14,
+ ///
+ /// Secure TCP/IP
+ ///
+ SecureTcpIp = 15,
+ ///
+ /// Used when comms needs to be handled in SIMPL and bridged opposite the normal direction
+ ///
+ ComBridge = 16,
+ ///
+ /// InfinetEX control
+ ///
+ InfinetEx = 17,
+ ///
+ /// UDP client
+ ///
+ UdpClient = 18,
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Comm/eStreamDebuggingSetting.cs b/src/PepperDash.Core/Comm/eStreamDebuggingSetting.cs
new file mode 100644
index 00000000..f9f7eb3f
--- /dev/null
+++ b/src/PepperDash.Core/Comm/eStreamDebuggingSetting.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace PepperDash.Core
+{
+ ///
+ /// The available settings for stream debugging
+ ///
+ [Flags]
+ public enum eStreamDebuggingSetting
+ {
+ ///
+ /// Debug off
+ ///
+ Off = 0,
+ ///
+ /// Debug received data
+ ///
+ Rx = 1,
+ ///
+ /// Debug transmitted data
+ ///
+ Tx = 2,
+ ///
+ /// Debug both received and transmitted data
+ ///
+ Both = Rx | Tx
+ }
+}
diff --git a/src/PepperDash.Core/CommunicationExtras.cs b/src/PepperDash.Core/CommunicationExtras.cs
index 04e36982..a8bc57d3 100644
--- a/src/PepperDash.Core/CommunicationExtras.cs
+++ b/src/PepperDash.Core/CommunicationExtras.cs
@@ -1,143 +1,136 @@
-extern alias NewtonsoftJson;
-
-using System;
+using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronSockets;
-using JsonConverter = NewtonsoftJson::Newtonsoft.Json.JsonConverterAttribute;
-using JsonProperty = NewtonsoftJson::Newtonsoft.Json.JsonPropertyAttribute;
-using StringEnumConverter = NewtonsoftJson::Newtonsoft.Json.Converters.StringEnumConverter;
+using Newtonsoft.Json;
-namespace PepperDash.Core;
-
-///
-/// An incoming communication stream
-///
-public interface ICommunicationReceiver : IKeyed
+namespace PepperDash.Core
{
///
- /// Notifies of bytes received
+ /// An incoming communication stream
///
- event EventHandler BytesReceived;
- ///
- /// Notifies of text received
- ///
- event EventHandler TextReceived;
+ public interface ICommunicationReceiver : IKeyed
+ {
+ ///
+ /// Notifies of bytes received
+ ///
+ event EventHandler BytesReceived;
+ ///
+ /// Notifies of text received
+ ///
+ event EventHandler TextReceived;
+
+ ///
+ /// Indicates connection status
+ ///
+ [JsonProperty("isConnected")]
+ bool IsConnected { get; }
+ ///
+ /// Connect to the device
+ ///
+ void Connect();
+ ///
+ /// Disconnect from the device
+ ///
+ void Disconnect();
+ }
///
- /// Indicates connection status
+ /// Defines the contract for IBasicCommunication
///
- [JsonProperty("isConnected")]
- bool IsConnected { get; }
- ///
- /// Connect to the device
- ///
- void Connect();
- ///
- /// Disconnect from the device
- ///
- void Disconnect();
-}
-
- ///
- /// Extends with methods for sending text and bytes to a device.
- ///
-public interface IBasicCommunication : ICommunicationReceiver
- {
- ///
- /// Send text to the device
- ///
- ///
+ public interface IBasicCommunication : ICommunicationReceiver
+ {
+ ///
+ /// Send text to the device
+ ///
+ ///
void SendText(string text);
- ///
- /// Send bytes to the device
- ///
- ///
+ ///
+ /// Send bytes to the device
+ ///
+ ///
void SendBytes(byte[] bytes);
}
-///
-/// Represents a device that implements IBasicCommunication and IStreamDebugging
-///
-public interface IBasicCommunicationWithStreamDebugging : IBasicCommunication, IStreamDebugging
-{
-
-}
-
-///
-/// Represents a device with stream debugging capablities
-///
-public interface IStreamDebugging : IKeyed
-{
///
- /// Object to enable stream debugging
+ /// Represents a device that implements IBasicCommunication and IStreamDebugging
///
- [JsonProperty("streamDebugging")]
- CommunicationStreamDebugging StreamDebugging { get; }
-}
+ public interface IBasicCommunicationWithStreamDebugging : IBasicCommunication, IStreamDebugging
+ {
+
+ }
- ///
- /// For IBasicCommunication classes that have SocketStatus. GenericSshClient,
- /// GenericTcpIpClient
- ///
- public interface ISocketStatus : IBasicCommunication
- {
///
- /// Notifies of socket status changes
+ /// Represents a device with stream debugging capablities
///
+ public interface IStreamDebugging : IKeyed
+ {
+ ///
+ /// Object to enable stream debugging
+ ///
+ [JsonProperty("streamDebugging")]
+ CommunicationStreamDebugging StreamDebugging { get; }
+ }
+
+ ///
+ /// For IBasicCommunication classes that have SocketStatus. GenericSshClient,
+ /// GenericTcpIpClient
+ ///
+ public interface ISocketStatus : IBasicCommunication
+ {
+ ///
+ /// Notifies of socket status changes
+ ///
event EventHandler ConnectionChange;
+ ///
+ /// The current socket status of the client
+ ///
+ [JsonProperty("clientStatus")]
+ [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
+ SocketStatus ClientStatus { get; }
+ }
+
///
- /// The current socket status of the client
+ /// Describes a device that implements ISocketStatus and IStreamDebugging
///
- [JsonProperty("clientStatus")]
- [JsonConverter(typeof(StringEnumConverter))]
- SocketStatus ClientStatus { get; }
- }
+ public interface ISocketStatusWithStreamDebugging : ISocketStatus, IStreamDebugging
+ {
-///
-/// Describes a device that implements ISocketStatus and IStreamDebugging
-///
-public interface ISocketStatusWithStreamDebugging : ISocketStatus, IStreamDebugging
-{
+ }
-}
-
-///
-/// Describes a device that can automatically attempt to reconnect
-///
+ ///
+ /// Describes a device that can automatically attempt to reconnect
+ ///
public interface IAutoReconnect
- {
- ///
- /// Enable automatic recconnect
- ///
- [JsonProperty("autoReconnect")]
- bool AutoReconnect { get; set; }
- ///
- /// Interval in ms to attempt automatic recconnections
- ///
- [JsonProperty("autoReconnectIntervalMs")]
- int AutoReconnectIntervalMs { get; set; }
- }
+ {
+ ///
+ /// Enable automatic recconnect
+ ///
+ [JsonProperty("autoReconnect")]
+ bool AutoReconnect { get; set; }
+ ///
+ /// Interval in ms to attempt automatic recconnections
+ ///
+ [JsonProperty("autoReconnectIntervalMs")]
+ int AutoReconnectIntervalMs { get; set; }
+ }
- ///
- ///
- ///
- public enum eGenericCommMethodStatusChangeType
- {
///
- /// Connected
+ ///
///
+ public enum eGenericCommMethodStatusChangeType
+ {
+ ///
+ /// Connected
+ ///
Connected,
- ///
- /// Disconnected
- ///
- Disconnected
- }
+ ///
+ /// Disconnected
+ ///
+ Disconnected
+ }
///
/// This delegate defines handler for IBasicCommunication status changes
@@ -146,20 +139,20 @@ public interface ISocketStatusWithStreamDebugging : ISocketStatus, IStreamDebugg
///
public delegate void GenericCommMethodStatusHandler(IBasicCommunication comm, eGenericCommMethodStatusChangeType status);
- ///
- /// Event args for bytes received from a communication method
- ///
- public class GenericCommMethodReceiveBytesArgs : EventArgs
- {
///
- /// The bytes received
+ ///
///
- public byte[] Bytes { get; private set; }
+ public class GenericCommMethodReceiveBytesArgs : EventArgs
+ {
+ ///
+ /// Gets or sets the Bytes
+ ///
+ public byte[] Bytes { get; private set; }
- ///
- /// Constructor
- ///
- ///
+ ///
+ ///
+ ///
+ ///
public GenericCommMethodReceiveBytesArgs(byte[] bytes)
{
Bytes = bytes;
@@ -171,81 +164,42 @@ public interface ISocketStatusWithStreamDebugging : ISocketStatus, IStreamDebugg
public GenericCommMethodReceiveBytesArgs() { }
}
- ///
- /// Event args for text received
- ///
- public class GenericCommMethodReceiveTextArgs : EventArgs
- {
///
- /// The text received
+ ///
///
+ public class GenericCommMethodReceiveTextArgs : EventArgs
+ {
+ ///
+ ///
+ ///
public string Text { get; private set; }
- ///
- /// The delimiter used to determine the end of a message, if applicable
- ///
- public string Delimiter { get; private set; }
-
- ///
- /// Constructor
- ///
- ///
+ ///
+ ///
+ ///
+ public string Delimiter { get; private set; }
+ ///
+ ///
+ ///
+ ///
public GenericCommMethodReceiveTextArgs(string text)
{
Text = text;
}
- ///
- ///
- ///
- ///
- ///
- public GenericCommMethodReceiveTextArgs(string text, string delimiter)
- :this(text)
- {
- Delimiter = delimiter;
+ ///
+ ///
+ ///
+ ///
+ ///
+ public GenericCommMethodReceiveTextArgs(string text, string delimiter)
+ : this(text)
+ {
+ Delimiter = delimiter;
+ }
+
+ ///
+ /// S+ Constructor
+ ///
+ public GenericCommMethodReceiveTextArgs() { }
}
-
- ///
- /// S+ Constructor
- ///
- public GenericCommMethodReceiveTextArgs() { }
- }
-
-
-
- ///
- /// Helper class to get escaped text for debugging communication streams
- ///
- public class ComTextHelper
- {
- ///
- /// Gets escaped text for a byte array
- ///
- ///
- ///
- public static string GetEscapedText(byte[] bytes)
- {
- return String.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
- }
-
- ///
- /// Gets escaped text for a string
- ///
- ///
- ///
- public static string GetEscapedText(string text)
- {
- var bytes = Encoding.GetEncoding(28591).GetBytes(text);
- return String.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
- }
-
- ///
- /// Gets debug text for a string
- ///
- ///
- ///
- public static string GetDebugText(string text)
- {
- return Regex.Replace(text, @"[^\u0020-\u007E]", a => GetEscapedText(a.Value));
- }
- }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Config/PortalConfigReader.cs b/src/PepperDash.Core/Config/PortalConfigReader.cs
index 1e24658c..41ca2cbd 100644
--- a/src/PepperDash.Core/Config/PortalConfigReader.cs
+++ b/src/PepperDash.Core/Config/PortalConfigReader.cs
@@ -1,21 +1,19 @@
-extern alias NewtonsoftJson;
-
-using System;
+using System;
using System.Linq;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
-using Formatting = NewtonsoftJson::Newtonsoft.Json.Formatting;
-using JArray = NewtonsoftJson::Newtonsoft.Json.Linq.JArray;
-using JObject = NewtonsoftJson::Newtonsoft.Json.Linq.JObject;
-using JToken = NewtonsoftJson::Newtonsoft.Json.Linq.JToken;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
using PepperDash.Core;
using Serilog.Events;
-namespace PepperDash.Core.Config;
+namespace PepperDash.Core.Config
+{
-///
-/// Reads a Portal formatted config file
-///
+
+ ///
+ /// Reads a Portal formatted config file
+ ///
public class PortalConfigReader
{
const string template = "template";
@@ -58,12 +56,12 @@ namespace PepperDash.Core.Config;
var merged = MergeConfigs(jsonObj);
if (jsonObj[systemUrl] != null)
{
- merged[systemUrl] = (string)jsonObj[systemUrl];
+ merged[systemUrl] = jsonObj[systemUrl].Value();
}
if (jsonObj[templateUrl] != null)
{
- merged[templateUrl] = (string)jsonObj[templateUrl];
+ merged[templateUrl] = jsonObj[templateUrl].Value();
}
jsonObj = merged;
@@ -124,37 +122,38 @@ namespace PepperDash.Core.Config;
Merge(template[destinationLists], system[destinationLists], destinationLists));
- if (system["cameraLists"] == null)
- merged.Add("cameraLists", template["cameraLists"]);
- else
- merged.Add("cameraLists", Merge(template["cameraLists"], system["cameraLists"], "cameraLists"));
+ if (system[cameraLists] == null)
+ merged.Add(cameraLists, template[cameraLists]);
+ else
+ merged.Add(cameraLists, Merge(template[cameraLists], system[cameraLists], cameraLists));
- if (system["audioControlPointLists"] == null)
- merged.Add("audioControlPointLists", template["audioControlPointLists"]);
- else
- merged.Add("audioControlPointLists",
- Merge(template["audioControlPointLists"], system["audioControlPointLists"], "audioControlPointLists"));
+ if (system[audioControlPointLists] == null)
+ merged.Add(audioControlPointLists, template[audioControlPointLists]);
+ else
+ merged.Add(audioControlPointLists,
+ Merge(template[audioControlPointLists], system[audioControlPointLists], audioControlPointLists));
- // Template tie lines take precedence. Config tool doesn't do them at system
- // level anyway...
- if (template["tieLines"] != null)
- merged.Add("tieLines", template["tieLines"]);
- else if (system["tieLines"] != null)
- merged.Add("tieLines", system["tieLines"]);
+ // Template tie lines take precedence. Config tool doesn't do them at system
+ // level anyway...
+ if (template[tieLines] != null)
+ merged.Add(tieLines, template[tieLines]);
+ else if (system[tieLines] != null)
+ merged.Add(tieLines, system[tieLines]);
else
merged.Add(tieLines, new JArray());
- if (template["joinMaps"] != null)
- merged.Add("joinMaps", template["joinMaps"]);
- else
- merged.Add("joinMaps", new JObject());
+ if (template[joinMaps] != null)
+ merged.Add(joinMaps, template[joinMaps]);
+ else
+ merged.Add(joinMaps, new JObject());
if (system[global] != null)
merged.Add(global, Merge(template[global], system[global], global));
else
merged.Add(global, template[global]);
+ //Debug.Console(2, "MERGED CONFIG RESULT: \x0d\x0a{0}", merged);
return merged;
}
@@ -170,26 +169,26 @@ namespace PepperDash.Core.Config;
return a1;
else if (a1 != null)
{
- if (a2[0]["key"] == null) // If the first item in the system array has no key, overwrite the template array
- { // with the system array
- return a2;
- }
- else // The arrays are keyed, merge them by key
- {
- for (int i = 0; i < a1.Count(); i++)
- {
- var a1Dev = a1[i];
- // Try to get a system device and if found, merge it onto template
- var a2Match = a2.FirstOrDefault(t => t[propertyName].Equals(a1Dev[propertyName]));// t.Value("uid") == tmplDev.Value("uid"));
- if (a2Match != null)
- {
- var mergedItem = Merge(a1Dev, a2Match, string.Format("{0}[{1}].", path, i));// Merge(JObject.FromObject(a1Dev), JObject.FromObject(a2Match));
- result.Add(mergedItem);
- }
- else
- result.Add(a1Dev);
+ if (a2[0]["key"] == null) // If the first item in the system array has no key, overwrite the template array
+ { // with the system array
+ return a2;
+ }
+ else // The arrays are keyed, merge them by key
+ {
+ for (int i = 0; i < a1.Count(); i++)
+ {
+ var a1Dev = a1[i];
+ // Try to get a system device and if found, merge it onto template
+ var a2Match = a2.FirstOrDefault(t => t[propertyName].Equals(a1Dev[propertyName]));// t.Value("uid") == tmplDev.Value("uid"));
+ if (a2Match != null)
+ {
+ var mergedItem = Merge(a1Dev, a2Match, string.Format("{0}[{1}].", path, i));// Merge(JObject.FromObject(a1Dev), JObject.FromObject(a2Match));
+ result.Add(mergedItem);
+ }
+ else
+ result.Add(a1Dev);
+ }
}
- }
}
return result;
}
@@ -206,9 +205,9 @@ namespace PepperDash.Core.Config;
///
/// Merge o2 onto o1
///
- ///
- ///
- ///
+ ///
+ ///
+ ///
static JObject Merge(JObject o1, JObject o2, string path)
{
foreach (var o2Prop in o2)
@@ -254,4 +253,5 @@ namespace PepperDash.Core.Config;
}
return o1;
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Conversion/Convert.cs b/src/PepperDash.Core/Conversion/Convert.cs
index 13ef7263..49f8bb94 100644
--- a/src/PepperDash.Core/Conversion/Convert.cs
+++ b/src/PepperDash.Core/Conversion/Convert.cs
@@ -4,18 +4,28 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Core;
-
-public class EncodingHelper
+namespace PepperDash.Core
{
- public static string ConvertUtf8ToAscii(string utf8String)
+ ///
+ /// Represents a EncodingHelper
+ ///
+ public class EncodingHelper
{
- return Encoding.ASCII.GetString(Encoding.UTF8.GetBytes(utf8String), 0, utf8String.Length);
- }
+ ///
+ /// ConvertUtf8ToAscii method
+ ///
+ public static string ConvertUtf8ToAscii(string utf8String)
+ {
+ return Encoding.ASCII.GetString(Encoding.UTF8.GetBytes(utf8String), 0, utf8String.Length);
+ }
- public static string ConvertUtf8ToUtf16(string utf8String)
- {
- return Encoding.Unicode.GetString(Encoding.UTF8.GetBytes(utf8String), 0, utf8String.Length);
- }
+ ///
+ /// ConvertUtf8ToUtf16 method
+ ///
+ public static string ConvertUtf8ToUtf16(string utf8String)
+ {
+ return Encoding.Unicode.GetString(Encoding.UTF8.GetBytes(utf8String), 0, utf8String.Length);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/CoreInterfaces.cs b/src/PepperDash.Core/CoreInterfaces.cs
index 89e6cdad..6e0b639e 100644
--- a/src/PepperDash.Core/CoreInterfaces.cs
+++ b/src/PepperDash.Core/CoreInterfaces.cs
@@ -1,35 +1,35 @@
-extern alias NewtonsoftJson;
-
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-using JsonProperty = NewtonsoftJson::Newtonsoft.Json.JsonPropertyAttribute;
+using Newtonsoft.Json;
using Serilog;
-namespace PepperDash.Core;
-
-///
-/// Unique key interface to require a unique key for the class
-///
+namespace PepperDash.Core
+{
+ ///
+ /// Unique key interface to require a unique key for the class
+ ///
public interface IKeyed
{
- ///
- /// Gets the unique key associated with the object.
- ///
- [JsonProperty("key")]
+ ///
+ /// Unique Key
+ ///
+ [JsonProperty("key")]
string Key { get; }
-}
+ }
-///
-/// Named Keyed device interface. Forces the device to have a Unique Key and a name.
-///
- public interface IKeyName : IKeyed
-{
- ///
- /// Gets the name associated with the current object.
+ ///
+ /// Named Keyed device interface. Forces the device to have a Unique Key and a name.
///
- [JsonProperty("name")]
+ public interface IKeyName : IKeyed
+ {
+ ///
+ /// Isn't it obvious :)
+ ///
+ [JsonProperty("name")]
string Name { get; }
+ }
+
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Device.cs b/src/PepperDash.Core/Device.cs
index e1e9c066..58b5fe3e 100644
--- a/src/PepperDash.Core/Device.cs
+++ b/src/PepperDash.Core/Device.cs
@@ -2,193 +2,188 @@
using System.Collections.Generic;
using Serilog.Events;
-namespace PepperDash.Core;
-
-//*********************************************************************************************************
-///
-/// Represents a Device
-///
-public class Device : IKeyName
+namespace PepperDash.Core
{
-
+ //*********************************************************************************************************
///
- /// Unique Key
+ /// Represents a Device
///
- public string Key { get; protected set; }
- ///
- /// Gets or sets the Name
- ///
- public string Name { get; protected set; }
- ///
- ///
- ///
- public bool Enabled { get; protected set; }
-
- ///
- /// A place to store reference to the original config object, if any. These values should
- /// NOT be used as properties on the device as they are all publicly-settable values.
- ///
- //public DeviceConfig Config { get; private set; }
- ///
- /// Helper method to check if Config exists
- ///
- //public bool HasConfig { get { return Config != null; } }
-
- List _PreActivationActions;
- List _PostActivationActions;
-
- ///
- ///
- ///
- public static Device DefaultDevice { get { return _DefaultDevice; } }
- static Device _DefaultDevice = new Device("Default", "Default");
-
- ///
- /// Base constructor for all Devices.
- ///
- ///
- public Device(string key)
+ public class Device : IKeyName
{
- Key = key;
- if (key.Contains(".")) Debug.LogMessage(LogEventLevel.Information, "WARNING: Device key should not include '.'", this);
- Name = "";
- }
- ///
- /// Constructor with key and name
- ///
- ///
- ///
- public Device(string key, string name) : this(key)
- {
- Name = name;
- }
+ ///
+ /// Unique Key
+ ///
+ public string Key { get; protected set; }
+ ///
+ /// Gets or sets the Name
+ ///
+ public string Name { get; protected set; }
+ ///
+ ///
+ ///
+ public bool Enabled { get; protected set; }
- //public Device(DeviceConfig config)
- // : this(config.Key, config.Name)
- //{
- // Config = config;
- //}
+ List _PreActivationActions;
+ List _PostActivationActions;
- ///
- /// Adds a pre activation action
- ///
- ///
- protected void AddPreActivationAction(Action act)
- {
- if (_PreActivationActions == null)
- _PreActivationActions = new List();
- _PreActivationActions.Add(act);
- }
+ ///
+ ///
+ ///
+ public static Device DefaultDevice { get { return _DefaultDevice; } }
+ static Device _DefaultDevice = new Device("Default", "Default");
- ///
- /// Adds a post activation action
- ///
- ///
- ///
- /// AddPostActivationAction method
- ///
- protected void AddPostActivationAction(Action act)
- {
- if (_PostActivationActions == null)
- _PostActivationActions = new List();
- _PostActivationActions.Add(act);
- }
+ ///
+ /// Base constructor for all Devices.
+ ///
+ ///
+ public Device(string key)
+ {
+ Key = key;
+ if (key.Contains(".")) Debug.LogMessage(LogEventLevel.Information, "WARNING: Device key should not include '.'", this);
+ Name = "";
+ }
- ///
- /// PreActivate method
- ///
- public void PreActivate()
- {
- if (_PreActivationActions != null)
- _PreActivationActions.ForEach(a =>
- {
- try
+ ///
+ /// Constructor with key and name
+ ///
+ ///
+ ///
+ public Device(string key, string name) : this(key)
+ {
+ Name = name;
+
+ }
+
+ //public Device(DeviceConfig config)
+ // : this(config.Key, config.Name)
+ //{
+ // Config = config;
+ //}
+
+ ///
+ /// Adds a pre activation action
+ ///
+ ///
+ public void AddPreActivationAction(Action act)
+ {
+ if (_PreActivationActions == null)
+ _PreActivationActions = new List();
+ _PreActivationActions.Add(act);
+ }
+
+ ///
+ /// Adds a post activation action
+ ///
+ ///
+ ///
+ /// AddPostActivationAction method
+ ///
+ public void AddPostActivationAction(Action act)
+ {
+ if (_PostActivationActions == null)
+ _PostActivationActions = new List();
+ _PostActivationActions.Add(act);
+ }
+
+ ///
+ /// PreActivate method
+ ///
+ public void PreActivate()
+ {
+ if (_PreActivationActions != null)
+ _PreActivationActions.ForEach(a =>
{
- a.Invoke();
- }
- catch (Exception e)
+ try
+ {
+ a.Invoke();
+ }
+ catch (Exception e)
+ {
+ Debug.LogMessage(e, "Error in PreActivationAction: " + e.Message, this);
+ }
+ });
+ }
+
+ ///
+ /// Activate method
+ ///
+ public bool Activate()
+ {
+ //if (_PreActivationActions != null)
+ // _PreActivationActions.ForEach(a => a.Invoke());
+ var result = CustomActivate();
+ //if(result && _PostActivationActions != null)
+ // _PostActivationActions.ForEach(a => a.Invoke());
+ return result;
+ }
+
+ ///
+ /// PostActivate method
+ ///
+ public void PostActivate()
+ {
+ if (_PostActivationActions != null)
+ _PostActivationActions.ForEach(a =>
{
- Debug.LogMessage(e, "Error in PreActivationAction: " + e.Message, this);
- }
- });
- }
+ try
+ {
+ a.Invoke();
+ }
+ catch (Exception e)
+ {
+ Debug.LogMessage(e, "Error in PostActivationAction: " + e.Message, this);
+ }
+ });
+ }
- ///
- /// Activate method
- ///
- public bool Activate()
- {
- //if (_PreActivationActions != null)
- // _PreActivationActions.ForEach(a => a.Invoke());
- var result = CustomActivate();
- //if(result && _PostActivationActions != null)
- // _PostActivationActions.ForEach(a => a.Invoke());
- return result;
- }
+ ///
+ /// Called in between Pre and PostActivationActions when Activate() is called.
+ /// Override to provide addtitional setup when calling activation. Overriding classes
+ /// do not need to call base.CustomActivate()
+ ///
+ /// true if device activated successfully.
+ ///
+ /// CustomActivate method
+ ///
+ public virtual bool CustomActivate() { return true; }
- ///
- /// PostActivate method
- ///
- public void PostActivate()
- {
- if (_PostActivationActions != null)
- _PostActivationActions.ForEach(a =>
- {
- try
- {
- a.Invoke();
- }
- catch (Exception e)
- {
- Debug.LogMessage(e, "Error in PostActivationAction: " + e.Message, this);
- }
- });
- }
+ ///
+ /// Call to deactivate device - unlink events, etc. Overriding classes do not
+ /// need to call base.Deactivate()
+ ///
+ ///
+ public virtual bool Deactivate() { return true; }
- ///
- /// Called in between Pre and PostActivationActions when Activate() is called.
- /// Override to provide addtitional setup when calling activation. Overriding classes
- /// do not need to call base.CustomActivate()
- ///
- /// true if device activated successfully.
- ///
- /// CustomActivate method
- ///
- protected virtual bool CustomActivate() { return true; }
+ ///
+ /// Call this method to start communications with a device. Overriding classes do not need to call base.Initialize()
+ ///
+ public virtual void Initialize()
+ {
+ }
- ///
- /// Call to deactivate device - unlink events, etc. Overriding classes do not
- /// need to call base.Deactivate()
- ///
- ///
- public virtual bool Deactivate() { return true; }
+ ///
+ /// Helper method to check object for bool value false and fire an Action method
+ ///
+ /// Should be of type bool, others will be ignored
+ /// Action to be run when o is false
+ public void OnFalse(object o, Action a)
+ {
+ if (o is bool && !(bool)o) a();
+ }
- ///
- /// Call this method to start communications with a device. Overriding classes do not need to call base.Initialize()
- ///
- protected virtual void Initialize()
- {
- }
-
- ///
- /// Helper method to check object for bool value false and fire an Action method
- ///
- /// Should be of type bool, others will be ignored
- /// Action to be run when o is false
- public void OnFalse(object o, Action a)
- {
- if (o is bool && !(bool)o) a();
- }
-
- ///
- /// Returns a string representation of the object, including its key and name.
- ///
- /// The returned string is formatted as "{Key} - {Name}". If the Name property is
- /// null or empty, "---" is used in place of the name.
- /// A string that represents the object, containing the key and name in the format "{Key} - {Name}".
- public override string ToString()
- {
- return string.Format("{0} - {1}", Key, string.IsNullOrEmpty(Name) ? "---" : Name);
+ ///
+ /// Returns a string representation of the object, including its key and name.
+ ///
+ /// The returned string is formatted as "{Key} - {Name}". If the Name property is
+ /// null or empty, "---" is used in place of the name.
+ /// A string that represents the object, containing the key and name in the format "{Key} - {Name}".
+ ///
+ /// ToString method
+ ///
+ public override string ToString()
+ {
+ return string.Format("{0} - {1}", Key, string.IsNullOrEmpty(Name) ? "---" : Name);
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/EthernetHelper.cs b/src/PepperDash.Core/EthernetHelper.cs
index 51372603..76a9405c 100644
--- a/src/PepperDash.Core/EthernetHelper.cs
+++ b/src/PepperDash.Core/EthernetHelper.cs
@@ -1,14 +1,12 @@
-extern alias NewtonsoftJson;
-
-using Crestron.SimplSharp;
-using JsonProperty = NewtonsoftJson::Newtonsoft.Json.JsonPropertyAttribute;
+using Crestron.SimplSharp;
+using Newtonsoft.Json;
using Serilog.Events;
-namespace PepperDash.Core;
-
-///
-/// Represents an EthernetHelper.
-///
+namespace PepperDash.Core
+{
+ ///
+ /// Represents a EthernetHelper
+ ///
public class EthernetHelper
{
///
@@ -115,4 +113,5 @@ namespace PepperDash.Core;
CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, 0);
}
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/EventArgs.cs b/src/PepperDash.Core/EventArgs.cs
index 68c2be53..c146a6f2 100644
--- a/src/PepperDash.Core/EventArgs.cs
+++ b/src/PepperDash.Core/EventArgs.cs
@@ -4,8 +4,8 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Core;
-
+namespace PepperDash.Core
+{
///
/// Bool change event args
///
@@ -168,4 +168,5 @@ namespace PepperDash.Core;
Type = type;
Index = index;
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/GenericRESTfulCommunications/Constants.cs b/src/PepperDash.Core/GenericRESTfulCommunications/Constants.cs
new file mode 100644
index 00000000..1b78c33f
--- /dev/null
+++ b/src/PepperDash.Core/GenericRESTfulCommunications/Constants.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Core.GenericRESTfulCommunications
+{
+ ///
+ /// Constants
+ ///
+ public class GenericRESTfulConstants
+ {
+ ///
+ /// Generic boolean change
+ ///
+ public const ushort BoolValueChange = 1;
+ ///
+ /// Generic Ushort change
+ ///
+ public const ushort UshrtValueChange = 101;
+ ///
+ /// Response Code Ushort change
+ ///
+ public const ushort ResponseCodeChange = 102;
+ ///
+ /// Generic String chagne
+ ///
+ public const ushort StringValueChange = 201;
+ ///
+ /// Response string change
+ ///
+ public const ushort ResponseStringChange = 202;
+ ///
+ /// Error string change
+ ///
+ public const ushort ErrorStringChange = 203;
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/GenericRESTfulCommunications/GenericRESTfulClient.cs b/src/PepperDash.Core/GenericRESTfulCommunications/GenericRESTfulClient.cs
new file mode 100644
index 00000000..bd33e13c
--- /dev/null
+++ b/src/PepperDash.Core/GenericRESTfulCommunications/GenericRESTfulClient.cs
@@ -0,0 +1,256 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.Net.Http;
+using Crestron.SimplSharp.Net.Https;
+
+namespace PepperDash.Core.GenericRESTfulCommunications
+{
+ ///
+ /// Generic RESTful communication class
+ ///
+ public class GenericRESTfulClient
+ {
+ ///
+ /// Boolean event handler
+ ///
+ public event EventHandler BoolChange;
+ ///
+ /// Ushort event handler
+ ///
+ public event EventHandler UshrtChange;
+ ///
+ /// String event handler
+ ///
+ public event EventHandler StringChange;
+
+ ///
+ /// Constructor
+ ///
+ public GenericRESTfulClient()
+ {
+
+ }
+
+ ///
+ /// Generic RESTful submit request
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void SubmitRequest(string url, ushort port, ushort requestType, string contentType, string username, string password)
+ {
+ if (url.StartsWith("https:", StringComparison.OrdinalIgnoreCase))
+ {
+ SubmitRequestHttps(url, port, requestType, contentType, username, password);
+ }
+ else if (url.StartsWith("http:", StringComparison.OrdinalIgnoreCase))
+ {
+ SubmitRequestHttp(url, port, requestType, contentType, username, password);
+ }
+ else
+ {
+ OnStringChange(string.Format("Invalid URL {0}", url), 0, GenericRESTfulConstants.ErrorStringChange);
+ }
+ }
+
+ ///
+ /// Private HTTP submit request
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private void SubmitRequestHttp(string url, ushort port, ushort requestType, string contentType, string username, string password)
+ {
+ try
+ {
+ HttpClient client = new HttpClient();
+ HttpClientRequest request = new HttpClientRequest();
+ HttpClientResponse response;
+
+ client.KeepAlive = false;
+
+ if(port >= 1 || port <= 65535)
+ client.Port = port;
+ else
+ client.Port = 80;
+
+ var authorization = "";
+ if (!string.IsNullOrEmpty(username))
+ authorization = EncodeBase64(username, password);
+
+ if (!string.IsNullOrEmpty(authorization))
+ request.Header.SetHeaderValue("Authorization", authorization);
+
+ if (!string.IsNullOrEmpty(contentType))
+ request.Header.ContentType = contentType;
+
+ request.Url.Parse(url);
+ request.RequestType = (Crestron.SimplSharp.Net.Http.RequestType)requestType;
+
+ response = client.Dispatch(request);
+
+ CrestronConsole.PrintLine(string.Format("SubmitRequestHttp Response[{0}]: {1}", response.Code, response.ContentString.ToString()));
+
+ if (!string.IsNullOrEmpty(response.ContentString.ToString()))
+ OnStringChange(response.ContentString.ToString(), 0, GenericRESTfulConstants.ResponseStringChange);
+
+ if (response.Code > 0)
+ OnUshrtChange((ushort)response.Code, 0, GenericRESTfulConstants.ResponseCodeChange);
+ }
+ catch (Exception e)
+ {
+ //var msg = string.Format("SubmitRequestHttp({0}, {1}, {2}) failed:{3}", url, port, requestType, e.Message);
+ //CrestronConsole.PrintLine(msg);
+ //ErrorLog.Error(msg);
+
+ CrestronConsole.PrintLine(e.Message);
+ OnStringChange(e.Message, 0, GenericRESTfulConstants.ErrorStringChange);
+ }
+ }
+
+ ///
+ /// Private HTTPS submit request
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private void SubmitRequestHttps(string url, ushort port, ushort requestType, string contentType, string username, string password)
+ {
+ try
+ {
+ HttpsClient client = new HttpsClient();
+ HttpsClientRequest request = new HttpsClientRequest();
+ HttpsClientResponse response;
+
+ client.KeepAlive = false;
+ client.HostVerification = false;
+ client.PeerVerification = false;
+
+ var authorization = "";
+ if (!string.IsNullOrEmpty(username))
+ authorization = EncodeBase64(username, password);
+
+ if (!string.IsNullOrEmpty(authorization))
+ request.Header.SetHeaderValue("Authorization", authorization);
+
+ if (!string.IsNullOrEmpty(contentType))
+ request.Header.ContentType = contentType;
+
+ request.Url.Parse(url);
+ request.RequestType = (Crestron.SimplSharp.Net.Https.RequestType)requestType;
+
+ response = client.Dispatch(request);
+
+ CrestronConsole.PrintLine(string.Format("SubmitRequestHttp Response[{0}]: {1}", response.Code, response.ContentString.ToString()));
+
+ if(!string.IsNullOrEmpty(response.ContentString.ToString()))
+ OnStringChange(response.ContentString.ToString(), 0, GenericRESTfulConstants.ResponseStringChange);
+
+ if(response.Code > 0)
+ OnUshrtChange((ushort)response.Code, 0, GenericRESTfulConstants.ResponseCodeChange);
+
+ }
+ catch (Exception e)
+ {
+ //var msg = string.Format("SubmitRequestHttps({0}, {1}, {2}, {3}, {4}) failed:{5}", url, port, requestType, username, password, e.Message);
+ //CrestronConsole.PrintLine(msg);
+ //ErrorLog.Error(msg);
+
+ CrestronConsole.PrintLine(e.Message);
+ OnStringChange(e.Message, 0, GenericRESTfulConstants.ErrorStringChange);
+ }
+ }
+
+ ///
+ /// Private method to encode username and password to Base64 string
+ ///
+ ///
+ ///
+ /// authorization
+ private string EncodeBase64(string username, string password)
+ {
+ var authorization = "";
+
+ try
+ {
+ if (!string.IsNullOrEmpty(username))
+ {
+ string base64String = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(string.Format("{0}:{1}", username, password)));
+ authorization = string.Format("Basic {0}", base64String);
+ }
+ }
+ catch (Exception e)
+ {
+ var msg = string.Format("EncodeBase64({0}, {1}) failed:\r{2}", username, password, e);
+ CrestronConsole.PrintLine(msg);
+ ErrorLog.Error(msg);
+ return "" ;
+ }
+
+ return authorization;
+ }
+
+ ///
+ /// Protected method to handle boolean change events
+ ///
+ ///
+ ///
+ ///
+ protected void OnBoolChange(bool state, ushort index, ushort type)
+ {
+ var handler = BoolChange;
+ if (handler != null)
+ {
+ var args = new BoolChangeEventArgs(state, type);
+ args.Index = index;
+ BoolChange(this, args);
+ }
+ }
+
+ ///
+ /// Protected mehtod to handle ushort change events
+ ///
+ ///
+ ///
+ ///
+ protected void OnUshrtChange(ushort value, ushort index, ushort type)
+ {
+ var handler = UshrtChange;
+ if (handler != null)
+ {
+ var args = new UshrtChangeEventArgs(value, type);
+ args.Index = index;
+ UshrtChange(this, args);
+ }
+ }
+
+ ///
+ /// Protected method to handle string change events
+ ///
+ ///
+ ///
+ ///
+ protected void OnStringChange(string value, ushort index, ushort type)
+ {
+ var handler = StringChange;
+ if (handler != null)
+ {
+ var args = new StringChangeEventArgs(value, type);
+ args.Index = index;
+ StringChange(this, args);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/JsonStandardObjects/EventArgs and Constants.cs b/src/PepperDash.Core/JsonStandardObjects/EventArgs and Constants.cs
index 668bd481..110587ab 100644
--- a/src/PepperDash.Core/JsonStandardObjects/EventArgs and Constants.cs
+++ b/src/PepperDash.Core/JsonStandardObjects/EventArgs and Constants.cs
@@ -4,8 +4,8 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Core.JsonStandardObjects;
-
+namespace PepperDash.Core.JsonStandardObjects
+{
///
/// Constants for simpl modules
///
@@ -73,4 +73,5 @@ namespace PepperDash.Core.JsonStandardObjects;
Type = type;
Index = index;
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/JsonStandardObjects/JsonToSimplDevice.cs b/src/PepperDash.Core/JsonStandardObjects/JsonToSimplDevice.cs
index 621e99e0..2ad47e27 100644
--- a/src/PepperDash.Core/JsonStandardObjects/JsonToSimplDevice.cs
+++ b/src/PepperDash.Core/JsonStandardObjects/JsonToSimplDevice.cs
@@ -4,8 +4,8 @@ using Crestron.SimplSharp;
using PepperDash.Core.JsonToSimpl;
using Serilog.Events;
-namespace PepperDash.Core.JsonStandardObjects;
-
+namespace PepperDash.Core.JsonStandardObjects
+{
///
/// Device class
///
@@ -182,4 +182,5 @@ namespace PepperDash.Core.JsonStandardObjects;
}
#endregion EventHandler Helpers
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/JsonStandardObjects/JsonToSimplDeviceConfig.cs b/src/PepperDash.Core/JsonStandardObjects/JsonToSimplDeviceConfig.cs
index 5d98ba5a..d754a029 100644
--- a/src/PepperDash.Core/JsonStandardObjects/JsonToSimplDeviceConfig.cs
+++ b/src/PepperDash.Core/JsonStandardObjects/JsonToSimplDeviceConfig.cs
@@ -4,8 +4,8 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Core.JsonStandardObjects;
-
+namespace PepperDash.Core.JsonStandardObjects
+{
/*
Convert JSON snippt to C#: http://json2csharp.com/#
@@ -52,55 +52,55 @@ namespace PepperDash.Core.JsonStandardObjects;
///
public class ComParamsConfig
{
- ///
- ///
- ///
+ ///
+ /// Gets or sets the baudRate
+ ///
public int baudRate { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public int dataBits { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public int stopBits { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string parity { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string protocol { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string hardwareHandshake { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string softwareHandshake { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public int pacing { get; set; }
// convert properties for simpl
- ///
- ///
- ///
+ ///
+ /// Gets or sets the simplBaudRate
+ ///
public ushort simplBaudRate { get { return Convert.ToUInt16(baudRate); } }
- ///
- ///
- ///
+ ///
+ /// Gets or sets the simplDataBits
+ ///
public ushort simplDataBits { get { return Convert.ToUInt16(dataBits); } }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public ushort simplStopBits { get { return Convert.ToUInt16(stopBits); } }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public ushort simplPacing { get { return Convert.ToUInt16(pacing); } }
///
@@ -117,43 +117,43 @@ namespace PepperDash.Core.JsonStandardObjects;
///
public class TcpSshPropertiesConfig
{
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string address { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public int port { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string username { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string password { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public bool autoReconnect { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public int autoReconnectIntervalMs { get; set; }
// convert properties for simpl
- ///
- ///
- ///
+ ///
+ /// Gets or sets the simplPort
+ ///
public ushort simplPort { get { return Convert.ToUInt16(port); } }
- ///
- ///
- ///
+ ///
+ /// Gets or sets the simplAutoReconnect
+ ///
public ushort simplAutoReconnect { get { return (ushort)(autoReconnect ? 1 : 0); } }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public ushort simplAutoReconnectIntervalMs { get { return Convert.ToUInt16(autoReconnectIntervalMs); } }
///
@@ -170,31 +170,31 @@ namespace PepperDash.Core.JsonStandardObjects;
///
public class ControlConfig
{
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string method { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string controlPortDevKey { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public int controlPortNumber { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public ComParamsConfig comParams { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public TcpSshPropertiesConfig tcpSshProperties { get; set; }
// convert properties for simpl
- ///
- ///
- ///
+ ///
+ /// Gets or sets the simplControlPortNumber
+ ///
public ushort simplControlPortNumber { get { return Convert.ToUInt16(controlPortNumber); } }
///
@@ -212,27 +212,27 @@ namespace PepperDash.Core.JsonStandardObjects;
///
public class PropertiesConfig
{
- ///
- ///
- ///
+ ///
+ ///
+ ///
public int deviceId { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public bool enabled { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public ControlConfig control { get; set; }
// convert properties for simpl
- ///
- ///
- ///
+ ///
+ /// Gets or sets the simplDeviceId
+ ///
public ushort simplDeviceId { get { return Convert.ToUInt16(deviceId); } }
- ///
- ///
- ///
+ ///
+ /// Gets or sets the simplEnabled
+ ///
public ushort simplEnabled { get { return (ushort)(enabled ? 1 : 0); } }
///
@@ -249,8 +249,9 @@ namespace PepperDash.Core.JsonStandardObjects;
///
public class RootObject
{
- ///
- /// The collection of devices
- ///
+ ///
+ /// The collection of devices
+ ///
public List devices { get; set; }
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/JsonToSimpl/Constants.cs b/src/PepperDash.Core/JsonToSimpl/Constants.cs
index a04c1966..ca638bbf 100644
--- a/src/PepperDash.Core/JsonToSimpl/Constants.cs
+++ b/src/PepperDash.Core/JsonToSimpl/Constants.cs
@@ -4,78 +4,78 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Core.JsonToSimpl;
-
+namespace PepperDash.Core.JsonToSimpl
+{
///
/// Constants for Simpl modules
///
public class JsonToSimplConstants
{
- ///
- ///
- ///
+ ///
+ ///
+ ///
public const ushort BoolValueChange = 1;
- ///
- ///
- ///
+ ///
+ ///
+ ///
public const ushort JsonIsValidBoolChange = 2;
- ///
- /// Reports the if the device is 3-series compatible
- ///
- public const ushort ProgramCompatibility3SeriesChange = 3;
+ ///
+ /// Reports the if the device is 3-series compatible
+ ///
+ public const ushort ProgramCompatibility3SeriesChange = 3;
- ///
- /// Reports the if the device is 4-series compatible
- ///
- public const ushort ProgramCompatibility4SeriesChange = 4;
+ ///
+ /// Reports the if the device is 4-series compatible
+ ///
+ public const ushort ProgramCompatibility4SeriesChange = 4;
- ///
- /// Reports the device platform enum value
- ///
- public const ushort DevicePlatformValueChange = 5;
+ ///
+ /// Reports the device platform enum value
+ ///
+ public const ushort DevicePlatformValueChange = 5;
- ///
- ///
- ///
+ ///
+ ///
+ ///
public const ushort UshortValueChange = 101;
- ///
- ///
- ///
+ ///
+ ///
+ ///
public const ushort StringValueChange = 201;
- ///
- ///
- ///
+ ///
+ ///
+ ///
public const ushort FullPathToArrayChange = 202;
- ///
- ///
- ///
+ ///
+ ///
+ ///
public const ushort ActualFilePathChange = 203;
- ///
- ///
- ///
+ ///
+ ///
+ ///
public const ushort FilenameResolvedChange = 204;
- ///
- ///
- ///
+ ///
+ ///
+ ///
public const ushort FilePathResolvedChange = 205;
- ///
- /// Reports the root directory change
- ///
- public const ushort RootDirectoryChange = 206;
+ ///
+ /// Reports the root directory change
+ ///
+ public const ushort RootDirectoryChange = 206;
- ///
- /// Reports the room ID change
- ///
- public const ushort RoomIdChange = 207;
+ ///
+ /// Reports the room ID change
+ ///
+ public const ushort RoomIdChange = 207;
- ///
- /// Reports the room name change
- ///
- public const ushort RoomNameChange = 208;
+ ///
+ /// Reports the room name change
+ ///
+ public const ushort RoomNameChange = 208;
}
///
@@ -88,33 +88,33 @@ namespace PepperDash.Core.JsonToSimpl;
///
public class SPlusValueWrapper
{
- ///
- ///
- ///
+ ///
+ /// Gets or sets the ValueType
+ ///
public SPlusType ValueType { get; private set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public ushort Index { get; private set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public ushort BoolUShortValue { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string StringValue { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public SPlusValueWrapper() {}
- ///
- ///
- ///
- ///
- ///
+ ///
+ ///
+ ///
+ ///
+ ///
public SPlusValueWrapper(SPlusType type, ushort index)
{
ValueType = type;
@@ -127,16 +127,17 @@ namespace PepperDash.Core.JsonToSimpl;
///
public enum SPlusType
{
- ///
- /// Digital
- ///
+ ///
+ /// Digital
+ ///
Digital,
- ///
- /// Analog
- ///
- Analog,
- ///
- /// String
- ///
- String
- }
\ No newline at end of file
+ ///
+ /// Analog
+ ///
+ Analog,
+ ///
+ /// String
+ ///
+ String
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/JsonToSimpl/Global.cs b/src/PepperDash.Core/JsonToSimpl/Global.cs
index 39633a61..8d71c5fb 100644
--- a/src/PepperDash.Core/JsonToSimpl/Global.cs
+++ b/src/PepperDash.Core/JsonToSimpl/Global.cs
@@ -7,11 +7,11 @@ using Serilog.Events;
//using PepperDash.Core;
-namespace PepperDash.Core.JsonToSimpl;
-
-///
-/// The global class to manage all the instances of JsonToSimplMaster
-///
+namespace PepperDash.Core.JsonToSimpl
+{
+ ///
+ /// The global class to manage all the instances of JsonToSimplMaster
+ ///
public class J2SGlobal
{
static List Masters = new List();
@@ -22,7 +22,10 @@ namespace PepperDash.Core.JsonToSimpl;
/// master, this will fail
///
/// New master to add
- ///
+ ///
+ ///
+ /// AddMaster method
+ ///
public static void AddMaster(JsonToSimplMaster master)
{
if (master == null)
@@ -56,4 +59,5 @@ namespace PepperDash.Core.JsonToSimpl;
{
return Masters.FirstOrDefault(m => m.UniqueID.Equals(file, StringComparison.OrdinalIgnoreCase));
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/JsonToSimpl/JsonToSimplArrayLookupChild.cs b/src/PepperDash.Core/JsonToSimpl/JsonToSimplArrayLookupChild.cs
index f49a4b77..268f6207 100644
--- a/src/PepperDash.Core/JsonToSimpl/JsonToSimplArrayLookupChild.cs
+++ b/src/PepperDash.Core/JsonToSimpl/JsonToSimplArrayLookupChild.cs
@@ -1,24 +1,22 @@
-extern alias NewtonsoftJson;
-
-using System;
+using System;
using System.Linq;
-using JArray = NewtonsoftJson::Newtonsoft.Json.Linq.JArray;
+using Newtonsoft.Json.Linq;
using Serilog.Events;
-namespace PepperDash.Core.JsonToSimpl;
-
-///
-/// Used to interact with an array of values with the S+ modules
-///
+namespace PepperDash.Core.JsonToSimpl
+{
+ ///
+ /// Represents a JsonToSimplArrayLookupChild
+ ///
public class JsonToSimplArrayLookupChild : JsonToSimplChildObjectBase
{
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string SearchPropertyName { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string SearchPropertyValue { get; set; }
int ArrayIndex;
@@ -78,9 +76,10 @@ namespace PepperDash.Core.JsonToSimpl;
PathSuffix == null ? "" : PathSuffix);
}
- ///
- /// Process all values
- ///
+ ///
+ /// ProcessAll method
+ ///
+ ///
public override void ProcessAll()
{
if (FindInArray())
@@ -130,7 +129,7 @@ namespace PepperDash.Core.JsonToSimpl;
var item = array.FirstOrDefault(o =>
{
var prop = o[SearchPropertyName];
- return prop != null && ((string)prop)
+ return prop != null && prop.Value()
.Equals(SearchPropertyValue, StringComparison.OrdinalIgnoreCase);
});
if (item == null)
@@ -160,4 +159,5 @@ namespace PepperDash.Core.JsonToSimpl;
return false;
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/JsonToSimpl/JsonToSimplChildObjectBase.cs b/src/PepperDash.Core/JsonToSimpl/JsonToSimplChildObjectBase.cs
index 5e644483..68e4bf91 100644
--- a/src/PepperDash.Core/JsonToSimpl/JsonToSimplChildObjectBase.cs
+++ b/src/PepperDash.Core/JsonToSimpl/JsonToSimplChildObjectBase.cs
@@ -1,34 +1,31 @@
-extern alias NewtonsoftJson;
-
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
-using PepperDash.Core.Logging;
-using JValue = NewtonsoftJson::Newtonsoft.Json.Linq.JValue;
+using Newtonsoft.Json.Linq;
-namespace PepperDash.Core.JsonToSimpl;
-
-///
-/// Base class for JSON objects
-///
+namespace PepperDash.Core.JsonToSimpl
+{
+ ///
+ /// Base class for JSON objects
+ ///
public abstract class JsonToSimplChildObjectBase : IKeyed
{
- ///
- /// Notifies of bool change
- ///
+ ///
+ /// Notifies of bool change
+ ///
public event EventHandler BoolChange;
- ///
- /// Notifies of ushort change
- ///
+ ///
+ /// Notifies of ushort change
+ ///
public event EventHandler UShortChange;
- ///
- /// Notifies of string change
- ///
+ ///
+ /// Notifies of string change
+ ///
public event EventHandler StringChange;
- ///
- /// Delegate to get all values
- ///
+ ///
+ /// Delegate to get all values
+ ///
public SPlusValuesDelegate GetAllValuesDelegate { get; set; }
///
@@ -36,9 +33,9 @@ namespace PepperDash.Core.JsonToSimpl;
///
public SPlusValuesDelegate SetAllPathsDelegate { get; set; }
- ///
- /// Unique identifier for instance
- ///
+ ///
+ /// Gets or sets the Key
+ ///
public string Key { get; protected set; }
///
@@ -52,33 +49,33 @@ namespace PepperDash.Core.JsonToSimpl;
///
public string PathSuffix { get; protected set; }
- ///
- /// Indicates if the instance is linked to an object
- ///
+ ///
+ /// Gets or sets the LinkedToObject
+ ///
public bool LinkedToObject { get; protected set; }
- ///
- /// Reference to Master instance
- ///
+ ///
+ /// Reference to Master instance
+ ///
protected JsonToSimplMaster Master;
- ///
- /// Paths to boolean values in JSON structure
- ///
- protected Dictionary BoolPaths = new Dictionary();
- ///
- /// Paths to numeric values in JSON structure
- ///
+ ///
+ /// Paths to boolean values in JSON structure
+ ///
+ protected Dictionary BoolPaths = new Dictionary();
+ ///
+ /// Paths to numeric values in JSON structure
+ ///
protected Dictionary UshortPaths = new Dictionary();
- ///
- /// Paths to string values in JSON structure
- ///
+ ///
+ /// Paths to string values in JSON structure
+ ///
protected Dictionary StringPaths = new Dictionary();
///
/// Call this before doing anything else
///
- ///
+ ///
///
///
///
@@ -92,13 +89,16 @@ namespace PepperDash.Core.JsonToSimpl;
if (Master != null)
Master.AddChild(this);
else
- this.LogWarning("JSON Child [{0}] cannot link to master {1}", key, masterUniqueId);
+ Debug.Console(1, "JSON Child [{0}] cannot link to master {1}", key, masterUniqueId);
}
- ///
- /// Sets the path prefix for the object
- ///
- ///
+ ///
+ /// Sets the path prefix for the object
+ ///
+ ///
+ ///
+ /// SetPathPrefix method
+ ///
public void SetPathPrefix(string pathPrefix)
{
PathPrefix = pathPrefix;
@@ -108,7 +108,7 @@ namespace PepperDash.Core.JsonToSimpl;
///
public void SetBoolPath(ushort index, string path)
{
- this.LogDebug("JSON Child[{0}] SetBoolPath {1}={2}", Key, index, path);
+ Debug.Console(1, "JSON Child[{0}] SetBoolPath {1}={2}", Key, index, path);
if (path == null || path.Trim() == string.Empty) return;
BoolPaths[index] = path;
}
@@ -118,7 +118,7 @@ namespace PepperDash.Core.JsonToSimpl;
///
public void SetUshortPath(ushort index, string path)
{
- this.LogDebug("JSON Child[{0}] SetUshortPath {1}={2}", Key, index, path);
+ Debug.Console(1, "JSON Child[{0}] SetUshortPath {1}={2}", Key, index, path);
if (path == null || path.Trim() == string.Empty) return;
UshortPaths[index] = path;
}
@@ -128,7 +128,7 @@ namespace PepperDash.Core.JsonToSimpl;
///
public void SetStringPath(ushort index, string path)
{
- this.LogDebug("JSON Child[{0}] SetStringPath {1}={2}", Key, index, path);
+ Debug.Console(1, "JSON Child[{0}] SetStringPath {1}={2}", Key, index, path);
if (path == null || path.Trim() == string.Empty) return;
StringPaths[index] = path;
}
@@ -141,13 +141,13 @@ namespace PepperDash.Core.JsonToSimpl;
{
if (!LinkedToObject)
{
- this.LogDebug("Not linked to object in file. Skipping");
+ Debug.Console(1, this, "Not linked to object in file. Skipping");
return;
}
if (SetAllPathsDelegate == null)
{
- this.LogDebug("No SetAllPathsDelegate set. Ignoring ProcessAll");
+ Debug.Console(1, this, "No SetAllPathsDelegate set. Ignoring ProcessAll");
return;
}
SetAllPathsDelegate();
@@ -173,18 +173,18 @@ namespace PepperDash.Core.JsonToSimpl;
}
// Processes the path to a ushort, converting to ushort if able, twos complement if necessary, firing off UshrtChange event
- void ProcessUshortPath(ushort index) {
- string response;
- if (Process(UshortPaths[index], out response)) {
- ushort val;
- try { val = Convert.ToInt32(response) < 0 ? (ushort)(Convert.ToInt16(response) + 65536) : Convert.ToUInt16(response); }
- catch { val = 0; }
+ void ProcessUshortPath(ushort index) {
+ string response;
+ if (Process(UshortPaths[index], out response)) {
+ ushort val;
+ try { val = Convert.ToInt32(response) < 0 ? (ushort)(Convert.ToInt16(response) + 65536) : Convert.ToUInt16(response); }
+ catch { val = 0; }
- OnUShortChange(val, index, JsonToSimplConstants.UshortValueChange);
+ OnUShortChange(val, index, JsonToSimplConstants.UshortValueChange);
+ }
+ else { }
+ // OnUShortChange(0, index, JsonToSimplConstants.UshortValueChange);
}
- else { }
- // OnUShortChange(0, index, JsonToSimplConstants.UshortValueChange);
- }
// Processes the path to a string property and fires of a StringChange event.
void ProcessStringPath(ushort index)
@@ -207,11 +207,11 @@ namespace PepperDash.Core.JsonToSimpl;
bool Process(string path, out string response)
{
path = GetFullPath(path);
- this.LogDebug("JSON Child[{0}] Processing {1}", Key, path);
+ Debug.Console(1, "JSON Child[{0}] Processing {1}", Key, path);
response = "";
if (Master == null)
{
- this.LogWarning("JSONChild[{0}] cannot process without Master attached", Key);
+ Debug.Console(1, "JSONChild[{0}] cannot process without Master attached", Key);
return false;
}
@@ -233,8 +233,8 @@ namespace PepperDash.Core.JsonToSimpl;
if (isCount)
response = (t.HasValues ? t.Children().Count() : 0).ToString();
else
- response = (string)t;
- this.LogDebug(" ='{0}'", response);
+ response = t.Value();
+ Debug.Console(1, " ='{0}'", response);
return true;
}
}
@@ -260,13 +260,13 @@ namespace PepperDash.Core.JsonToSimpl;
{
if (!LinkedToObject)
{
- this.LogDebug("Not linked to object in file. Skipping");
+ Debug.Console(1, this, "Not linked to object in file. Skipping");
return;
}
if (SetAllPathsDelegate == null)
{
- this.LogDebug("No SetAllPathsDelegate set. Ignoring UpdateInputsForMaster");
+ Debug.Console(1, this, "No SetAllPathsDelegate set. Ignoring UpdateInputsForMaster");
return;
}
SetAllPathsDelegate();
@@ -275,60 +275,75 @@ namespace PepperDash.Core.JsonToSimpl;
GetAllValuesDelegate();
}
- ///
- ///
- ///
- ///
- ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// USetBoolValue method
+ ///
public void USetBoolValue(ushort key, ushort theValue)
{
SetBoolValue(key, theValue == 1);
}
- ///
- ///
- ///
- ///
- ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// SetBoolValue method
+ ///
public void SetBoolValue(ushort key, bool theValue)
{
if (BoolPaths.ContainsKey(key))
SetValueOnMaster(BoolPaths[key], new JValue(theValue));
}
- ///
- ///
- ///
- ///
- ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// SetUShortValue method
+ ///
public void SetUShortValue(ushort key, ushort theValue)
{
if (UshortPaths.ContainsKey(key))
SetValueOnMaster(UshortPaths[key], new JValue(theValue));
}
- ///
- ///
- ///
- ///
- ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// SetStringValue method
+ ///
public void SetStringValue(ushort key, string theValue)
{
if (StringPaths.ContainsKey(key))
SetValueOnMaster(StringPaths[key], new JValue(theValue));
}
- ///
- ///
- ///
- ///
- ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// SetValueOnMaster method
+ ///
public void SetValueOnMaster(string keyPath, JValue valueToSave)
{
var path = GetFullPath(keyPath);
try
{
- this.LogDebug("JSON Child[{0}] Queueing value on master {1}='{2}'", Key, path, valueToSave);
+ Debug.Console(1, "JSON Child[{0}] Queueing value on master {1}='{2}'", Key, path, valueToSave);
//var token = Master.JsonObject.SelectToken(path);
//if (token != null) // The path exists in the file
@@ -336,7 +351,7 @@ namespace PepperDash.Core.JsonToSimpl;
}
catch (Exception e)
{
- this.LogDebug("JSON Child[{0}] Failed setting value for path '{1}'\r{2}", Key, path, e);
+ Debug.Console(1, "JSON Child[{0}] Failed setting value for path '{1}'\r{2}", Key, path, e);
}
}
@@ -352,12 +367,12 @@ namespace PepperDash.Core.JsonToSimpl;
// Helpers for events
//******************************************************************************************
- ///
- /// Event helper
- ///
- ///
- ///
- ///
+ ///
+ /// Event helper
+ ///
+ ///
+ ///
+ ///
protected void OnBoolChange(bool state, ushort index, ushort type)
{
var handler = BoolChange;
@@ -370,12 +385,12 @@ namespace PepperDash.Core.JsonToSimpl;
}
//******************************************************************************************
- ///
- /// Event helper
- ///
- ///
- ///
- ///
+ ///
+ /// Event helper
+ ///
+ ///
+ ///
+ ///
protected void OnUShortChange(ushort state, ushort index, ushort type)
{
var handler = UShortChange;
@@ -387,12 +402,12 @@ namespace PepperDash.Core.JsonToSimpl;
}
}
- ///
- /// Event helper
- ///
- ///
- ///
- ///
+ ///
+ /// Event helper
+ ///
+ ///
+ ///
+ ///
protected void OnStringChange(string value, ushort index, ushort type)
{
var handler = StringChange;
@@ -403,4 +418,5 @@ namespace PepperDash.Core.JsonToSimpl;
StringChange(this, args);
}
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/JsonToSimpl/JsonToSimplFileMaster.cs b/src/PepperDash.Core/JsonToSimpl/JsonToSimplFileMaster.cs
index 1cf9efcf..13354fdd 100644
--- a/src/PepperDash.Core/JsonToSimpl/JsonToSimplFileMaster.cs
+++ b/src/PepperDash.Core/JsonToSimpl/JsonToSimplFileMaster.cs
@@ -1,263 +1,292 @@
-extern alias NewtonsoftJson;
-
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
-using PepperDash.Core.Logging;
-using Formatting = NewtonsoftJson::Newtonsoft.Json.Formatting;
-using JObject = NewtonsoftJson::Newtonsoft.Json.Linq.JObject;
-using JValue = NewtonsoftJson::Newtonsoft.Json.Linq.JValue;
+using Newtonsoft.Json.Linq;
-namespace PepperDash.Core.JsonToSimpl;
-
-///
-/// Represents a JSON file that can be read and written to
-///
-public class JsonToSimplFileMaster : JsonToSimplMaster
+namespace PepperDash.Core.JsonToSimpl
{
///
- /// Sets the filepath as well as registers this with the Global.Masters list
+ /// Represents a JSON file that can be read and written to
///
- public string Filepath { get; private set; }
-
- ///
- /// Filepath to the actual file that will be read (Portal or local)
- ///
- public string ActualFilePath { get; private set; }
-
- ///
- ///
- ///
- public string Filename { get; private set; }
- ///
- ///
- ///
- public string FilePathName { get; private set; }
-
- /*****************************************************************************************/
- /** Privates **/
-
-
- // The JSON file in JObject form
- // For gathering the incoming data
- object StringBuilderLock = new object();
- // To prevent multiple same-file access
- static object FileLock = new object();
-
- /*****************************************************************************************/
-
- ///
- /// SIMPL+ default constructor.
- ///
- public JsonToSimplFileMaster()
+ public class JsonToSimplFileMaster : JsonToSimplMaster
{
- }
+ ///
+ /// Sets the filepath as well as registers this with the Global.Masters list
+ ///
+ public string Filepath { get; private set; }
- ///
- /// Read, evaluate and udpate status
- ///
- public void EvaluateFile(string filepath)
- {
- try
+ ///
+ /// Gets or sets the ActualFilePath
+ ///
+ public string ActualFilePath { get; private set; }
+
+ ///
+ /// Gets or sets the Filename
+ ///
+ public string Filename { get; private set; }
+ ///
+ ///
+ ///
+ public string FilePathName { get; private set; }
+
+ /*****************************************************************************************/
+ /** Privates **/
+
+
+ // The JSON file in JObject form
+ // For gathering the incoming data
+ object StringBuilderLock = new object();
+ // To prevent multiple same-file access
+ static object FileLock = new object();
+
+ /*****************************************************************************************/
+
+ ///
+ /// SIMPL+ default constructor.
+ ///
+ public JsonToSimplFileMaster()
{
- OnBoolChange(false, 0, JsonToSimplConstants.JsonIsValidBoolChange);
+ }
- var dirSeparator = Path.DirectorySeparatorChar;
- var dirSeparatorAlt = Path.AltDirectorySeparatorChar;
-
- var series = CrestronEnvironment.ProgramCompatibility;
-
- var is3Series = (eCrestronSeries.Series3 == (series & eCrestronSeries.Series3));
- OnBoolChange(is3Series, 0,
- JsonToSimplConstants.ProgramCompatibility3SeriesChange);
-
- var is4Series = (eCrestronSeries.Series4 == (series & eCrestronSeries.Series4));
- OnBoolChange(is4Series, 0,
- JsonToSimplConstants.ProgramCompatibility4SeriesChange);
-
- var isServer = CrestronEnvironment.DevicePlatform == eDevicePlatform.Server;
- OnBoolChange(isServer, 0,
- JsonToSimplConstants.DevicePlatformValueChange);
-
- // get the roomID
- var roomId = Crestron.SimplSharp.InitialParametersClass.RoomId;
- if (!string.IsNullOrEmpty(roomId))
+ ///
+ /// Read, evaluate and udpate status
+ ///
+ public void EvaluateFile(string filepath)
+ {
+ try
{
- OnStringChange(roomId, 0, JsonToSimplConstants.RoomIdChange);
- }
+ OnBoolChange(false, 0, JsonToSimplConstants.JsonIsValidBoolChange);
- // get the roomName
- var roomName = Crestron.SimplSharp.InitialParametersClass.RoomName;
- if (!string.IsNullOrEmpty(roomName))
- {
- OnStringChange(roomName, 0, JsonToSimplConstants.RoomNameChange);
- }
+ var dirSeparator = Path.DirectorySeparatorChar;
+ var dirSeparatorAlt = Path.AltDirectorySeparatorChar;
- var rootDirectory = Directory.GetApplicationRootDirectory();
- OnStringChange(rootDirectory, 0, JsonToSimplConstants.RootDirectoryChange);
-
- var splusPath = string.Empty;
- if (Regex.IsMatch(filepath, @"user", RegexOptions.IgnoreCase))
- {
- if (is4Series)
- splusPath = Regex.Replace(filepath, "user", "user", RegexOptions.IgnoreCase);
- else if (isServer)
- splusPath = Regex.Replace(filepath, "user", "User", RegexOptions.IgnoreCase);
+ var series = CrestronEnvironment.ProgramCompatibility;
+
+ var is3Series = (eCrestronSeries.Series3 == (series & eCrestronSeries.Series3));
+ OnBoolChange(is3Series, 0,
+ JsonToSimplConstants.ProgramCompatibility3SeriesChange);
+
+ var is4Series = (eCrestronSeries.Series4 == (series & eCrestronSeries.Series4));
+ OnBoolChange(is4Series, 0,
+ JsonToSimplConstants.ProgramCompatibility4SeriesChange);
+
+ var isServer = CrestronEnvironment.DevicePlatform == eDevicePlatform.Server;
+ OnBoolChange(isServer, 0,
+ JsonToSimplConstants.DevicePlatformValueChange);
+
+ // get the roomID
+ var roomId = Crestron.SimplSharp.InitialParametersClass.RoomId;
+ if (!string.IsNullOrEmpty(roomId))
+ {
+ OnStringChange(roomId, 0, JsonToSimplConstants.RoomIdChange);
+ }
+
+ // get the roomName
+ var roomName = Crestron.SimplSharp.InitialParametersClass.RoomName;
+ if (!string.IsNullOrEmpty(roomName))
+ {
+ OnStringChange(roomName, 0, JsonToSimplConstants.RoomNameChange);
+ }
+
+ var rootDirectory = Directory.GetApplicationRootDirectory();
+ OnStringChange(rootDirectory, 0, JsonToSimplConstants.RootDirectoryChange);
+
+ var splusPath = string.Empty;
+ if (Regex.IsMatch(filepath, @"user", RegexOptions.IgnoreCase))
+ {
+ if (is4Series)
+ splusPath = Regex.Replace(filepath, "user", "user", RegexOptions.IgnoreCase);
+ else if (isServer)
+ splusPath = Regex.Replace(filepath, "user", "User", RegexOptions.IgnoreCase);
+ else
+ splusPath = filepath;
+ }
+
+ filepath = splusPath.Replace(dirSeparatorAlt, dirSeparator);
+
+ Filepath = string.Format("{1}{0}{2}", dirSeparator, rootDirectory,
+ filepath.TrimStart(dirSeparator, dirSeparatorAlt));
+
+ OnStringChange(string.Format("Attempting to evaluate {0}", Filepath), 0, JsonToSimplConstants.StringValueChange);
+
+ if (string.IsNullOrEmpty(Filepath))
+ {
+ OnStringChange(string.Format("Cannot evaluate file. JSON file path not set"), 0, JsonToSimplConstants.StringValueChange);
+ CrestronConsole.PrintLine("Cannot evaluate file. JSON file path not set");
+ return;
+ }
+
+ // get file directory and name to search
+ var fileDirectory = Path.GetDirectoryName(Filepath);
+ var fileName = Path.GetFileName(Filepath);
+
+ OnStringChange(string.Format("Checking '{0}' for '{1}'", fileDirectory, fileName), 0, JsonToSimplConstants.StringValueChange);
+ Debug.Console(1, "Checking '{0}' for '{1}'", fileDirectory, fileName);
+
+ if (Directory.Exists(fileDirectory))
+ {
+ // get the directory info
+ var directoryInfo = new DirectoryInfo(fileDirectory);
+
+ // get the file to be read
+ var actualFile = directoryInfo.GetFiles(fileName).FirstOrDefault();
+ if (actualFile == null)
+ {
+ var msg = string.Format("JSON file not found: {0}", Filepath);
+ OnStringChange(msg, 0, JsonToSimplConstants.StringValueChange);
+ CrestronConsole.PrintLine(msg);
+ ErrorLog.Error(msg);
+ return;
+ }
+
+ // \xSE\xR\PDT000-Template_Main_Config-Combined_DSP_v00.02.json
+ // \USER\PDT000-Template_Main_Config-Combined_DSP_v00.02.json
+ ActualFilePath = actualFile.FullName;
+ OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange);
+ OnStringChange(string.Format("Actual JSON file is {0}", ActualFilePath), 0, JsonToSimplConstants.StringValueChange);
+ Debug.Console(1, "Actual JSON file is {0}", ActualFilePath);
+
+ Filename = actualFile.Name;
+ OnStringChange(Filename, 0, JsonToSimplConstants.FilenameResolvedChange);
+ OnStringChange(string.Format("JSON Filename is {0}", Filename), 0, JsonToSimplConstants.StringValueChange);
+ Debug.Console(1, "JSON Filename is {0}", Filename);
+
+
+ FilePathName = string.Format(@"{0}{1}", actualFile.DirectoryName, dirSeparator);
+ OnStringChange(string.Format(@"{0}", actualFile.DirectoryName), 0, JsonToSimplConstants.FilePathResolvedChange);
+ OnStringChange(string.Format(@"JSON File Path is {0}", actualFile.DirectoryName), 0, JsonToSimplConstants.StringValueChange);
+ Debug.Console(1, "JSON File Path is {0}", FilePathName);
+
+ var json = File.ReadToEnd(ActualFilePath, System.Text.Encoding.ASCII);
+
+ JsonObject = JObject.Parse(json);
+ foreach (var child in Children)
+ child.ProcessAll();
+
+ OnBoolChange(true, 0, JsonToSimplConstants.JsonIsValidBoolChange);
+ }
else
- splusPath = filepath;
+ {
+ OnStringChange(string.Format("'{0}' not found", fileDirectory), 0, JsonToSimplConstants.StringValueChange);
+ Debug.Console(1, "'{0}' not found", fileDirectory);
+ }
+ }
+ catch (Exception e)
+ {
+ var msg = string.Format("EvaluateFile Exception: Message\r{0}", e.Message);
+ OnStringChange(msg, 0, JsonToSimplConstants.StringValueChange);
+ CrestronConsole.PrintLine(msg);
+ ErrorLog.Error(msg);
+
+ var stackTrace = string.Format("EvaluateFile: Stack Trace\r{0}", e.StackTrace);
+ OnStringChange(stackTrace, 0, JsonToSimplConstants.StringValueChange);
+ CrestronConsole.PrintLine(stackTrace);
+ ErrorLog.Error(stackTrace);
+ }
+ }
+
+
+ ///
+ /// Sets the debug level
+ ///
+ ///
+ ///
+ /// setDebugLevel method
+ ///
+ public void setDebugLevel(uint level)
+ {
+ Debug.SetDebugLevel(level);
+ }
+
+ ///
+ /// Saves the values to the file
+ ///
+ public override void Save()
+ {
+ // this code is duplicated in the other masters!!!!!!!!!!!!!
+ UnsavedValues = new Dictionary();
+ // Make each child update their values into master object
+ foreach (var child in Children)
+ {
+ Debug.Console(1, "Master [{0}] checking child [{1}] for updates to save", UniqueID, child.Key);
+ child.UpdateInputsForMaster();
}
- filepath = splusPath.Replace(dirSeparatorAlt, dirSeparator);
-
- Filepath = string.Format("{1}{0}{2}", dirSeparator, rootDirectory,
- filepath.TrimStart(dirSeparator, dirSeparatorAlt));
-
- OnStringChange(string.Format("Attempting to evaluate {0}", Filepath), 0, JsonToSimplConstants.StringValueChange);
-
- if (string.IsNullOrEmpty(Filepath))
+ if (UnsavedValues == null || UnsavedValues.Count == 0)
{
- OnStringChange(string.Format("Cannot evaluate file. JSON file path not set"), 0, JsonToSimplConstants.StringValueChange);
- CrestronConsole.PrintLine("Cannot evaluate file. JSON file path not set");
+ Debug.Console(1, "Master [{0}] No updated values to save. Skipping", UniqueID);
return;
}
-
- // get file directory and name to search
- var fileDirectory = Path.GetDirectoryName(Filepath);
- var fileName = Path.GetFileName(Filepath);
-
- OnStringChange(string.Format("Checking '{0}' for '{1}'", fileDirectory, fileName), 0, JsonToSimplConstants.StringValueChange);
- this.LogInformation("Checking '{0}' for '{1}'", fileDirectory, fileName);
-
- if (Directory.Exists(fileDirectory))
+ lock (FileLock)
{
- // get the directory info
- var directoryInfo = new DirectoryInfo(fileDirectory);
-
- // get the file to be read
- var actualFile = directoryInfo.GetFiles(fileName).FirstOrDefault();
- if (actualFile == null)
+ Debug.Console(1, "Saving");
+ foreach (var path in UnsavedValues.Keys)
{
- var msg = string.Format("JSON file not found: {0}", Filepath);
- OnStringChange(msg, 0, JsonToSimplConstants.StringValueChange);
- CrestronConsole.PrintLine(msg);
- this.LogError(msg);
- return;
+ var tokenToReplace = JsonObject.SelectToken(path);
+ if (tokenToReplace != null)
+ {// It's found
+ tokenToReplace.Replace(UnsavedValues[path]);
+ Debug.Console(1, "JSON Master[{0}] Updating '{1}'", UniqueID, path);
+ }
+ else // No token. Let's make one
+ {
+ //http://stackoverflow.com/questions/17455052/how-to-set-the-value-of-a-json-path-using-json-net
+ Debug.Console(1, "JSON Master[{0}] Cannot write value onto missing property: '{1}'", UniqueID, path);
+
+ // JContainer jpart = JsonObject;
+ // // walk down the path and find where it goes
+ //#warning Does not handle arrays.
+ // foreach (var part in path.Split('.'))
+ // {
+
+ // var openPos = part.IndexOf('[');
+ // if (openPos > -1)
+ // {
+ // openPos++; // move to number
+ // var closePos = part.IndexOf(']');
+ // var arrayName = part.Substring(0, openPos - 1); // get the name
+ // var index = Convert.ToInt32(part.Substring(openPos, closePos - openPos));
+
+ // // Check if the array itself exists and add the item if so
+ // if (jpart[arrayName] != null)
+ // {
+ // var arrayObj = jpart[arrayName] as JArray;
+ // var item = arrayObj[index];
+ // if (item == null)
+ // arrayObj.Add(new JObject());
+ // }
+
+ // Debug.Console(0, "IGNORING MISSING ARRAY VALUE FOR NOW");
+ // continue;
+ // }
+ // // Build the
+ // if (jpart[part] == null)
+ // jpart.Add(new JProperty(part, new JObject()));
+ // jpart = jpart[part] as JContainer;
+ // }
+ // jpart.Replace(UnsavedValues[path]);
+ }
}
-
- // \xSE\xR\PDT000-Template_Main_Config-Combined_DSP_v00.02.json
- // \USER\PDT000-Template_Main_Config-Combined_DSP_v00.02.json
- ActualFilePath = actualFile.FullName;
- OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange);
- OnStringChange(string.Format("Actual JSON file is {0}", ActualFilePath), 0, JsonToSimplConstants.StringValueChange);
- this.LogInformation("Actual JSON file is {0}", ActualFilePath);
-
- Filename = actualFile.Name;
- OnStringChange(Filename, 0, JsonToSimplConstants.FilenameResolvedChange);
- OnStringChange(string.Format("JSON Filename is {0}", Filename), 0, JsonToSimplConstants.StringValueChange);
- this.LogInformation("JSON Filename is {0}", Filename);
-
-
- FilePathName = string.Format(@"{0}{1}", actualFile.DirectoryName, dirSeparator);
- OnStringChange(string.Format(@"{0}", actualFile.DirectoryName), 0, JsonToSimplConstants.FilePathResolvedChange);
- OnStringChange(string.Format(@"JSON File Path is {0}", actualFile.DirectoryName), 0, JsonToSimplConstants.StringValueChange);
- this.LogInformation("JSON File Path is {0}", FilePathName);
-
- var json = File.ReadToEnd(ActualFilePath, System.Text.Encoding.ASCII);
-
- JsonObject = JObject.Parse(json);
- foreach (var child in Children)
- child.ProcessAll();
-
- OnBoolChange(true, 0, JsonToSimplConstants.JsonIsValidBoolChange);
- }
- else
- {
- OnStringChange(string.Format("'{0}' not found", fileDirectory), 0, JsonToSimplConstants.StringValueChange);
- this.LogError("'{0}' not found", fileDirectory);
- }
- }
- catch (Exception e)
- {
- var msg = string.Format("EvaluateFile Exception: Message\r{0}", e.Message);
- OnStringChange(msg, 0, JsonToSimplConstants.StringValueChange);
- CrestronConsole.PrintLine(msg);
- this.LogException(e, "EvaluateFile Exception: {0}", e.Message);
-
- var stackTrace = string.Format("EvaluateFile: Stack Trace\r{0}", e.StackTrace);
- OnStringChange(stackTrace, 0, JsonToSimplConstants.StringValueChange);
- CrestronConsole.PrintLine(stackTrace);
- this.LogVerbose("EvaluateFile: Stack Trace\r{0}", e.StackTrace);
- }
- }
-
-
- ///
- /// Sets the debug level
- ///
- ///
- public void setDebugLevel(uint level)
- {
- Debug.SetDebugLevel(level);
- }
-
- ///
- /// Saves the values to the file
- ///
- public override void Save()
- {
- // this code is duplicated in the other masters!!!!!!!!!!!!!
- UnsavedValues = new Dictionary();
- // Make each child update their values into master object
- foreach (var child in Children)
- {
- this.LogInformation("Master [{0}] checking child [{1}] for updates to save", UniqueID, child.Key);
- child.UpdateInputsForMaster();
- }
-
- if (UnsavedValues == null || UnsavedValues.Count == 0)
- {
- this.LogInformation("Master [{0}] No updated values to save. Skipping", UniqueID);
- return;
- }
- lock (FileLock)
- {
- this.LogInformation("Saving");
- foreach (var path in UnsavedValues.Keys)
- {
- var tokenToReplace = JsonObject.SelectToken(path);
- if (tokenToReplace != null)
- {// It's found
- tokenToReplace.Replace(UnsavedValues[path]);
- this.LogInformation("JSON Master[{0}] Updating '{1}'", UniqueID, path);
- }
- else // No token. Let's make one
+ using (StreamWriter sw = new StreamWriter(ActualFilePath))
{
- //http://stackoverflow.com/questions/17455052/how-to-set-the-value-of-a-json-path-using-json-net
- this.LogWarning("JSON Master[{0}] Cannot write value onto missing property: '{1}'", UniqueID, path);
-
- }
- }
- using (StreamWriter sw = new StreamWriter(ActualFilePath))
- {
- try
- {
- sw.Write(JsonObject.ToString());
- sw.Flush();
- }
- catch (Exception e)
- {
- string err = string.Format("Error writing JSON file:\r{0}", e);
- this.LogException(e, "Error writing JSON file: {0}", e.Message);
- this.LogVerbose("Stack Trace:\r{0}", e.StackTrace);
- return;
+ try
+ {
+ sw.Write(JsonObject.ToString());
+ sw.Flush();
+ }
+ catch (Exception e)
+ {
+ string err = string.Format("Error writing JSON file:\r{0}", e);
+ Debug.Console(0, err);
+ ErrorLog.Warn(err);
+ return;
+ }
}
}
}
}
}
-
-
diff --git a/src/PepperDash.Core/JsonToSimpl/JsonToSimplFixedPathObject.cs b/src/PepperDash.Core/JsonToSimpl/JsonToSimplFixedPathObject.cs
index 6338e309..9fa8ba01 100644
--- a/src/PepperDash.Core/JsonToSimpl/JsonToSimplFixedPathObject.cs
+++ b/src/PepperDash.Core/JsonToSimpl/JsonToSimplFixedPathObject.cs
@@ -1,17 +1,18 @@
-namespace PepperDash.Core.JsonToSimpl;
-
-///
-///
-///
+namespace PepperDash.Core.JsonToSimpl
+{
+ ///
+ /// Represents a JsonToSimplFixedPathObject
+ ///
public class JsonToSimplFixedPathObject : JsonToSimplChildObjectBase
{
- ///
- /// Constructor
- ///
+ ///
+ /// Constructor
+ ///
public JsonToSimplFixedPathObject()
{
this.LinkedToObject = true;
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/JsonToSimpl/JsonToSimplGenericMaster.cs b/src/PepperDash.Core/JsonToSimpl/JsonToSimplGenericMaster.cs
index 423c435a..14f7eaa8 100644
--- a/src/PepperDash.Core/JsonToSimpl/JsonToSimplGenericMaster.cs
+++ b/src/PepperDash.Core/JsonToSimpl/JsonToSimplGenericMaster.cs
@@ -1,42 +1,37 @@
-extern alias NewtonsoftJson;
-
-using System;
+using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
-using PepperDash.Core.Logging;
-using Renci.SshNet.Messages;
-using JObject = NewtonsoftJson::Newtonsoft.Json.Linq.JObject;
-using JValue = NewtonsoftJson::Newtonsoft.Json.Linq.JValue;
+using Newtonsoft.Json.Linq;
-namespace PepperDash.Core.JsonToSimpl;
-
-///
-/// Generic Master
-///
- public class JsonToSimplGenericMaster : JsonToSimplMaster
+namespace PepperDash.Core.JsonToSimpl
{
+ ///
+ /// Represents a JsonToSimplGenericMaster
+ ///
+ public class JsonToSimplGenericMaster : JsonToSimplMaster
+ {
/*****************************************************************************************/
/** Privates **/
-
+
// The JSON file in JObject form
// For gathering the incoming data
object StringBuilderLock = new object();
// To prevent multiple same-file access
static object WriteLock = new object();
- ///
- /// Callback action for saving
- ///
+ ///
+ /// Gets or sets the SaveCallback
+ ///
public Action SaveCallback { get; set; }
/*****************************************************************************************/
///
- /// SIMPL+ default constructor.
- ///
+ /// SIMPL+ default constructor.
+ ///
public JsonToSimplGenericMaster()
- {
+ {
}
///
@@ -76,8 +71,7 @@ namespace PepperDash.Core.JsonToSimpl;
}
catch (Exception e)
{
- this.LogException(e, "JSON parsing failed:\r{0}", e.Message);
- this.LogVerbose("Stack Trace:\r{0}", e.StackTrace);
+ Debug.Console(0, this, "JSON parsing failed:\r{0}", e);
}
}
@@ -88,40 +82,41 @@ namespace PepperDash.Core.JsonToSimpl;
public override void Save()
{
// this code is duplicated in the other masters!!!!!!!!!!!!!
- UnsavedValues = new Dictionary();
+ UnsavedValues = new Dictionary();
// Make each child update their values into master object
foreach (var child in Children)
{
- this.LogDebug("Master. checking child [{0}] for updates to save", child.Key);
+ Debug.Console(1, this, "Master. checking child [{0}] for updates to save", child.Key);
child.UpdateInputsForMaster();
}
if (UnsavedValues == null || UnsavedValues.Count == 0)
{
- this.LogDebug("Master. No updated values to save. Skipping");
+ Debug.Console(1, this, "Master. No updated values to save. Skipping");
return;
}
lock (WriteLock)
{
- this.LogDebug("Saving");
+ Debug.Console(1, this, "Saving");
foreach (var path in UnsavedValues.Keys)
{
var tokenToReplace = JsonObject.SelectToken(path);
if (tokenToReplace != null)
{// It's found
tokenToReplace.Replace(UnsavedValues[path]);
- this.LogDebug("Master Updating '{0}'", path);
+ Debug.Console(1, this, "Master Updating '{0}'", path);
}
else // No token. Let's make one
{
- this.LogDebug("Master Cannot write value onto missing property: '{0}'", path);
+ Debug.Console(1, "Master Cannot write value onto missing property: '{0}'", path);
}
}
}
if (SaveCallback != null)
SaveCallback(JsonObject.ToString());
else
- this.LogDebug("WARNING: No save callback defined.");
+ Debug.Console(0, this, "WARNING: No save callback defined.");
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/JsonToSimpl/JsonToSimplMaster.cs b/src/PepperDash.Core/JsonToSimpl/JsonToSimplMaster.cs
index 2b2a504f..b3fa92a3 100644
--- a/src/PepperDash.Core/JsonToSimpl/JsonToSimplMaster.cs
+++ b/src/PepperDash.Core/JsonToSimpl/JsonToSimplMaster.cs
@@ -1,40 +1,34 @@
-extern alias NewtonsoftJson;
-
using System;
using System.Collections.Generic;
using System.IO;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
-using JArray = NewtonsoftJson::Newtonsoft.Json.Linq.JArray;
-using JObject = NewtonsoftJson::Newtonsoft.Json.Linq.JObject;
-using JValue = NewtonsoftJson::Newtonsoft.Json.Linq.JValue;
-using JsonSerializationException = NewtonsoftJson::Newtonsoft.Json.JsonSerializationException;
-using JsonTextReader = NewtonsoftJson::Newtonsoft.Json.JsonTextReader;
-using PepperDash.Core.Logging;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
-namespace PepperDash.Core.JsonToSimpl;
-
-///
-/// Abstract base class for JsonToSimpl interactions
-///
+namespace PepperDash.Core.JsonToSimpl
+{
+ ///
+ /// Abstract base class for JsonToSimpl interactions
+ ///
public abstract class JsonToSimplMaster : IKeyed
{
- ///
- /// Notifies of bool change
- ///
+ ///
+ /// Notifies of bool change
+ ///
public event EventHandler BoolChange;
- ///
- /// Notifies of ushort change
- ///
+ ///
+ /// Notifies of ushort change
+ ///
public event EventHandler UshrtChange;
- ///
- /// Notifies of string change
- ///
+ ///
+ /// Notifies of string change
+ ///
public event EventHandler StringChange;
- ///
- /// A collection of associated child modules
- ///
+ ///
+ /// A collection of associated child modules
+ ///
protected List Children = new List();
/*****************************************************************************************/
@@ -44,9 +38,9 @@ namespace PepperDash.Core.JsonToSimpl;
///
public string Key { get { return UniqueID; } }
- ///
- /// A unique ID
- ///
+ ///
+ /// Gets or sets the UniqueID
+ ///
public string UniqueID { get; protected set; }
///
@@ -88,9 +82,9 @@ namespace PepperDash.Core.JsonToSimpl;
}
}
- ///
- ///
- ///
+ ///
+ /// Gets or sets the JsonObject
+ ///
public JObject JsonObject { get; protected set; }
/*****************************************************************************************/
@@ -143,15 +137,16 @@ namespace PepperDash.Core.JsonToSimpl;
{
if (UnsavedValues.ContainsKey(path))
{
- this.LogWarning("Master[{0}] WARNING - Attempt to add duplicate value for path '{1}'.\r Ingoring. Please ensure that path does not exist on multiple modules.", UniqueID, path);
+ Debug.Console(0, "Master[{0}] WARNING - Attempt to add duplicate value for path '{1}'.\r Ingoring. Please ensure that path does not exist on multiple modules.", UniqueID, path);
}
else
UnsavedValues.Add(path, value);
+ //Debug.Console(0, "Master[{0}] Unsaved size={1}", UniqueID, UnsavedValues.Count);
}
- ///
- /// Saves the file
- ///
+ ///
+ /// Saves the file
+ ///
public abstract void Save();
@@ -160,14 +155,18 @@ namespace PepperDash.Core.JsonToSimpl;
///
public static class JsonFixes
{
- ///
- /// Deserializes a string into a JObject
- ///
- ///
- ///
+ ///
+ /// Deserializes a string into a JObject
+ ///
+ ///
+ ///
public static JObject ParseObject(string json)
{
- using (var reader = new JsonTextReader(new System.IO.StringReader(json)))
+ #if NET6_0
+ using (var reader = new JsonTextReader(new System.IO.StringReader(json)))
+#else
+ using (var reader = new JsonTextReader(new Crestron.SimplSharp.CrestronIO.StringReader(json)))
+#endif
{
var startDepth = reader.Depth;
var obj = JObject.Load(reader);
@@ -177,15 +176,21 @@ namespace PepperDash.Core.JsonToSimpl;
}
}
- ///
- /// Deserializes a string into a JArray
- ///
- ///
- ///
+ ///
+ /// Deserializes a string into a JArray
+ ///
+ ///
+ ///
+ ///
+ /// ParseArray method
+ ///
public static JArray ParseArray(string json)
{
-
- using (var reader = new JsonTextReader(new System.IO.StringReader(json)))
+ #if NET6_0
+ using (var reader = new JsonTextReader(new System.IO.StringReader(json)))
+#else
+ using (var reader = new JsonTextReader(new Crestron.SimplSharp.CrestronIO.StringReader(json)))
+#endif
{
var startDepth = reader.Depth;
var obj = JArray.Load(reader);
@@ -228,12 +233,12 @@ namespace PepperDash.Core.JsonToSimpl;
}
}
- ///
- /// Helper event
- ///
- ///
- ///
- ///
+ ///
+ /// Helper event
+ ///
+ ///
+ ///
+ ///
protected void OnStringChange(string value, ushort index, ushort type)
{
if (StringChange != null)
@@ -244,3 +249,4 @@ namespace PepperDash.Core.JsonToSimpl;
}
}
}
+}
diff --git a/src/PepperDash.Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs b/src/PepperDash.Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs
index bbd8df2c..e142d1bc 100644
--- a/src/PepperDash.Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs
+++ b/src/PepperDash.Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs
@@ -1,31 +1,27 @@
-extern alias NewtonsoftJson;
-
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
-using JObject = NewtonsoftJson::Newtonsoft.Json.Linq.JObject;
-using JValue = NewtonsoftJson::Newtonsoft.Json.Linq.JValue;
+using Newtonsoft.Json.Linq;
using PepperDash.Core.Config;
-using PepperDash.Core.Logging;
-namespace PepperDash.Core.JsonToSimpl;
-
-///
-/// Portal File Master
-///
-public class JsonToSimplPortalFileMaster : JsonToSimplMaster
+namespace PepperDash.Core.JsonToSimpl
{
+ ///
+ /// Portal File Master
+ ///
+ public class JsonToSimplPortalFileMaster : JsonToSimplMaster
+ {
///
/// Sets the filepath as well as registers this with the Global.Masters list
///
public string PortalFilepath { get; private set; }
- ///
- /// File path of the actual file being read (Portal or local)
- ///
- public string ActualFilePath { get; private set; }
+ ///
+ /// Gets or sets the ActualFilePath
+ ///
+ public string ActualFilePath { get; private set; }
/*****************************************************************************************/
/** Privates **/
@@ -37,10 +33,10 @@ public class JsonToSimplPortalFileMaster : JsonToSimplMaster
/*****************************************************************************************/
///
- /// SIMPL+ default constructor.
- ///
+ /// SIMPL+ default constructor.
+ ///
public JsonToSimplPortalFileMaster()
- {
+ {
}
///
@@ -62,19 +58,19 @@ public class JsonToSimplPortalFileMaster : JsonToSimplMaster
// If the portal file is xyz.json, then
// the file we want to check for first will be called xyz.local.json
var localFilepath = Path.ChangeExtension(PortalFilepath, "local.json");
- this.LogInformation("Checking for local file {0}", localFilepath);
+ Debug.Console(0, this, "Checking for local file {0}", localFilepath);
var actualLocalFile = GetActualFileInfoFromPath(localFilepath);
if (actualLocalFile != null)
{
ActualFilePath = actualLocalFile.FullName;
- OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange);
+ OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange);
}
// If the local file does not exist, then read the portal file xyz.json
// and create the local.
else
{
- this.LogInformation("Local JSON file not found {0}\rLoading portal JSON file", localFilepath);
+ Debug.Console(1, this, "Local JSON file not found {0}\rLoading portal JSON file", localFilepath);
var actualPortalFile = GetActualFileInfoFromPath(portalFilepath);
if (actualPortalFile != null)
{
@@ -82,18 +78,19 @@ public class JsonToSimplPortalFileMaster : JsonToSimplMaster
// got the portal file, hand off to the merge / save method
PortalConfigReader.ReadAndMergeFileIfNecessary(actualPortalFile.FullName, newLocalPath);
ActualFilePath = newLocalPath;
- OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange);
+ OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange);
}
else
{
var msg = string.Format("Portal JSON file not found: {0}", PortalFilepath);
- this.LogError(msg);
+ Debug.Console(1, this, msg);
+ ErrorLog.Error(msg);
return;
}
}
// At this point we should have a local file. Do it.
- this.LogInformation("Reading local JSON file {0}", ActualFilePath);
+ Debug.Console(1, "Reading local JSON file {0}", ActualFilePath);
string json = File.ReadToEnd(ActualFilePath, System.Text.Encoding.ASCII);
@@ -107,7 +104,8 @@ public class JsonToSimplPortalFileMaster : JsonToSimplMaster
catch (Exception e)
{
var msg = string.Format("JSON parsing failed:\r{0}", e);
- this.LogError(msg);
+ CrestronConsole.PrintLine(msg);
+ ErrorLog.Error(msg);
return;
}
}
@@ -148,30 +146,30 @@ public class JsonToSimplPortalFileMaster : JsonToSimplMaster
// Make each child update their values into master object
foreach (var child in Children)
{
- this.LogInformation("Master [{0}] checking child [{1}] for updates to save", UniqueID, child.Key);
+ Debug.Console(1, "Master [{0}] checking child [{1}] for updates to save", UniqueID, child.Key);
child.UpdateInputsForMaster();
}
if (UnsavedValues == null || UnsavedValues.Count == 0)
{
- this.LogInformation("Master [{0}] No updated values to save. Skipping", UniqueID);
+ Debug.Console(1, "Master [{0}] No updated values to save. Skipping", UniqueID);
return;
}
lock (FileLock)
{
- this.LogInformation("Saving");
+ Debug.Console(1, "Saving");
foreach (var path in UnsavedValues.Keys)
{
var tokenToReplace = JsonObject.SelectToken(path);
if (tokenToReplace != null)
{// It's found
tokenToReplace.Replace(UnsavedValues[path]);
- this.LogInformation("JSON Master[{0}] Updating '{1}'", UniqueID, path);
+ Debug.Console(1, "JSON Master[{0}] Updating '{1}'", UniqueID, path);
}
else // No token. Let's make one
{
//http://stackoverflow.com/questions/17455052/how-to-set-the-value-of-a-json-path-using-json-net
- this.LogWarning("JSON Master[{0}] Cannot write value onto missing property: '{1}'", UniqueID, path);
+ Debug.Console(1, "JSON Master[{0}] Cannot write value onto missing property: '{1}'", UniqueID, path);
}
}
@@ -185,11 +183,12 @@ public class JsonToSimplPortalFileMaster : JsonToSimplMaster
catch (Exception e)
{
string err = string.Format("Error writing JSON file:\r{0}", e);
- this.LogException(e, "Error writing JSON file: {0}", e.Message);
- this.LogVerbose("Stack Trace:\r{0}", e.StackTrace);
+ Debug.Console(0, err);
+ ErrorLog.Warn(err);
return;
}
}
}
}
}
+}
diff --git a/src/PepperDash.Core/Logging/CrestronEnricher.cs b/src/PepperDash.Core/Logging/CrestronEnricher.cs
index 65369957..c25306ca 100644
--- a/src/PepperDash.Core/Logging/CrestronEnricher.cs
+++ b/src/PepperDash.Core/Logging/CrestronEnricher.cs
@@ -7,38 +7,37 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace PepperDash.Core.Logging;
-
-///
-/// Enriches log events with Crestron-specific context properties, such as the application name based on the device platform.
-///
-public class CrestronEnricher : ILogEventEnricher
+namespace PepperDash.Core.Logging
{
- static readonly string _appName;
-
- static CrestronEnricher()
+ ///
+ /// Represents a CrestronEnricher
+ ///
+ public class CrestronEnricher : ILogEventEnricher
{
- switch (CrestronEnvironment.DevicePlatform)
+ static readonly string _appName;
+
+ static CrestronEnricher()
{
- case eDevicePlatform.Appliance:
- _appName = $"App {InitialParametersClass.ApplicationNumber}";
- break;
- case eDevicePlatform.Server:
- _appName = $"{InitialParametersClass.RoomId}";
- break;
+ switch (CrestronEnvironment.DevicePlatform)
+ {
+ case eDevicePlatform.Appliance:
+ _appName = $"App {InitialParametersClass.ApplicationNumber}";
+ break;
+ case eDevicePlatform.Server:
+ _appName = $"{InitialParametersClass.RoomId}";
+ break;
+ }
+ }
+
+
+ ///
+ /// Enrich method
+ ///
+ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
+ {
+ var property = propertyFactory.CreateProperty("App", _appName);
+
+ logEvent.AddOrUpdateProperty(property);
}
}
-
-
- ///
- /// Enriches the log event with Crestron-specific properties.
- ///
- ///
- ///
- public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
- {
- var property = propertyFactory.CreateProperty("App", _appName);
-
- logEvent.AddOrUpdateProperty(property);
- }
}
diff --git a/src/PepperDash.Core/Logging/Debug.cs b/src/PepperDash.Core/Logging/Debug.cs
index 97a6e93e..97ac5890 100644
--- a/src/PepperDash.Core/Logging/Debug.cs
+++ b/src/PepperDash.Core/Logging/Debug.cs
@@ -1,17 +1,12 @@
-extern alias NewtonsoftJson;
-
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
-using System.Timers;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronDataStore;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp.CrestronLogger;
-using Formatting = NewtonsoftJson::Newtonsoft.Json.Formatting;
-using JsonConvert = NewtonsoftJson::Newtonsoft.Json.JsonConvert;
-using PdCore = PepperDash.Core.Abstractions;
+using Newtonsoft.Json;
using PepperDash.Core.Logging;
using Serilog;
using Serilog.Context;
@@ -21,1124 +16,1224 @@ using Serilog.Formatting.Compact;
using Serilog.Formatting.Json;
using Serilog.Templates;
-namespace PepperDash.Core;
-
-///
-/// Contains debug commands for use in various situations
-///
-public static class Debug
+namespace PepperDash.Core
{
- private static readonly string LevelStoreKey = "ConsoleDebugLevel";
- private static readonly string WebSocketLevelStoreKey = "WebsocketDebugLevel";
- private static readonly string ErrorLogLevelStoreKey = "ErrorLogDebugLevel";
- private static readonly string FileLevelStoreKey = "FileDebugLevel";
- private static readonly string DoNotLoadOnNextBootKey = "DoNotLoadOnNextBoot";
-
- private static readonly Dictionary _logLevels = new Dictionary()
+ ///
+ ///
+ public static class Debug
{
- {0, LogEventLevel.Information },
- {3, LogEventLevel.Warning },
- {4, LogEventLevel.Error },
- {5, LogEventLevel.Fatal },
- {1, LogEventLevel.Debug },
- {2, LogEventLevel.Verbose },
- };
+ private static readonly string LevelStoreKey = "ConsoleDebugLevel";
+ private static readonly string WebSocketLevelStoreKey = "WebsocketDebugLevel";
+ private static readonly string ErrorLogLevelStoreKey = "ErrorLogDebugLevel";
+ private static readonly string FileLevelStoreKey = "FileDebugLevel";
- private static ILogger _logger;
-
- // Injected service abstractions. Populated by DebugServiceRegistration.Register()
- // before the Debug static constructor runs. Null when running on hardware without
- // pre-registration (the static ctor falls back to the Crestron SDK directly).
- private static PdCore.ICrestronEnvironment _environment;
- private static PdCore.ICrestronConsole _console;
- private static PdCore.ICrestronDataStore _dataStore;
-
- private static readonly LoggingLevelSwitch consoleLoggingLevelSwitch;
-
- private static readonly LoggingLevelSwitch websocketLoggingLevelSwitch;
-
- private static readonly LoggingLevelSwitch errorLogLevelSwitch;
-
- private static readonly LoggingLevelSwitch fileLoggingLevelSwitch;
-
- ///
- /// The minimum log level for messages to be sent to the console sink
- ///
- public static LogEventLevel WebsocketMinimumLogLevel
- {
- get { return websocketLoggingLevelSwitch.MinimumLevel; }
- }
-
- private static readonly DebugWebsocketSink websocketSink;
-
- ///
- /// The DebugWebsocketSink instance used for sending log messages to connected websocket clients.
- /// This is exposed publicly in case there is a need to call methods on the sink directly, such as SendMessageToClients.
- /// For general logging purposes, use the LogMessage and LogError methods in this class which will send messages to all configured sinks including the websocket sink.
- ///
- public static DebugWebsocketSink WebsocketSink
- {
- get { return websocketSink; }
- }
-
- ///
- /// Describes the folder location where a given program stores it's debug level memory. By default, the
- /// file written will be named appNdebug where N is 1-10.
- ///
- public static string OldFilePathPrefix = @"\nvram\debug\";
-
- ///
- /// Describes the new folder location where a given program stores it's debug level memory. By default, the
- /// file written will be named appNdebug where N is 1-10.
- ///
- public static string NewFilePathPrefix = @"\user\debug\";
-
- ///
- /// The name of the file containing the current debug settings.
- ///
- public static string FileName = "app0Debug.json"; // default; updated in static ctor using _environment.ApplicationNumber
-
- ///
- /// Debug level to set for a given program.
- ///
- public static int Level { get; private set; }
-
- ///
- /// When this is true, the configuration file will NOT be loaded until triggered by either a console command or a signal
- ///
- public static bool DoNotLoadConfigOnNextBoot { get; private set; }
-
- private static DebugContextCollection _contexts;
-
- private const int SaveTimeoutMs = 30000;
-
- ///
- /// Indicates whether the code is running on an appliance or not. Used to determine file paths and other appliance vs server differences
- ///
- public static bool IsRunningOnAppliance; // set in static constructor
-
- ///
- /// Version for the currently loaded PepperDashCore dll
- ///
- public static string PepperDashCoreVersion { get; private set; }
-
- // private static Timer _saveTimer;
-
-
- private const int defaultConsoleDebugTimeoutMin = 120;
-
- private static Timer consoleDebugTimer;
-
- ///
- /// When true, the IncludedExcludedKeys dict will contain keys to include.
- /// When false (default), IncludedExcludedKeys will contain keys to exclude.
- ///
- private static bool _excludeAllMode;
-
- //static bool ExcludeNoKeyMessages;
-
- private static readonly Dictionary IncludedExcludedKeys;
-
- private static readonly LoggerConfiguration _defaultLoggerConfiguration;
-
- private static LoggerConfiguration _loggerConfiguration;
-
- ///
- /// The default logger configuration used by the Debug class. Can be used as a base for creating custom logger configurations.
- /// If changes are made to this configuration after initialization, call ResetLoggerConfiguration to have those changes reflected in the logger.
- ///
- public static LoggerConfiguration LoggerConfiguration => _loggerConfiguration;
-
- static Debug()
- {
- try
+ private static readonly Dictionary _logLevels = new Dictionary()
{
- // Pick up services pre-registered by the composition root (or test setup).
- // If null, fall back to direct Crestron SDK calls for production hardware.
- _environment = PdCore.DebugServiceRegistration.Environment;
- _console = PdCore.DebugServiceRegistration.Console;
- _dataStore = PdCore.DebugServiceRegistration.DataStore;
+ {0, LogEventLevel.Information },
+ {3, LogEventLevel.Warning },
+ {4, LogEventLevel.Error },
+ {5, LogEventLevel.Fatal },
+ {1, LogEventLevel.Debug },
+ {2, LogEventLevel.Verbose },
+ };
- IsRunningOnAppliance = _environment?.DevicePlatform == PdCore.DevicePlatform.Appliance;
+ private static ILogger _logger;
- // Update FileName now that _environment is available (avoids Crestron SDK ref in field initializer).
- FileName = $"app{_environment?.ApplicationNumber ?? 0}Debug.json";
+ private static readonly LoggingLevelSwitch _consoleLogLevelSwitch;
- _dataStore?.InitStore();
+ private static readonly LoggingLevelSwitch _websocketLogLevelSwitch;
- consoleDebugTimer = new Timer(defaultConsoleDebugTimeoutMin * 60000) { AutoReset = false };
- consoleDebugTimer.Elapsed += (s, e) =>
- {
- SetDebugLevel(LogEventLevel.Information);
- _console?.ConsoleCommandResponse($"Console debug level reset to {LogEventLevel.Information} after timeout of {defaultConsoleDebugTimeoutMin} minutes");
- };
+ private static readonly LoggingLevelSwitch _errorLogLevelSwitch;
+
+ private static readonly LoggingLevelSwitch _fileLogLevelSwitch;
+
+ ///
+ /// Gets the minimum log level for the websocket sink.
+ ///
+ public static LogEventLevel WebsocketMinimumLogLevel
+ {
+ get { return _websocketLogLevelSwitch.MinimumLevel; }
+ }
+
+ private static readonly DebugWebsocketSink _websocketSink;
+
+ ///
+ /// Gets the websocket sink for debug logging.
+ ///
+ public static DebugWebsocketSink WebsocketSink
+ {
+ get { return _websocketSink; }
+ }
+
+ ///
+ /// Describes the folder location where a given program stores it's debug level memory. By default, the
+ /// file written will be named appNdebug where N is 1-10.
+ ///
+ public static string OldFilePathPrefix = @"\nvram\debug\";
+
+ ///
+ /// Describes the new folder location where a given program stores it's debug level memory. By default, the
+ /// file written will be named appNdebug where N is 1-10.
+ ///
+ public static string NewFilePathPrefix = @"\user\debug\";
+
+ ///
+ /// The name of the file containing the current debug settings.
+ ///
+ public static string FileName = string.Format(@"app{0}Debug.json", InitialParametersClass.ApplicationNumber);
+
+ ///
+ /// Gets or sets the Level
+ ///
+ public static int Level { get; private set; }
+
+ ///
+ /// Gets or sets the DoNotLoadConfigOnNextBoot
+ ///
+ public static bool DoNotLoadConfigOnNextBoot { get; private set; }
+
+ private static DebugContextCollection _contexts;
+
+ private const int SaveTimeoutMs = 30000;
+
+ ///
+ /// Indicates whether the system is running on an appliance.
+ ///
+ public static bool IsRunningOnAppliance = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance;
+
+ ///
+ /// Gets or sets the PepperDashCoreVersion
+ ///
+ public static string PepperDashCoreVersion { get; private set; }
+
+ private static CTimer _saveTimer;
+
+ ///
+ /// When true, the IncludedExcludedKeys dict will contain keys to include.
+ /// When false (default), IncludedExcludedKeys will contain keys to exclude.
+ ///
+ private static bool _excludeAllMode;
+
+ private static readonly Dictionary IncludedExcludedKeys;
+
+ private static readonly LoggerConfiguration _defaultLoggerConfiguration;
+
+ private static LoggerConfiguration _loggerConfiguration;
+
+ ///
+ /// Gets the logger configuration for the debug logging.
+ ///
+ public static LoggerConfiguration LoggerConfiguration => _loggerConfiguration;
+
+ static Debug()
+ {
+ CrestronDataStoreStatic.InitCrestronDataStore();
var defaultConsoleLevel = GetStoredLogEventLevel(LevelStoreKey);
+
var defaultWebsocketLevel = GetStoredLogEventLevel(WebSocketLevelStoreKey);
+
var defaultErrorLogLevel = GetStoredLogEventLevel(ErrorLogLevelStoreKey);
+
var defaultFileLogLevel = GetStoredLogEventLevel(FileLevelStoreKey);
- consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel);
- websocketLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel);
- errorLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultErrorLogLevel);
- fileLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultFileLogLevel);
+ _consoleLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel);
- websocketSink = TryCreateWebsocketSink();
+ _websocketLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel);
- var appRoot = _environment?.GetApplicationRootDirectory()
- ?? System.IO.Path.GetTempPath();
- var sep = System.IO.Path.DirectorySeparatorChar;
- var appNum = _environment?.ApplicationNumber ?? 0;
- var roomId = _environment?.RoomId ?? 0;
+ _errorLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultErrorLogLevel);
- var logFilePath = IsRunningOnAppliance
- ? $"{appRoot}{sep}user{sep}debug{sep}app{appNum}{sep}global-log.log"
- : $"{appRoot}{sep}user{sep}debug{sep}room{roomId}{sep}global-log.log";
+ _fileLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultFileLogLevel);
- _console?.PrintLine($"Saving log files to {logFilePath}");
+ _websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true));
+
+ var logFilePath = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ?
+ $@"{Directory.GetApplicationRootDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}app{InitialParametersClass.ApplicationNumber}{Path.DirectorySeparatorChar}global-log.log" :
+ $@"{Directory.GetApplicationRootDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}room{InitialParametersClass.RoomId}{Path.DirectorySeparatorChar}global-log.log";
+
+ CrestronConsole.PrintLine($"Saving log files to {logFilePath}");
+
+ var errorLogTemplate = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance
+ ? "{@t:fff}ms [{@l:u4}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}"
+ : "[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}";
- // Build the base Serilog pipeline — sinks that require the Crestron SDK are
- // added conditionally so the logger remains usable in test environments.
_defaultLoggerConfiguration = new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.FromLogContext()
- .WriteTo.Sink(new DebugConsoleSink(new ExpressionTemplate("[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}")), levelSwitch: consoleLoggingLevelSwitch)
+ .Enrich.With(new CrestronEnricher())
+ .WriteTo.Sink(new DebugConsoleSink(new ExpressionTemplate("[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}")), levelSwitch: _consoleLogLevelSwitch)
+ .WriteTo.Sink(_websocketSink, levelSwitch: _websocketLogLevelSwitch)
+ .WriteTo.Sink(new DebugErrorLogSink(new ExpressionTemplate(errorLogTemplate)), levelSwitch: _errorLogLevelSwitch)
.WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath,
rollingInterval: RollingInterval.Day,
restrictedToMinimumLevel: LogEventLevel.Debug,
- retainedFileCountLimit: IsRunningOnAppliance ? 30 : 60,
- levelSwitch: fileLoggingLevelSwitch
+ retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 7 : 14,
+ levelSwitch: _fileLogLevelSwitch
);
- // Websocket sink is null when DebugWebsocketSink failed to construct (e.g. test env).
- if (websocketSink != null)
- _defaultLoggerConfiguration.WriteTo.Sink(websocketSink, levelSwitch: websocketLoggingLevelSwitch);
-
- // Add Crestron-specific enricher and error-log sink only on real hardware.
- if (_environment?.IsHardwareRuntime == true)
+ try
{
- var errorLogTemplate = IsRunningOnAppliance
- ? "{@t:fff}ms [{@l:u4}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}"
- : "[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}";
-
- _defaultLoggerConfiguration
- .Enrich.With(new CrestronEnricher())
- .WriteTo.Sink(new DebugErrorLogSink(new ExpressionTemplate(errorLogTemplate)), levelSwitch: errorLogLevelSwitch);
+ if (InitialParametersClass.NumberOfRemovableDrives > 0)
+ {
+ CrestronConsole.PrintLine("{0} RM Drive(s) Present. Initializing CrestronLogger", InitialParametersClass.NumberOfRemovableDrives);
+ _defaultLoggerConfiguration.WriteTo.Sink(new DebugCrestronLoggerSink());
+ }
+ else
+ CrestronConsole.PrintLine("No RM Drive(s) Present. Not using Crestron Logger");
+ }
+ catch (Exception e)
+ {
+ CrestronConsole.PrintLine("Initializing of CrestronLogger failed: {0}", e);
}
+ // Instantiate the root logger
_loggerConfiguration = _defaultLoggerConfiguration;
- _logger = _loggerConfiguration.CreateLogger();
+ _logger = _loggerConfiguration.CreateLogger();
+ // Get the assembly version and print it to console and the log
GetVersion();
- string msg = IsRunningOnAppliance
- ? $"[App {appNum}] Using PepperDash_Core v{PepperDashCoreVersion}"
- : $"[Room {roomId}] Using PepperDash_Core v{PepperDashCoreVersion}";
+ string msg = $"[App {InitialParametersClass.ApplicationNumber}] Using PepperDash_Core v{PepperDashCoreVersion}";
+
+ if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server)
+ {
+ msg = $"[Room {InitialParametersClass.RoomId}] Using PepperDash_Core v{PepperDashCoreVersion}";
+ }
+
+ CrestronConsole.PrintLine(msg);
- _console?.PrintLine(msg);
LogMessage(LogEventLevel.Information, msg);
IncludedExcludedKeys = new Dictionary();
- if (_environment?.RuntimeEnvironment == PdCore.RuntimeEnvironment.SimplSharpPro)
+ if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro)
{
- _console?.AddNewConsoleCommand(SetDoNotLoadOnNextBootFromConsole, "donotloadonnextboot",
- "donotloadonnextboot:P [true/false]: Should the application load on next boot",
- PdCore.ConsoleAccessLevel.AccessOperator);
+ // Add command to console
+ CrestronConsole.AddNewConsoleCommand(SetDoNotLoadOnNextBootFromConsole, "donotloadonnextboot",
+ "donotloadonnextboot:P [true/false]: Should the application load on next boot", ConsoleAccessLevelEnum.AccessOperator);
- _console?.AddNewConsoleCommand(SetDebugFromConsole, "appdebug",
+ CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, "appdebug",
"appdebug:P [0-5]: Sets the application's console debug message level",
- PdCore.ConsoleAccessLevel.AccessOperator);
-
- _console?.AddNewConsoleCommand(ShowDebugLog, "appdebuglog",
+ ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(ShowDebugLog, "appdebuglog",
"appdebuglog:P [all] Use \"all\" for full log.",
- PdCore.ConsoleAccessLevel.AccessOperator);
-
- _console?.AddNewConsoleCommand(s => CrestronLogger.Clear(false), "appdebugclear",
+ ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(s => CrestronLogger.Clear(false), "appdebugclear",
"appdebugclear:P Clears the current custom log",
- PdCore.ConsoleAccessLevel.AccessOperator);
-
- _console?.AddNewConsoleCommand(SetDebugFilterFromConsole, "appdebugfilter",
- "appdebugfilter [params]",
- PdCore.ConsoleAccessLevel.AccessOperator);
+ ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(SetDebugFilterFromConsole, "appdebugfilter",
+ "appdebugfilter [params]", ConsoleAccessLevelEnum.AccessOperator);
}
- DoNotLoadConfigOnNextBoot = GetDoNotLoadOnNextBoot();
+ CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
+
+ LoadMemory();
+
+ var context = _contexts.GetOrCreateItem("DEFAULT");
+ Level = context.Level;
+ DoNotLoadConfigOnNextBoot = context.DoNotLoadOnNextBoot;
if (DoNotLoadConfigOnNextBoot)
- _console?.PrintLine($"Program {appNum} will not load config after next boot. Use console command go:{appNum} to load the config manually");
+ CrestronConsole.PrintLine(string.Format("Program {0} will not load config after next boot. Use console command go:{0} to load the config manually", InitialParametersClass.ApplicationNumber));
- consoleLoggingLevelSwitch.MinimumLevelChanged += (sender, args) =>
+ _errorLogLevelSwitch.MinimumLevelChanged += (sender, args) =>
{
- LogMessage(LogEventLevel.Information, "Console debug level set to {minimumLevel}", consoleLoggingLevelSwitch.MinimumLevel);
+ LogMessage(LogEventLevel.Information, "Error log debug level set to {minimumLevel}", _errorLogLevelSwitch.MinimumLevel);
};
+
+ // Set initial error log level based on platform && stored level. If appliance, use stored level, otherwise default to verbose
+ SetErrorLogMinimumDebugLevel(CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? _errorLogLevelSwitch.MinimumLevel : LogEventLevel.Verbose);
}
- catch (Exception ex)
+
+ ///
+ /// UpdateLoggerConfiguration method
+ ///
+ public static void UpdateLoggerConfiguration(LoggerConfiguration config)
{
- // _logger may not have been initialized yet — do not call LogError here.
- // _console may also be null; fall back to CrestronConsole as last resort.
- // IMPORTANT: this catch block must not throw — any exception escaping a static
- // constructor permanently faults the type, making the entire class unusable.
- try { _console?.PrintLine($"Exception in Debug static constructor: {ex.Message}\r\n{ex.StackTrace}"); }
- catch
+ _loggerConfiguration = config;
+
+ _logger = config.CreateLogger();
+ }
+
+ ///
+ /// ResetLoggerConfiguration method
+ ///
+ public static void ResetLoggerConfiguration()
+ {
+ _loggerConfiguration = _defaultLoggerConfiguration;
+
+ _logger = _loggerConfiguration.CreateLogger();
+ }
+
+ private static LogEventLevel GetStoredLogEventLevel(string levelStoreKey)
+ {
+ try
{
- try { CrestronConsole.PrintLine($"Exception in Debug static constructor: {ex.Message}\r\n{ex.StackTrace}"); }
- catch { /* CrestronConsole unavailable (test/dev env) — swallow to keep type initializer healthy */ }
- }
- }
- finally
- {
- // Guarantee _logger is never null — all Debug.Log* calls are safe even if the
- // ctor failed partway through (e.g. on a dev machine without Crestron hardware).
- _logger ??= new LoggerConfiguration().MinimumLevel.Fatal().CreateLogger();
- }
- }
+ var result = CrestronDataStoreStatic.GetLocalIntValue(levelStoreKey, out int logLevel);
- /// Creates the WebSocket sink, returning null if construction fails in a test/dev environment.
- private static DebugWebsocketSink? TryCreateWebsocketSink()
- {
- try
- {
- return new DebugWebsocketSink(new JsonFormatter(renderMessage: true));
- }
- catch
- {
- _console?.PrintLine("DebugWebsocketSink could not be created in this environment; websocket logging disabled.");
- return null;
- }
- }
-
- private static bool GetDoNotLoadOnNextBoot()
- {
- if (_dataStore == null) return false;
-
- if (!_dataStore.TryGetLocalBool(DoNotLoadOnNextBootKey, out var doNotLoad))
- {
- LogError("Error retrieving DoNotLoadOnNextBoot value");
- return false;
- }
-
- return doNotLoad;
- }
-
- ///
- /// Updates the LoggerConfiguration used by the Debug class.
- /// This allows for changing logger settings such as sinks and output templates.
- /// After calling this method, the new configuration will be used for all subsequent log messages.
- ///
- ///
- public static void UpdateLoggerConfiguration(LoggerConfiguration config)
- {
- _loggerConfiguration = config;
-
- _logger = config.CreateLogger();
- }
-
- ///
- /// Resets the LoggerConfiguration to the default configuration defined in this class.
- ///
- public static void ResetLoggerConfiguration()
- {
- _loggerConfiguration = _defaultLoggerConfiguration;
-
- _logger = _loggerConfiguration.CreateLogger();
- }
-
- private static LogEventLevel GetStoredLogEventLevel(string levelStoreKey)
- {
- try
- {
- if (_dataStore == null) return LogEventLevel.Information;
-
- if (!_dataStore.TryGetLocalInt(levelStoreKey, out int logLevel))
- {
- _console?.Print($"Unable to retrieve stored log level for {levelStoreKey}. Setting level to {LogEventLevel.Information}\r\n");
- return LogEventLevel.Information;
- }
-
- if (logLevel < 0 || logLevel > 5)
- {
- _console?.PrintLine($"Stored Log level not valid for {levelStoreKey}: {logLevel}. Setting level to {LogEventLevel.Information}");
- return LogEventLevel.Information;
- }
-
- return (LogEventLevel)logLevel;
- }
- catch (Exception ex)
- {
- _console?.PrintLine($"Exception retrieving log level for {levelStoreKey}: {ex.Message}");
- return LogEventLevel.Information;
- }
- }
-
- private static void GetVersion()
- {
- var assembly = Assembly.GetExecutingAssembly();
- var ver =
- assembly
- .GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false);
-
- if (ver != null && ver.Length > 0)
- {
- if (ver[0] is AssemblyInformationalVersionAttribute verAttribute)
- {
- PepperDashCoreVersion = verAttribute.InformationalVersion;
- }
- }
- else
- {
- var version = assembly.GetName().Version;
- PepperDashCoreVersion = version.ToString();
- }
- }
-
- // ///
- // /// Used to save memory when shutting down
- // ///
- // ///
- // static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
- // {
-
- // if (programEventType == eProgramStatusEventType.Stopping)
- // {
- // Log.CloseAndFlush();
-
- // if (_saveTimer != null)
- // {
- // _saveTimer.Stop();
- // _saveTimer = null;
- // }
- // LogMessage(LogEventLevel.Information, "Saving debug settings");
- // // SaveMemory();
- // }
- // }
-
- ///
- /// Callback for console command
- ///
- ///
- public static void SetDebugFromConsole(string levelString)
- {
- try
- {
- if (levelString.Trim() == "?")
- {
- _console?.ConsoleCommandResponse(
-
- "Used to set the minimum level of debug messages to be printed to the console:\r\n" +
- "[LogLevel] [TimeoutInMinutes]\r\n" +
- "If TimeoutInMinutes is not provided, it will default to 120 minutes. If provided, the level will reset to Information after the timeout period elapses.\r\n" +
- "LogLevel can be either a number from 0-5 or a log level name. If using a number, the mapping is as follows:\r\n" +
- $"{_logLevels[0]} = 0\r\n" +
- $"{_logLevels[1]} = 1\r\n" +
- $"{_logLevels[2]} = 2\r\n" +
- $"{_logLevels[3]} = 3\r\n" +
- $"{_logLevels[4]} = 4\r\n" +
- $"{_logLevels[5]} = 5");
- return;
- }
-
- if (string.IsNullOrEmpty(levelString.Trim()))
- {
- _console?.ConsoleCommandResponse($"AppDebug level = {consoleLoggingLevelSwitch.MinimumLevel}");
- return;
- }
-
- // split on space to allow for potential future addition of timeout parameter without breaking existing command usage
- var parts = Regex.Split(levelString.Trim(), @"\s+");
- levelString = parts[0];
-
- if (parts.Length > 1 && long.TryParse(parts[1], out var timeout))
- {
- timeout = Math.Max(timeout, 1); // enforce minimum timeout of 1 minute
- consoleDebugTimer.Interval = timeout * 60000;
- }
-
- // first try to parse as int for backward compatibility with existing usage of numeric levels
-
- if (int.TryParse(levelString, out var levelInt))
- {
- if (levelInt < 0 || levelInt > 5)
+ if (result != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
{
- _console?.ConsoleCommandResponse($"Error: Unable to parse {levelString} to valid log level. If using a number, value must be between 0-5");
- return;
+ CrestronConsole.Print($"Unable to retrieve stored log level for {levelStoreKey}.\r\nError: {result}.\r\nSetting level to {LogEventLevel.Information}\r\n");
+
+ CrestronDataStoreStatic.SetLocalIntValue(levelStoreKey, levelStoreKey == ErrorLogLevelStoreKey ? (int)LogEventLevel.Warning : (int)LogEventLevel.Information);
+
+ return levelStoreKey == ErrorLogLevelStoreKey ? LogEventLevel.Warning : LogEventLevel.Information;
}
- SetDebugLevel((uint)levelInt);
- return;
- }
- // make this parse attempt case-insensitive to allow for more flexible command usage
- if (Enum.TryParse(levelString, true, out var levelEnum))
+ if (logLevel < 0 || logLevel > 5)
+ {
+ CrestronConsole.PrintLine($"Stored Log level not valid for {levelStoreKey}: {logLevel}. Setting level to {LogEventLevel.Information}");
+ return LogEventLevel.Information;
+ }
+
+ CrestronConsole.PrintLine($"Stored log level for {levelStoreKey} is {logLevel}");
+
+ return (LogEventLevel)logLevel;
+ }
+ catch (Exception ex)
{
- SetDebugLevel(levelEnum);
- return;
+ CrestronConsole.PrintLine($"Exception retrieving log level for {levelStoreKey}: {ex.Message}");
+ return LogEventLevel.Information;
}
-
- _console?.ConsoleCommandResponse($"Error: Unable to parse {levelString} to valid log level");
- }
- catch
- {
- _console?.ConsoleCommandResponse("Usage: appdebug:P [0-5]");
- }
- }
-
- ///
- /// Sets the debug level
- ///
- /// Valid values 0-5
- /// Timeout in minutes
- public static void SetDebugLevel(uint level, int timeout = defaultConsoleDebugTimeoutMin)
- {
- if (!_logLevels.TryGetValue(level, out var logLevel))
- {
- logLevel = LogEventLevel.Information;
-
- _console?.ConsoleCommandResponse($"{level} not valid. Setting level to {logLevel}");
-
- SetDebugLevel(logLevel, timeout);
}
- SetDebugLevel(logLevel, timeout);
- }
-
- ///
- /// Sets the debug level
- ///
- /// The log level to set
- /// Timeout in minutes
- public static void SetDebugLevel(LogEventLevel level, int timeout = defaultConsoleDebugTimeoutMin)
- {
- consoleDebugTimer.Stop();
- consoleDebugTimer.Interval = timeout * 60000;
- consoleDebugTimer.Start();
-
- consoleLoggingLevelSwitch.MinimumLevel = level;
-
- var appNum = _environment?.ApplicationNumber ?? 0;
- _console?.ConsoleCommandResponse($"[Application {appNum}], Debug level set to {consoleLoggingLevelSwitch.MinimumLevel}\r\n");
- _console?.ConsoleCommandResponse($"Storing level {level}:{(int)level}");
-
- if (_dataStore != null && !_dataStore.SetLocalInt(LevelStoreKey, (int)level))
- _console?.PrintLine($"Error saving console debug level setting");
- else
- _console?.ConsoleCommandResponse($"Store result: {(int)level}");
- }
-
- ///
- /// Sets the debug level for the websocket sink
- ///
- ///
- public static void SetWebSocketMinimumDebugLevel(LogEventLevel level)
- {
- websocketLoggingLevelSwitch.MinimumLevel = level;
-
- if (_dataStore != null && !_dataStore.SetLocalUint(WebSocketLevelStoreKey, (uint)level))
- LogMessage(LogEventLevel.Information, "Error saving websocket debug level setting");
-
- LogMessage(LogEventLevel.Information, "Websocket debug level set to {0}", websocketLoggingLevelSwitch.MinimumLevel);
- }
-
-
- ///
- /// Sets the minimum debug level for the error log sink
- ///
- ///
- public static void SetErrorLogMinimumDebugLevel(LogEventLevel level)
- {
- errorLogLevelSwitch.MinimumLevel = level;
-
- if (_dataStore != null && !_dataStore.SetLocalUint(ErrorLogLevelStoreKey, (uint)level))
- LogMessage(LogEventLevel.Information, "Error saving Error Log debug level setting");
-
- LogMessage(LogEventLevel.Information, "Error log debug level set to {0}", errorLogLevelSwitch.MinimumLevel);
- }
-
- ///
- /// Sets the minimum debug level for the file sink
- ///
- public static void SetFileMinimumDebugLevel(LogEventLevel level)
- {
- fileLoggingLevelSwitch.MinimumLevel = level;
-
- if (_dataStore != null && !_dataStore.SetLocalUint(FileLevelStoreKey, (uint)level))
- LogMessage(LogEventLevel.Information, "Error saving File debug level setting");
-
- LogMessage(LogEventLevel.Information, "File debug level set to {0}", fileLoggingLevelSwitch.MinimumLevel);
- }
-
- ///
- /// Callback for console command
- ///
- ///
- public static void SetDoNotLoadOnNextBootFromConsole(string stateString)
- {
- try
+ private static void GetVersion()
{
- if (string.IsNullOrEmpty(stateString.Trim()))
+ var assembly = Assembly.GetExecutingAssembly();
+ var ver =
+ assembly
+ .GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false);
+
+ if (ver != null && ver.Length > 0)
{
- _console?.ConsoleCommandResponse($"DoNotLoadOnNextBoot = {DoNotLoadConfigOnNextBoot}");
- return;
+ if (ver[0] is AssemblyInformationalVersionAttribute verAttribute)
+ {
+ PepperDashCoreVersion = verAttribute.InformationalVersion;
+ }
}
-
- SetDoNotLoadConfigOnNextBoot(bool.Parse(stateString));
- }
- catch
- {
- _console?.ConsoleCommandResponse("Usage: donotloadonnextboot:P [true/false]");
- }
- }
-
- ///
- /// Callback for console command
- ///
- ///
- public static void SetDebugFilterFromConsole(string items)
- {
- var str = items.Trim();
- if (str == "?")
- {
- CrestronConsole.ConsoleCommandResponse("Usage:\r APPDEBUGFILTER key1 key2 key3....\r " +
- "+all: at beginning puts filter into 'default include' mode\r" +
- " All keys that follow will be excluded from output.\r" +
- "-all: at beginning puts filter into 'default exclude all' mode.\r" +
- " All keys that follow will be the only keys that are shown\r" +
- "+nokey: Enables messages with no key (default)\r" +
- "-nokey: Disables messages with no key.\r" +
- "(nokey settings are independent of all other settings)");
- return;
- }
- var keys = Regex.Split(str, @"\s*");
- foreach (var keyToken in keys)
- {
- var lkey = keyToken.ToLower();
- if (lkey == "+all")
- {
- IncludedExcludedKeys.Clear();
- _excludeAllMode = false;
- }
- else if (lkey == "-all")
- {
- IncludedExcludedKeys.Clear();
- _excludeAllMode = true;
- }
- //else if (lkey == "+nokey")
- //{
- // ExcludeNoKeyMessages = false;
- //}
- //else if (lkey == "-nokey")
- //{
- // ExcludeNoKeyMessages = true;
- //}
else
{
- string key;
- if (lkey.StartsWith("-"))
+ var version = assembly.GetName().Version;
+ PepperDashCoreVersion = version.ToString();
+ }
+ }
+
+ ///
+ /// Used to save memory when shutting down
+ ///
+ ///
+ static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+
+ if (programEventType == eProgramStatusEventType.Stopping)
+ {
+ Log.CloseAndFlush();
+
+ if (_saveTimer != null)
{
- key = lkey.Substring(1);
- // if in exclude all mode, we need to remove this from the inclusions
- if (_excludeAllMode)
+ _saveTimer.Stop();
+ _saveTimer = null;
+ }
+ LogMessage(LogEventLevel.Information, "Saving debug settings");
+ SaveMemory();
+ }
+ }
+
+ ///
+ /// Callback for console command
+ ///
+ ///
+ ///
+ /// SetDebugFromConsole method
+ ///
+ public static void SetDebugFromConsole(string levelString)
+ {
+ try
+ {
+ if (levelString.Trim() == "?")
+ {
+ CrestronConsole.ConsoleCommandResponse(
+ "Used to set the minimum level of debug messages:\r\n" +
+ "Usage: appdebug:P [sink] [level]\r\n" +
+ " sink: console (default), errorlog, file, all\r\n" +
+ " all: sets all sinks to the specified level\r\n" +
+ " level: 0-5 or LogEventLevel name\r\n" +
+ $"{_logLevels[0]} = 0\r\n" +
+ $"{_logLevels[1]} = 1\r\n" +
+ $"{_logLevels[2]} = 2\r\n" +
+ $"{_logLevels[3]} = 3\r\n" +
+ $"{_logLevels[4]} = 4\r\n" +
+ $"{_logLevels[5]} = 5");
+ return;
+ }
+
+ if (string.IsNullOrEmpty(levelString.Trim()))
+ {
+ CrestronConsole.ConsoleCommandResponse("Console log level = {0}\r\n", _consoleLogLevelSwitch.MinimumLevel);
+ CrestronConsole.ConsoleCommandResponse("File log level = {0}\r\n", _fileLogLevelSwitch.MinimumLevel);
+ CrestronConsole.ConsoleCommandResponse("Error log level = {0}\r\n", _errorLogLevelSwitch.MinimumLevel);
+ return;
+ }
+
+ // Parse tokens: first token is sink (defaults to console), second token is level
+ var tokens = levelString.Trim().Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
+
+ string sinkName;
+ string levelToken;
+
+ if (tokens.Length == 1)
+ {
+ // Single token - assume it's a level for console sink
+ sinkName = "console";
+ levelToken = tokens[0];
+ }
+ else if (tokens.Length == 2)
+ {
+ // Two tokens - first is sink, second is level
+ sinkName = tokens[0].ToLowerInvariant();
+ levelToken = tokens[1];
+ }
+ else
+ {
+ CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [sink] [level]");
+ return;
+ }
+
+ // Parse the level using the same logic as before
+ LogEventLevel level;
+
+ if (int.TryParse(levelToken, out var levelInt))
+ {
+ if (levelInt < 0 || levelInt > 5)
{
- if (IncludedExcludedKeys.ContainsKey(key))
- IncludedExcludedKeys.Remove(key);
+ CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelToken} to valid log level. If using a number, value must be between 0-5");
+ return;
}
- // otherwise include all mode, add to the exclusions
- else
+
+ if (!_logLevels.TryGetValue((uint)levelInt, out level))
{
- IncludedExcludedKeys[key] = new object();
+ level = LogEventLevel.Information;
+ CrestronConsole.ConsoleCommandResponse($"{levelInt} not valid. Setting level to {level}");
}
}
- else if (lkey.StartsWith("+"))
+ else if (Enum.TryParse(levelToken, true, out level))
{
- key = lkey.Substring(1);
- // if in exclude all mode, we need to add this as inclusion
- if (_excludeAllMode)
- {
+ // Successfully parsed as LogEventLevel enum
+ }
+ else
+ {
+ CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelToken} to valid log level");
+ return;
+ }
- IncludedExcludedKeys[key] = new object();
- }
- // otherwise include all mode, remove this from exclusions
- else
+ // Set the level for the specified sink
+ switch (sinkName)
+ {
+ case "console":
+ SetDebugLevel(level);
+ break;
+ case "errorlog":
+ SetErrorLogMinimumDebugLevel(level);
+ break;
+ case "file":
+ SetFileMinimumDebugLevel(level);
+ break;
+ case "all":
+ SetDebugLevel(level);
+ SetErrorLogMinimumDebugLevel(level);
+ SetFileMinimumDebugLevel(level);
+ break;
+ default:
+ CrestronConsole.ConsoleCommandResponse($"Error: Unknown sink '{sinkName}'. Valid sinks: console, errorlog, file");
+ break;
+ }
+ }
+ catch
+ {
+ CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [sink] [level]");
+ }
+ }
+
+ ///
+ /// Sets the debug level
+ ///
+ /// Valid values 0-5
+ ///
+ /// SetDebugLevel method
+ ///
+ public static void SetDebugLevel(uint level)
+ {
+ if (!_logLevels.TryGetValue(level, out var logLevel))
+ {
+ logLevel = LogEventLevel.Information;
+
+ CrestronConsole.ConsoleCommandResponse($"{level} not valid. Setting level to {logLevel}");
+
+ SetDebugLevel(logLevel);
+ }
+
+ SetDebugLevel(logLevel);
+ }
+
+ ///
+ /// SetDebugLevel method
+ ///
+ public static void SetDebugLevel(LogEventLevel level)
+ {
+ _consoleLogLevelSwitch.MinimumLevel = level;
+
+ CrestronConsole.ConsoleCommandResponse("[Application {0}] Debug level set to {1}\r\n",
+ InitialParametersClass.ApplicationNumber, _consoleLogLevelSwitch.MinimumLevel);
+
+ CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}");
+
+ var err = CrestronDataStoreStatic.SetLocalIntValue(LevelStoreKey, (int)level);
+
+ CrestronConsole.ConsoleCommandResponse($"Store result: {err}:{(int)level}");
+
+ if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
+ CrestronConsole.PrintLine($"Error saving console debug level setting: {err}");
+ }
+
+ ///
+ /// SetWebSocketMinimumDebugLevel method
+ ///
+ public static void SetWebSocketMinimumDebugLevel(LogEventLevel level)
+ {
+ _websocketLogLevelSwitch.MinimumLevel = level;
+
+ var err = CrestronDataStoreStatic.SetLocalUintValue(WebSocketLevelStoreKey, (uint)level);
+
+ if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
+ LogMessage(LogEventLevel.Information, "Error saving websocket debug level setting: {erro}", err);
+
+ LogMessage(LogEventLevel.Information, "Websocket debug level set to {0}", _websocketLogLevelSwitch.MinimumLevel);
+ }
+
+ ///
+ /// SetErrorLogMinimumDebugLevel method
+ ///
+ public static void SetErrorLogMinimumDebugLevel(LogEventLevel level)
+ {
+ _errorLogLevelSwitch.MinimumLevel = level;
+
+ CrestronConsole.ConsoleCommandResponse("[Application {0}] Error log level set to {1}\r\n",
+ InitialParametersClass.ApplicationNumber, _errorLogLevelSwitch.MinimumLevel);
+
+ CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}");
+
+ var err = CrestronDataStoreStatic.SetLocalIntValue(ErrorLogLevelStoreKey, (int)level);
+
+ CrestronConsole.ConsoleCommandResponse($"Store result: {err}:{(int)level}");
+
+ if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
+ CrestronConsole.PrintLine($"Error saving error log debug level setting: {err}");
+ }
+
+ ///
+ /// SetFileMinimumDebugLevel method
+ ///
+ public static void SetFileMinimumDebugLevel(LogEventLevel level)
+ {
+ _fileLogLevelSwitch.MinimumLevel = level;
+
+ CrestronConsole.ConsoleCommandResponse("[Application {0}] File log level set to {1}\r\n",
+ InitialParametersClass.ApplicationNumber, _fileLogLevelSwitch.MinimumLevel);
+
+ CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}");
+
+ var err = CrestronDataStoreStatic.SetLocalIntValue(FileLevelStoreKey, (int)level);
+
+ CrestronConsole.ConsoleCommandResponse($"Store result: {err}:{(int)level}");
+
+ if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
+ CrestronConsole.PrintLine($"Error saving file debug level setting: {err}");
+ }
+
+ ///
+ /// Callback for console command
+ ///
+ ///
+ ///
+ /// SetDoNotLoadOnNextBootFromConsole method
+ ///
+ public static void SetDoNotLoadOnNextBootFromConsole(string stateString)
+ {
+ try
+ {
+ if (string.IsNullOrEmpty(stateString.Trim()))
+ {
+ CrestronConsole.ConsoleCommandResponse("DoNotLoadOnNextBoot = {0}", DoNotLoadConfigOnNextBoot);
+ return;
+ }
+
+ SetDoNotLoadConfigOnNextBoot(bool.Parse(stateString));
+ }
+ catch
+ {
+ CrestronConsole.ConsoleCommandResponse("Usage: donotloadonnextboot:P [true/false]");
+ }
+ }
+
+ ///
+ /// Callback for console command
+ ///
+ ///
+ ///
+ /// SetDebugFilterFromConsole method
+ ///
+ public static void SetDebugFilterFromConsole(string items)
+ {
+ var str = items.Trim();
+ if (str == "?")
+ {
+ CrestronConsole.ConsoleCommandResponse("Usage:\r APPDEBUGFILTER key1 key2 key3....\r " +
+ "+all: at beginning puts filter into 'default include' mode\r" +
+ " All keys that follow will be excluded from output.\r" +
+ "-all: at beginning puts filter into 'default exclude all' mode.\r" +
+ " All keys that follow will be the only keys that are shown\r" +
+ "+nokey: Enables messages with no key (default)\r" +
+ "-nokey: Disables messages with no key.\r" +
+ "(nokey settings are independent of all other settings)");
+ return;
+ }
+ var keys = Regex.Split(str, @"\s*");
+ foreach (var keyToken in keys)
+ {
+ var lkey = keyToken.ToLower();
+ if (lkey == "+all")
+ {
+ IncludedExcludedKeys.Clear();
+ _excludeAllMode = false;
+ }
+ else if (lkey == "-all")
+ {
+ IncludedExcludedKeys.Clear();
+ _excludeAllMode = true;
+ }
+ //else if (lkey == "+nokey")
+ //{
+ // ExcludeNoKeyMessages = false;
+ //}
+ //else if (lkey == "-nokey")
+ //{
+ // ExcludeNoKeyMessages = true;
+ //}
+ else
+ {
+ string key;
+ if (lkey.StartsWith("-"))
{
- if (IncludedExcludedKeys.ContainsKey(key))
- IncludedExcludedKeys.Remove(key);
+ key = lkey.Substring(1);
+ // if in exclude all mode, we need to remove this from the inclusions
+ if (_excludeAllMode)
+ {
+ if (IncludedExcludedKeys.ContainsKey(key))
+ IncludedExcludedKeys.Remove(key);
+ }
+ // otherwise include all mode, add to the exclusions
+ else
+ {
+ IncludedExcludedKeys[key] = new object();
+ }
+ }
+ else if (lkey.StartsWith("+"))
+ {
+ key = lkey.Substring(1);
+ // if in exclude all mode, we need to add this as inclusion
+ if (_excludeAllMode)
+ {
+
+ IncludedExcludedKeys[key] = new object();
+ }
+ // otherwise include all mode, remove this from exclusions
+ else
+ {
+ if (IncludedExcludedKeys.ContainsKey(key))
+ IncludedExcludedKeys.Remove(key);
+ }
}
}
}
}
- }
- ///
- /// sets the settings for a device or creates a new entry
- ///
- ///
- ///
- ///
- public static void SetDeviceDebugSettings(string deviceKey, object settings)
- {
- _contexts.SetDebugSettingsForKey(deviceKey, settings);
- // SaveMemoryOnTimeout();
- }
-
- ///
- /// Gets the device settings for a device by key or returns null
- ///
- ///
- ///
- public static object GetDeviceDebugSettingsForKey(string deviceKey)
- {
- return _contexts.GetDebugSettingsForKey(deviceKey);
- }
-
- ///
- /// Sets the flag to prevent application starting on next boot
- ///
- ///
- public static void SetDoNotLoadConfigOnNextBoot(bool state)
- {
- DoNotLoadConfigOnNextBoot = state;
-
- if (_dataStore != null && !_dataStore.SetLocalBool(DoNotLoadOnNextBootKey, state))
- LogError("Error saving DoNotLoadConfigOnNextBoot setting");
-
- LogInformation("Do Not Load Config on Next Boot set to {state}", DoNotLoadConfigOnNextBoot);
- }
-
- ///
- ///
- ///
- public static void ShowDebugLog(string s)
- {
- if (_environment == null) return; // CrestronLogger not available in test environments
- var loglist = CrestronLogger.PrintTheLog(s.ToLower() == "all");
- foreach (var l in loglist)
- _console?.ConsoleCommandResponse(l + CrestronEnvironment.NewLine);
- }
-
- ///
- /// Log an Exception as an Error
- ///
- /// Exception to log
- /// Message template
- /// Optional IKeyed device. If provided, the Key of the device will be added to the log message
- /// Args to put into message template
- public static void LogMessage(Exception ex, string message, IKeyed device = null, params object[] args)
- {
- using (LogContext.PushProperty("Key", device?.Key))
+ ///
+ /// sets the settings for a device or creates a new entry
+ ///
+ ///
+ ///
+ ///
+ public static void SetDeviceDebugSettings(string deviceKey, object settings)
{
- _logger.Error(ex, message, args);
+ _contexts.SetDebugSettingsForKey(deviceKey, settings);
+ SaveMemoryOnTimeout();
}
- }
- ///
- /// Log a message
- ///
- /// Level to log at
- /// Message template
- /// Optional IKeyed device. If provided, the Key of the device will be added to the log message
- /// Args to put into message template
- public static void LogMessage(LogEventLevel level, string message, IKeyed device = null, params object[] args)
- {
- using (LogContext.PushProperty("Key", device?.Key))
+ ///
+ /// Gets the device settings for a device by key or returns null
+ ///
+ ///
+ ///
+ ///
+ /// GetDeviceDebugSettingsForKey method
+ ///
+ public static object GetDeviceDebugSettingsForKey(string deviceKey)
+ {
+ return _contexts.GetDebugSettingsForKey(deviceKey);
+ }
+
+ ///
+ /// Sets the flag to prevent application starting on next boot
+ ///
+ ///
+ public static void SetDoNotLoadConfigOnNextBoot(bool state)
+ {
+ DoNotLoadConfigOnNextBoot = state;
+ _contexts.GetOrCreateItem("DEFAULT").DoNotLoadOnNextBoot = state;
+ SaveMemoryOnTimeout();
+
+ CrestronConsole.ConsoleCommandResponse("[Application {0}], Do Not Load Config on Next Boot set to {1}",
+ InitialParametersClass.ApplicationNumber, DoNotLoadConfigOnNextBoot);
+ }
+
+ ///
+ /// ShowDebugLog method
+ ///
+ public static void ShowDebugLog(string s)
+ {
+ var loglist = CrestronLogger.PrintTheLog(s.ToLower() == "all");
+ foreach (var l in loglist)
+ CrestronConsole.ConsoleCommandResponse(l + CrestronEnvironment.NewLine);
+ }
+
+ ///
+ /// Log an Exception as an Error
+ ///
+ /// Exception to log
+ /// Message template
+ /// Optional IKeyed device. If provided, the Key of the device will be added to the log message
+ /// Args to put into message template
+ ///
+ /// LogMessage method
+ ///
+ public static void LogMessage(Exception ex, string message, IKeyed device = null, params object[] args)
+ {
+ using (LogContext.PushProperty("Key", device?.Key))
+ {
+ _logger.Error(ex, message, args);
+ }
+ }
+
+ ///
+ /// Log a message
+ ///
+ /// Level to log at
+ /// Message template
+ /// Optional IKeyed device. If provided, the Key of the device will be added to the log message
+ /// Args to put into message template
+ public static void LogMessage(LogEventLevel level, string message, IKeyed device = null, params object[] args)
+ {
+ using (LogContext.PushProperty("Key", device?.Key))
+ {
+ _logger.Write(level, message, args);
+ }
+ }
+
+ ///
+ /// Logs a message at the specified log level.
+ ///
+ /// Level to log at
+ /// Message template
+ /// Args to put into message template
+ public static void LogMessage(LogEventLevel level, string message, params object[] args)
{
_logger.Write(level, message, args);
}
- }
- ///
- /// Log a message with at the specified level.
- ///
- public static void LogMessage(LogEventLevel level, string message, params object[] args)
- {
- _logger.Write(level, message, args);
- }
-
- ///
- /// Log a message with at the specified level and exception.
- ///
- ///
- ///
- ///
- ///
- public static void LogMessage(LogEventLevel level, Exception ex, string message, params object[] args)
- {
- _logger.Write(level, ex, message, args);
- }
-
- ///
- /// Log a message with at the specified level and device context.
- ///
- ///
- ///
- ///
- public static void LogMessage(LogEventLevel level, IKeyed keyed, string message, params object[] args)
- {
- LogMessage(level, message, keyed, args);
- }
-
- ///
- /// Log a message with at the specified level, exception, and device context.
- ///
- ///
- ///
- ///
- ///
- ///
- public static void LogMessage(LogEventLevel level, Exception ex, IKeyed device, string message, params object[] args)
- {
- using (LogContext.PushProperty("Key", device?.Key))
+ ///
+ /// LogMessage method
+ ///
+ public static void LogMessage(LogEventLevel level, Exception ex, string message, params object[] args)
{
_logger.Write(level, ex, message, args);
}
- }
- #region Explicit methods for logging levels
+ ///
+ /// LogMessage method
+ ///
+ public static void LogMessage(LogEventLevel level, IKeyed keyed, string message, params object[] args)
+ {
+ LogMessage(level, message, keyed, args);
+ }
- ///
- /// Log a message with Verbose level and device context.
- ///
- ///
- ///
- ///
- public static void LogVerbose(IKeyed keyed, string message, params object[] args)
- {
- using (LogContext.PushProperty("Key", keyed?.Key))
+ ///
+ /// LogMessage method
+ ///
+ public static void LogMessage(LogEventLevel level, Exception ex, IKeyed device, string message, params object[] args)
+ {
+ using (LogContext.PushProperty("Key", device?.Key))
+ {
+ _logger.Write(level, ex, message, args);
+ }
+ }
+
+ #region Explicit methods for logging levels
+ ///
+ /// LogVerbose method
+ ///
+ public static void LogVerbose(IKeyed keyed, string message, params object[] args)
+ {
+ using (LogContext.PushProperty("Key", keyed?.Key))
+ {
+ _logger.Write(LogEventLevel.Verbose, message, args);
+ }
+ }
+
+ ///
+ /// LogVerbose method
+ ///
+ public static void LogVerbose(Exception ex, IKeyed keyed, string message, params object[] args)
+ {
+ using (LogContext.PushProperty("Key", keyed?.Key))
+ {
+ _logger.Write(LogEventLevel.Verbose, ex, message, args);
+ }
+ }
+
+ ///
+ /// LogVerbose method
+ ///
+ public static void LogVerbose(string message, params object[] args)
{
_logger.Write(LogEventLevel.Verbose, message, args);
}
- }
- ///
- /// Log a message with Verbose level, exception, and device context.
- ///
- public static void LogVerbose(Exception ex, IKeyed keyed, string message, params object[] args)
- {
- using (LogContext.PushProperty("Key", keyed?.Key))
+ ///
+ /// LogVerbose method
+ ///
+ public static void LogVerbose(Exception ex, string message, params object[] args)
{
_logger.Write(LogEventLevel.Verbose, ex, message, args);
}
- }
- ///
- /// Log a message with Verbose level.
- ///
- public static void LogVerbose(string message, params object[] args)
- {
- _logger.Write(LogEventLevel.Verbose, message, args);
- }
+ ///
+ /// LogDebug method
+ ///
+ public static void LogDebug(IKeyed keyed, string message, params object[] args)
+ {
+ using (LogContext.PushProperty("Key", keyed?.Key))
+ {
+ _logger.Write(LogEventLevel.Debug, message, args);
+ }
+ }
- ///
- /// Log a message with Verbose level and exception.
- ///
- public static void LogVerbose(Exception ex, string message, params object[] args)
- {
- _logger.Write(LogEventLevel.Verbose, ex, null, message, args);
- }
+ ///
+ /// LogDebug method
+ ///
+ public static void LogDebug(Exception ex, IKeyed keyed, string message, params object[] args)
+ {
+ using (LogContext.PushProperty("Key", keyed?.Key))
+ {
+ _logger.Write(LogEventLevel.Debug, ex, message, args);
+ }
+ }
- ///
- /// Log a message with Debug level and device context.
- ///
- public static void LogDebug(IKeyed keyed, string message, params object[] args)
- {
- using (LogContext.PushProperty("Key", keyed?.Key))
+ ///
+ /// LogDebug method
+ ///
+ public static void LogDebug(string message, params object[] args)
{
_logger.Write(LogEventLevel.Debug, message, args);
}
- }
- ///
- /// Log a message with Debug level, exception, and device context.
- ///
- public static void LogDebug(Exception ex, IKeyed keyed, string message, params object[] args)
- {
- using (LogContext.PushProperty("Key", keyed?.Key))
+ ///
+ /// LogDebug method
+ ///
+ public static void LogDebug(Exception ex, string message, params object[] args)
{
- _logger.Write(LogEventLevel.Debug, ex, message, args);
+ _logger.Write(LogEventLevel.Debug, ex, null, message, args);
}
- }
- ///
- /// Log a message with Debug level.
- ///
- public static void LogDebug(string message, params object[] args)
- {
- _logger.Write(LogEventLevel.Debug, message, args);
- }
+ ///
+ /// LogInformation method
+ ///
+ public static void LogInformation(IKeyed keyed, string message, params object[] args)
+ {
+ using (LogContext.PushProperty("Key", keyed?.Key))
+ {
+ _logger.Write(LogEventLevel.Information, message, args);
+ }
+ }
- ///
- /// Log a message with Debug level and exception.
- ///
- public static void LogDebug(Exception ex, string message, params object[] args)
- {
- _logger.Write(LogEventLevel.Debug, ex, null, message, args);
- }
+ ///
+ /// LogInformation method
+ ///
+ public static void LogInformation(Exception ex, IKeyed keyed, string message, params object[] args)
+ {
+ using (LogContext.PushProperty("Key", keyed?.Key))
+ {
+ _logger.Write(LogEventLevel.Information, ex, message, args);
+ }
+ }
- ///
- /// Log a message with Information level and device context.
- ///
- public static void LogInformation(IKeyed keyed, string message, params object[] args)
- {
- using (LogContext.PushProperty("Key", keyed?.Key))
+ ///
+ /// LogInformation method
+ ///
+ public static void LogInformation(string message, params object[] args)
{
_logger.Write(LogEventLevel.Information, message, args);
}
- }
- ///
- /// Log a message with Information level, exception, and device context.
- ///
- public static void LogInformation(Exception ex, IKeyed keyed, string message, params object[] args)
- {
- using (LogContext.PushProperty("Key", keyed?.Key))
+ ///
+ /// LogInformation method
+ ///
+ public static void LogInformation(Exception ex, string message, params object[] args)
{
_logger.Write(LogEventLevel.Information, ex, message, args);
}
- }
- ///
- /// Log a message with Information level.
- ///
- public static void LogInformation(string message, params object[] args)
- {
- _logger.Write(LogEventLevel.Information, message, args);
- }
+ ///
+ /// LogWarning method
+ ///
+ public static void LogWarning(IKeyed keyed, string message, params object[] args)
+ {
+ using (LogContext.PushProperty("Key", keyed?.Key))
+ {
+ _logger.Write(LogEventLevel.Warning, message, args);
+ }
+ }
- ///
- /// Log a message with Information level and exception.
- ///
- public static void LogInformation(Exception ex, string message, params object[] args)
- {
- _logger.Write(LogEventLevel.Information, ex, null, message, args);
- }
+ ///
+ /// LogWarning method
+ ///
+ public static void LogWarning(Exception ex, IKeyed keyed, string message, params object[] args)
+ {
+ using (LogContext.PushProperty("Key", keyed?.Key))
+ {
+ _logger.Write(LogEventLevel.Warning, ex, message, args);
+ }
+ }
- ///
- /// Log a message with Warning level and device context.
- ///
- public static void LogWarning(IKeyed keyed, string message, params object[] args)
- {
- using (LogContext.PushProperty("Key", keyed?.Key))
+ ///
+ /// LogWarning method
+ ///
+ public static void LogWarning(string message, params object[] args)
{
_logger.Write(LogEventLevel.Warning, message, args);
}
- }
- ///
- /// Log a message with Warning level, exception, and device context.
- ///
- public static void LogWarning(Exception ex, IKeyed keyed, string message, params object[] args)
- {
- using (LogContext.PushProperty("Key", keyed?.Key))
+ ///
+ /// LogWarning method
+ ///
+ public static void LogWarning(Exception ex, string message, params object[] args)
{
_logger.Write(LogEventLevel.Warning, ex, message, args);
}
- }
- ///
- /// Log a message with Warning level.
- ///
- public static void LogWarning(string message, params object[] args)
- {
- _logger.Write(LogEventLevel.Warning, message, args);
- }
+ ///
+ /// LogError method
+ ///
+ public static void LogError(IKeyed keyed, string message, params object[] args)
+ {
+ using (LogContext.PushProperty("Key", keyed?.Key))
+ {
+ _logger.Write(LogEventLevel.Error, message, args);
+ }
+ }
- ///
- /// Log a message with Warning level and exception.
- ///
- public static void LogWarning(Exception ex, string message, params object[] args)
- {
- _logger.Write(LogEventLevel.Warning, ex, null, message, args);
- }
+ ///
+ /// LogError method
+ ///
+ public static void LogError(Exception ex, IKeyed keyed, string message, params object[] args)
+ {
+ using (LogContext.PushProperty("Key", keyed?.Key))
+ {
+ _logger.Write(LogEventLevel.Error, ex, message, args);
+ }
+ }
- ///
- /// Log a message with Error level and device context.
- ///
- public static void LogError(IKeyed keyed, string message, params object[] args)
- {
- using (LogContext.PushProperty("Key", keyed?.Key))
+ ///
+ /// LogError method
+ ///
+ public static void LogError(string message, params object[] args)
{
_logger.Write(LogEventLevel.Error, message, args);
}
- }
- ///
- /// Log a message with Error level, exception, and device context.
- ///
- public static void LogError(Exception ex, IKeyed keyed, string message, params object[] args)
- {
- using (LogContext.PushProperty("Key", keyed?.Key))
+ ///
+ /// LogError method
+ ///
+ public static void LogError(Exception ex, string message, params object[] args)
{
_logger.Write(LogEventLevel.Error, ex, message, args);
}
- }
- ///
- /// Log a message with Error level.
- ///
- public static void LogError(string message, params object[] args)
- {
- _logger.Write(LogEventLevel.Error, message, args);
- }
+ ///
+ /// LogFatal method
+ ///
+ public static void LogFatal(IKeyed keyed, string message, params object[] args)
+ {
+ using (LogContext.PushProperty("Key", keyed?.Key))
+ {
+ _logger.Write(LogEventLevel.Fatal, message, args);
+ }
+ }
- ///
- /// Log a message with Error level and exception.
- ///
- public static void LogError(Exception ex, string message, params object[] args)
- {
- _logger.Write(LogEventLevel.Error, ex, null, message, args);
- }
+ ///
+ /// LogFatal method
+ ///
+ public static void LogFatal(Exception ex, IKeyed keyed, string message, params object[] args)
+ {
+ using (LogContext.PushProperty("Key", keyed?.Key))
+ {
+ _logger.Write(LogEventLevel.Fatal, ex, message, args);
+ }
+ }
- ///
- /// Log a message with Fatal level and device context.
- ///
- public static void LogFatal(IKeyed keyed, string message, params object[] args)
- {
- using (LogContext.PushProperty("Key", keyed?.Key))
+ ///
+ /// LogFatal method
+ ///
+ public static void LogFatal(string message, params object[] args)
{
_logger.Write(LogEventLevel.Fatal, message, args);
}
- }
- ///
- /// Log a message with Fatal level, exception, and device context.
- ///
- public static void LogFatal(Exception ex, IKeyed keyed, string message, params object[] args)
- {
- using (LogContext.PushProperty("Key", keyed?.Key))
+ ///
+ /// LogFatal method
+ ///
+ public static void LogFatal(Exception ex, string message, params object[] args)
{
_logger.Write(LogEventLevel.Fatal, ex, message, args);
}
- }
- ///
- /// Log a message with Fatal level.
- ///
- ///
- ///
- public static void LogFatal(string message, params object[] args)
- {
- _logger.Write(LogEventLevel.Fatal, message, args);
- }
-
- ///
- /// Log a message with Fatal level and exception.
- ///
- public static void LogFatal(Exception ex, string message, params object[] args)
- {
- _logger.Write(LogEventLevel.Fatal, ex, null, message, args);
- }
-
- #endregion
+ #endregion
- private static void LogMessage(uint level, string format, params object[] items)
- {
- if (!_logLevels.ContainsKey(level)) return;
-
- var logLevel = _logLevels[level];
-
- LogMessage(logLevel, format, items);
- }
-
- private static void LogMessage(uint level, IKeyed keyed, string format, params object[] items)
- {
- if (!_logLevels.ContainsKey(level)) return;
-
- var logLevel = _logLevels[level];
-
- LogMessage(logLevel, keyed, format, items);
- }
-
-
- // ///
- // /// Writes the memory object after timeout
- // ///
- // static void SaveMemoryOnTimeout()
- // {
- // LogInformation("Saving debug settings");
- // if (_saveTimer == null)
- // {
- // _saveTimer = new Timer(SaveTimeoutMs) { AutoReset = false };
- // _saveTimer.Elapsed += (s, e) =>
- // {
- // _saveTimer = null;
- // SaveMemory();
- // };
- // _saveTimer.Start();
- // }
- // else
- // {
- // _saveTimer.Stop();
- // _saveTimer.Interval = SaveTimeoutMs;
- // _saveTimer.Start();
- // }
- // }
-
- // ///
- // /// Writes the memory - use SaveMemoryOnTimeout
- // ///
- // static void SaveMemory()
- // {
- // //var dir = @"\NVRAM\debug";
- // //if (!Directory.Exists(dir))
- // // Directory.Create(dir);
-
- // try
- // {
- // var fileName = GetMemoryFileName();
-
- // LogInformation("Loading debug settings file from {fileName}", fileName);
-
- // using (var sw = new StreamWriter(fileName))
- // {
- // var json = JsonConvert.SerializeObject(_contexts);
- // sw.Write(json);
- // sw.Flush();
- // }
- // }
- // catch (Exception ex)
- // {
- // LogError("Exception saving debug settings: {message}", ex);
- // return;
- // }
- // }
-
- // ///
- // ///
- // ///
- // static void LoadMemory()
- // {
- // var file = GetMemoryFileName();
- // if (File.Exists(file))
- // {
- // using (var sr = new StreamReader(file))
- // {
- // var json = sr.ReadToEnd();
- // _contexts = JsonConvert.DeserializeObject(json);
-
- // if (_contexts != null)
- // {
- // LogMessage(LogEventLevel.Debug, "Debug memory restored from file");
- // return;
- // }
- // }
- // }
-
- // _contexts = new DebugContextCollection();
- // }
-
- ///
- /// Helper to get the file path for this app's debug memory
- ///
- static string GetMemoryFileName()
- {
- if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)
+ private static void LogMessage(uint level, string format, params object[] items)
{
- // CheckForMigration();
- return string.Format(@"\user\debugSettings\program{0}", InitialParametersClass.ApplicationNumber);
+ if (!_logLevels.ContainsKey(level)) return;
+
+ var logLevel = _logLevels[level];
+
+ LogMessage(logLevel, format, items);
}
- return string.Format("{0}{1}user{1}debugSettings{1}{2}.json", Directory.GetApplicationRootDirectory(), Path.DirectorySeparatorChar, InitialParametersClass.RoomId);
+ private static void LogMessage(uint level, IKeyed keyed, string format, params object[] items)
+ {
+ if (!_logLevels.ContainsKey(level)) return;
+
+ var logLevel = _logLevels[level];
+
+ LogMessage(logLevel, keyed, format, items);
+ }
+
+
+ ///
+ /// Prints message to console if current debug level is equal to or higher than the level of this message.
+ /// Uses CrestronConsole.PrintLine.
+ ///
+ ///
+ /// Console format string
+ /// Object parameters
+ [Obsolete("Use LogMessage methods. Will be removed in 2.2.0 and later versions")]
+ public static void Console(uint level, string format, params object[] items)
+ {
+
+ LogMessage(level, format, items);
+
+ //if (IsRunningOnAppliance)
+ //{
+ // CrestronConsole.PrintLine("[{0}]App {1} Lvl {2}:{3}", DateTime.Now.ToString("HH:mm:ss.fff"),
+ // InitialParametersClass.ApplicationNumber,
+ // level,
+ // string.Format(format, items));
+ //}
+ }
+
+ ///
+ /// Logs to Console when at-level, and all messages to error log, including device key
+ ///
+ [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
+ public static void Console(uint level, IKeyed dev, string format, params object[] items)
+ {
+ LogMessage(level, dev, format, items);
+
+ //if (Level >= level)
+ // Console(level, "[{0}] {1}", dev.Key, message);
+ }
+
+ ///
+ /// Prints message to console if current debug level is equal to or higher than the level of this message. Always sends message to Error Log.
+ /// Uses CrestronConsole.PrintLine.
+ ///
+ [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
+ public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel,
+ string format, params object[] items)
+ {
+ LogMessage(level, dev, format, items);
+ }
+
+ ///
+ /// Logs to Console when at-level, and all messages to error log
+ ///
+ [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
+ public static void Console(uint level, ErrorLogLevel errorLogLevel,
+ string format, params object[] items)
+ {
+ LogMessage(level, format, items);
+ }
+
+ ///
+ /// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at
+ /// or above the level provided, then the output will be written to both console and the log. Otherwise
+ /// it will only be written to the log.
+ ///
+ [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
+ public static void ConsoleWithLog(uint level, string format, params object[] items)
+ {
+ LogMessage(level, format, items);
+
+ // var str = string.Format(format, items);
+ //if (Level >= level)
+ // CrestronConsole.PrintLine("App {0}:{1}", InitialParametersClass.ApplicationNumber, str);
+ // CrestronLogger.WriteToLog(str, level);
+ }
+
+ ///
+ /// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at
+ /// or above the level provided, then the output will be written to both console and the log. Otherwise
+ /// it will only be written to the log.
+ ///
+ [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
+ public static void ConsoleWithLog(uint level, IKeyed dev, string format, params object[] items)
+ {
+ LogMessage(level, dev, format, items);
+
+ // var str = string.Format(format, items);
+ // CrestronLogger.WriteToLog(string.Format("[{0}] {1}", dev.Key, str), level);
+ }
+
+ ///
+ /// Prints to log and error log
+ ///
+ ///
+ ///
+ [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
+ public static void LogError(ErrorLogLevel errorLogLevel, string str)
+ {
+ switch (errorLogLevel)
+ {
+ case ErrorLogLevel.Error:
+ LogMessage(LogEventLevel.Error, str);
+ break;
+ case ErrorLogLevel.Warning:
+ LogMessage(LogEventLevel.Warning, str);
+ break;
+ case ErrorLogLevel.Notice:
+ LogMessage(LogEventLevel.Information, str);
+ break;
+ }
+ }
+
+ ///
+ /// Writes the memory object after timeout
+ ///
+ static void SaveMemoryOnTimeout()
+ {
+ Console(0, "Saving debug settings");
+ if (_saveTimer == null)
+ _saveTimer = new CTimer(o =>
+ {
+ _saveTimer = null;
+ SaveMemory();
+ }, SaveTimeoutMs);
+ else
+ _saveTimer.Reset(SaveTimeoutMs);
+ }
+
+ ///
+ /// Writes the memory - use SaveMemoryOnTimeout
+ ///
+ static void SaveMemory()
+ {
+ //var dir = @"\NVRAM\debug";
+ //if (!Directory.Exists(dir))
+ // Directory.Create(dir);
+
+ var fileName = GetMemoryFileName();
+
+ LogMessage(LogEventLevel.Information, "Loading debug settings file from {fileName}", fileName);
+
+ using (var sw = new StreamWriter(fileName))
+ {
+ var json = JsonConvert.SerializeObject(_contexts);
+ sw.Write(json);
+ sw.Flush();
+ }
+ }
+
+ ///
+ ///
+ ///
+ static void LoadMemory()
+ {
+ var file = GetMemoryFileName();
+ if (File.Exists(file))
+ {
+ using (var sr = new StreamReader(file))
+ {
+ var json = sr.ReadToEnd();
+ _contexts = JsonConvert.DeserializeObject(json);
+
+ if (_contexts != null)
+ {
+ LogMessage(LogEventLevel.Debug, "Debug memory restored from file");
+ return;
+ }
+ }
+ }
+
+ _contexts = new DebugContextCollection();
+ }
+
+ ///
+ /// Helper to get the file path for this app's debug memory
+ ///
+ static string GetMemoryFileName()
+ {
+ if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)
+ {
+ // CheckForMigration();
+ return string.Format(@"\user\debugSettings\program{0}", InitialParametersClass.ApplicationNumber);
+ }
+
+ return string.Format("{0}{1}user{1}debugSettings{1}{2}.json", Directory.GetApplicationRootDirectory(), Path.DirectorySeparatorChar, InitialParametersClass.RoomId);
+ }
+
+ ///
+ /// Enumeration of ErrorLogLevel values
+ ///
+ public enum ErrorLogLevel
+ {
+ ///
+ /// Error
+ ///
+ Error,
+ ///
+ /// Warning
+ ///
+ Warning,
+ ///
+ /// Notice
+ ///
+ Notice,
+ ///
+ /// None
+ ///
+ None,
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Logging/DebugConsoleSink.cs b/src/PepperDash.Core/Logging/DebugConsoleSink.cs
index 5e32a8a5..6363caa5 100644
--- a/src/PepperDash.Core/Logging/DebugConsoleSink.cs
+++ b/src/PepperDash.Core/Logging/DebugConsoleSink.cs
@@ -9,45 +9,56 @@ using System.IO;
using System.Text;
-namespace PepperDash.Core;
-
-public class DebugConsoleSink : ILogEventSink
+namespace PepperDash.Core
{
- private readonly ITextFormatter _textFormatter;
-
- public void Emit(LogEvent logEvent)
+ ///
+ /// Represents a DebugConsoleSink
+ ///
+ public class DebugConsoleSink : ILogEventSink
{
- if (!Debug.IsRunningOnAppliance) return;
+ private readonly ITextFormatter _textFormatter;
- /*string message = $"[{logEvent.Timestamp}][{logEvent.Level}][App {InitialParametersClass.ApplicationNumber}]{logEvent.RenderMessage()}";
-
- if(logEvent.Properties.TryGetValue("Key",out var value) && value is ScalarValue sv && sv.Value is string rawValue)
+ ///
+ /// Emit method
+ ///
+ public void Emit(LogEvent logEvent)
{
- message = $"[{logEvent.Timestamp}][{logEvent.Level}][App {InitialParametersClass.ApplicationNumber}][{rawValue,3}]: {logEvent.RenderMessage()}";
- }*/
+ if (!Debug.IsRunningOnAppliance) return;
- var buffer = new StringWriter(new StringBuilder(256));
+ /*string message = $"[{logEvent.Timestamp}][{logEvent.Level}][App {InitialParametersClass.ApplicationNumber}]{logEvent.RenderMessage()}";
- _textFormatter.Format(logEvent, buffer);
+ if(logEvent.Properties.TryGetValue("Key",out var value) && value is ScalarValue sv && sv.Value is string rawValue)
+ {
+ message = $"[{logEvent.Timestamp}][{logEvent.Level}][App {InitialParametersClass.ApplicationNumber}][{rawValue,3}]: {logEvent.RenderMessage()}";
+ }*/
- var message = buffer.ToString();
+ var buffer = new StringWriter(new StringBuilder(256));
+
+ _textFormatter.Format(logEvent, buffer);
+
+ var message = buffer.ToString();
+
+ CrestronConsole.PrintLine(message);
+ }
+
+ public DebugConsoleSink(ITextFormatter formatProvider )
+ {
+ _textFormatter = formatProvider ?? new JsonFormatter();
+ }
- CrestronConsole.PrintLine(message);
}
- public DebugConsoleSink(ITextFormatter formatProvider )
+ public static class DebugConsoleSinkExtensions
{
- _textFormatter = formatProvider ?? new JsonFormatter();
+ ///
+ /// DebugConsoleSink method
+ ///
+ public static LoggerConfiguration DebugConsoleSink(
+ this LoggerSinkConfiguration loggerConfiguration,
+ ITextFormatter formatProvider = null)
+ {
+ return loggerConfiguration.Sink(new DebugConsoleSink(formatProvider));
+ }
}
}
-
-public static class DebugConsoleSinkExtensions
-{
- public static LoggerConfiguration DebugConsoleSink(
- this LoggerSinkConfiguration loggerConfiguration,
- ITextFormatter formatProvider = null)
- {
- return loggerConfiguration.Sink(new DebugConsoleSink(formatProvider));
- }
-}
diff --git a/src/PepperDash.Core/Logging/DebugContext.cs b/src/PepperDash.Core/Logging/DebugContext.cs
new file mode 100644
index 00000000..66e63634
--- /dev/null
+++ b/src/PepperDash.Core/Logging/DebugContext.cs
@@ -0,0 +1,294 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronIO;
+using Newtonsoft.Json;
+
+
+namespace PepperDash.Core
+{
+ ///
+ /// Represents a debugging context
+ ///
+ [Obsolete("DebugContext is no longer supported and will be removed in a future release.")]
+ public class DebugContext
+ {
+ ///
+ /// Describes the folder location where a given program stores it's debug level memory. By default, the
+ /// file written will be named appNdebug where N is 1-10.
+ ///
+ public string Key { get; private set; }
+
+ ///
+ /// The name of the file containing the current debug settings.
+ ///
+ //string FileName = string.Format(@"\nvram\debug\app{0}Debug.json", InitialParametersClass.ApplicationNumber);
+
+ DebugContextSaveData SaveData;
+
+ int SaveTimeoutMs = 30000;
+
+ CTimer SaveTimer;
+
+
+ static List Contexts = new List();
+
+ ///
+ /// Creates or gets a debug context
+ ///
+ ///
+ ///
+ ///
+ /// GetDebugContext method
+ ///
+ public static DebugContext GetDebugContext(string key)
+ {
+ var context = Contexts.FirstOrDefault(c => c.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
+ if (context == null)
+ {
+ context = new DebugContext(key);
+ Contexts.Add(context);
+ }
+ return context;
+ }
+
+ ///
+ /// Do not use. For S+ access.
+ ///
+ public DebugContext() { }
+
+ DebugContext(string key)
+ {
+ Key = key;
+ if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro)
+ {
+ // Add command to console
+ CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, "appdebug",
+ "appdebug:P [0-2]: Sets the application's console debug message level",
+ ConsoleAccessLevelEnum.AccessOperator);
+ }
+
+ CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
+
+ LoadMemory();
+ }
+
+ ///
+ /// Used to save memory when shutting down
+ ///
+ ///
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping)
+ {
+ if (SaveTimer != null)
+ {
+ SaveTimer.Stop();
+ SaveTimer = null;
+ }
+ Console(0, "Saving debug settings");
+ SaveMemory();
+ }
+ }
+
+ ///
+ /// Callback for console command
+ ///
+ ///
+ ///
+ /// SetDebugFromConsole method
+ ///
+ public void SetDebugFromConsole(string levelString)
+ {
+ try
+ {
+ if (string.IsNullOrEmpty(levelString.Trim()))
+ {
+ CrestronConsole.ConsoleCommandResponse("AppDebug level = {0}", SaveData.Level);
+ return;
+ }
+
+ SetDebugLevel(Convert.ToInt32(levelString));
+ }
+ catch
+ {
+ CrestronConsole.PrintLine("Usage: appdebug:P [0-2]");
+ }
+ }
+
+ ///
+ /// Sets the debug level
+ ///
+ /// Valid values 0 (no debug), 1 (critical), 2 (all messages)
+ ///
+ /// SetDebugLevel method
+ ///
+ public void SetDebugLevel(int level)
+ {
+ if (level <= 2)
+ {
+ SaveData.Level = level;
+ SaveMemoryOnTimeout();
+
+ CrestronConsole.PrintLine("[Application {0}], Debug level set to {1}",
+ InitialParametersClass.ApplicationNumber, SaveData.Level);
+ }
+ }
+
+ ///
+ /// Prints message to console if current debug level is equal to or higher than the level of this message.
+ /// Uses CrestronConsole.PrintLine.
+ ///
+ ///
+ /// Console format string
+ /// Object parameters
+ public void Console(uint level, string format, params object[] items)
+ {
+ if (SaveData.Level >= level)
+ CrestronConsole.PrintLine("App {0}:{1}", InitialParametersClass.ApplicationNumber,
+ string.Format(format, items));
+ }
+
+ ///
+ /// Console method
+ ///
+ public void Console(uint level, IKeyed dev, string format, params object[] items)
+ {
+ if (SaveData.Level >= level)
+ Console(level, "[{0}] {1}", dev.Key, string.Format(format, items));
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void Console(uint level, IKeyed dev, Debug.ErrorLogLevel errorLogLevel,
+ string format, params object[] items)
+ {
+ if (SaveData.Level >= level)
+ {
+ var str = string.Format("[{0}] {1}", dev.Key, string.Format(format, items));
+ Console(level, str);
+ LogError(errorLogLevel, str);
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void Console(uint level, Debug.ErrorLogLevel errorLogLevel,
+ string format, params object[] items)
+ {
+ if (SaveData.Level >= level)
+ {
+ var str = string.Format(format, items);
+ Console(level, str);
+ LogError(errorLogLevel, str);
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// LogError method
+ ///
+ public void LogError(Debug.ErrorLogLevel errorLogLevel, string str)
+ {
+ string msg = string.Format("App {0}:{1}", InitialParametersClass.ApplicationNumber, str);
+ switch (errorLogLevel)
+ {
+ case Debug.ErrorLogLevel.Error:
+ ErrorLog.Error(msg);
+ break;
+ case Debug.ErrorLogLevel.Warning:
+ ErrorLog.Warn(msg);
+ break;
+ case Debug.ErrorLogLevel.Notice:
+ ErrorLog.Notice(msg);
+ break;
+ }
+ }
+
+ ///
+ /// Writes the memory object after timeout
+ ///
+ void SaveMemoryOnTimeout()
+ {
+ if (SaveTimer == null)
+ SaveTimer = new CTimer(o =>
+ {
+ SaveTimer = null;
+ SaveMemory();
+ }, SaveTimeoutMs);
+ else
+ SaveTimer.Reset(SaveTimeoutMs);
+ }
+
+ ///
+ /// Writes the memory - use SaveMemoryOnTimeout
+ ///
+ void SaveMemory()
+ {
+ using (StreamWriter sw = new StreamWriter(GetMemoryFileName()))
+ {
+ var json = JsonConvert.SerializeObject(SaveData);
+ sw.Write(json);
+ sw.Flush();
+ }
+ }
+
+ ///
+ ///
+ ///
+ void LoadMemory()
+ {
+ var file = GetMemoryFileName();
+ if (File.Exists(file))
+ {
+ using (StreamReader sr = new StreamReader(file))
+ {
+ var data = JsonConvert.DeserializeObject(sr.ReadToEnd());
+ if (data != null)
+ {
+ SaveData = data;
+ Debug.Console(1, "Debug memory restored from file");
+ return;
+ }
+ else
+ SaveData = new DebugContextSaveData();
+ }
+ }
+ }
+
+ ///
+ /// Helper to get the file path for this app's debug memory
+ ///
+ string GetMemoryFileName()
+ {
+ return string.Format(@"\NVRAM\debugSettings\program{0}-{1}", InitialParametersClass.ApplicationNumber, Key);
+ }
+ }
+
+ ///
+ ///
+ ///
+ public class DebugContextSaveData
+ {
+ ///
+ ///
+ ///
+ public int Level { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Logging/DebugCrestronLoggerSink.cs b/src/PepperDash.Core/Logging/DebugCrestronLoggerSink.cs
index a2e82ec8..76859209 100644
--- a/src/PepperDash.Core/Logging/DebugCrestronLoggerSink.cs
+++ b/src/PepperDash.Core/Logging/DebugCrestronLoggerSink.cs
@@ -3,26 +3,33 @@ using Crestron.SimplSharp.CrestronLogger;
using Serilog.Core;
using Serilog.Events;
-namespace PepperDash.Core.Logging;
-
-public class DebugCrestronLoggerSink : ILogEventSink
+namespace PepperDash.Core.Logging
{
- public void Emit(LogEvent logEvent)
+ ///
+ /// Represents a DebugCrestronLoggerSink
+ ///
+ public class DebugCrestronLoggerSink : ILogEventSink
{
- if (!Debug.IsRunningOnAppliance) return;
-
- string message = $"[{logEvent.Timestamp}][{logEvent.Level}][App {InitialParametersClass.ApplicationNumber}]{logEvent.RenderMessage()}";
-
- if (logEvent.Properties.TryGetValue("Key", out var value) && value is ScalarValue sv && sv.Value is string rawValue)
+ ///
+ /// Emit method
+ ///
+ public void Emit(LogEvent logEvent)
{
- message = $"[{logEvent.Timestamp}][{logEvent.Level}][App {InitialParametersClass.ApplicationNumber}][{rawValue}]: {logEvent.RenderMessage()}";
+ if (!Debug.IsRunningOnAppliance) return;
+
+ string message = $"[{logEvent.Timestamp}][{logEvent.Level}][App {InitialParametersClass.ApplicationNumber}]{logEvent.RenderMessage()}";
+
+ if (logEvent.Properties.TryGetValue("Key", out var value) && value is ScalarValue sv && sv.Value is string rawValue)
+ {
+ message = $"[{logEvent.Timestamp}][{logEvent.Level}][App {InitialParametersClass.ApplicationNumber}][{rawValue}]: {logEvent.RenderMessage()}";
+ }
+
+ CrestronLogger.WriteToLog(message, (uint)logEvent.Level);
}
- CrestronLogger.WriteToLog(message, (uint)logEvent.Level);
- }
-
- public DebugCrestronLoggerSink()
- {
- CrestronLogger.Initialize(1, LoggerModeEnum.RM);
+ public DebugCrestronLoggerSink()
+ {
+ CrestronLogger.Initialize(1, LoggerModeEnum.RM);
+ }
}
}
diff --git a/src/PepperDash.Core/Logging/DebugErrorLogSink.cs b/src/PepperDash.Core/Logging/DebugErrorLogSink.cs
index 3c2e6761..9ab4dc6b 100644
--- a/src/PepperDash.Core/Logging/DebugErrorLogSink.cs
+++ b/src/PepperDash.Core/Logging/DebugErrorLogSink.cs
@@ -9,56 +9,63 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace PepperDash.Core.Logging;
-
-public class DebugErrorLogSink : ILogEventSink
+namespace PepperDash.Core.Logging
{
- private ITextFormatter _formatter;
-
- private Dictionary> _errorLogMap = new Dictionary>
+ ///
+ /// Represents a DebugErrorLogSink
+ ///
+ public class DebugErrorLogSink : ILogEventSink
{
- { LogEventLevel.Verbose, (msg) => ErrorLog.Notice(msg) },
- {LogEventLevel.Debug, (msg) => ErrorLog.Notice(msg) },
- {LogEventLevel.Information, (msg) => ErrorLog.Notice(msg) },
- {LogEventLevel.Warning, (msg) => ErrorLog.Warn(msg) },
- {LogEventLevel.Error, (msg) => ErrorLog.Error(msg) },
- {LogEventLevel.Fatal, (msg) => ErrorLog.Error(msg) }
- };
- public void Emit(LogEvent logEvent)
- {
- string message;
+ private ITextFormatter _formatter;
- if (_formatter == null)
+ private Dictionary> _errorLogMap = new Dictionary>
{
- var programId = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance
- ? $"App {InitialParametersClass.ApplicationNumber}"
- : $"Room {InitialParametersClass.RoomId}";
+ { LogEventLevel.Verbose, (msg) => ErrorLog.Notice(msg) },
+ {LogEventLevel.Debug, (msg) => ErrorLog.Notice(msg) },
+ {LogEventLevel.Information, (msg) => ErrorLog.Notice(msg) },
+ {LogEventLevel.Warning, (msg) => ErrorLog.Warn(msg) },
+ {LogEventLevel.Error, (msg) => ErrorLog.Error(msg) },
+ {LogEventLevel.Fatal, (msg) => ErrorLog.Error(msg) }
+ };
+ ///
+ /// Emit method
+ ///
+ public void Emit(LogEvent logEvent)
+ {
+ string message;
- message = $"[{logEvent.Timestamp}][{logEvent.Level}][{programId}]{logEvent.RenderMessage()}";
-
- if (logEvent.Properties.TryGetValue("Key", out var value) && value is ScalarValue sv && sv.Value is string rawValue)
+ if (_formatter == null)
{
- message = $"[{logEvent.Timestamp}][{logEvent.Level}][{programId}][{rawValue}]: {logEvent.RenderMessage()}";
+ var programId = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance
+ ? $"App {InitialParametersClass.ApplicationNumber}"
+ : $"Room {InitialParametersClass.RoomId}";
+
+ message = $"[{logEvent.Timestamp}][{logEvent.Level}][{programId}]{logEvent.RenderMessage()}";
+
+ if (logEvent.Properties.TryGetValue("Key", out var value) && value is ScalarValue sv && sv.Value is string rawValue)
+ {
+ message = $"[{logEvent.Timestamp}][{logEvent.Level}][{programId}][{rawValue}]: {logEvent.RenderMessage()}";
+ }
+ } else
+ {
+ var buffer = new StringWriter(new StringBuilder(256));
+
+ _formatter.Format(logEvent, buffer);
+
+ message = buffer.ToString();
}
- } else
- {
- var buffer = new StringWriter(new StringBuilder(256));
- _formatter.Format(logEvent, buffer);
+ if(!_errorLogMap.TryGetValue(logEvent.Level, out var handler))
+ {
+ return;
+ }
- message = buffer.ToString();
+ handler(message);
}
- if(!_errorLogMap.TryGetValue(logEvent.Level, out var handler))
+ public DebugErrorLogSink(ITextFormatter formatter = null)
{
- return;
+ _formatter = formatter;
}
-
- handler(message);
- }
-
- public DebugErrorLogSink(ITextFormatter formatter = null)
- {
- _formatter = formatter;
}
}
diff --git a/src/PepperDash.Core/Logging/DebugExtensions.cs b/src/PepperDash.Core/Logging/DebugExtensions.cs
index 7ae40d97..ccee4943 100644
--- a/src/PepperDash.Core/Logging/DebugExtensions.cs
+++ b/src/PepperDash.Core/Logging/DebugExtensions.cs
@@ -2,72 +2,112 @@
using Serilog.Events;
using Log = PepperDash.Core.Debug;
-namespace PepperDash.Core.Logging;
-
-public static class DebugExtensions
+namespace PepperDash.Core.Logging
{
- public static void LogException(this IKeyed device, Exception ex, string message, params object[] args)
+ public static class DebugExtensions
{
- Log.LogMessage(ex, message, device, args);
- }
+ ///
+ /// LogException method
+ ///
+ public static void LogException(this IKeyed device, Exception ex, string message, params object[] args)
+ {
+ Log.LogMessage(ex, message, device: device, args);
+ }
- public static void LogVerbose(this IKeyed device, Exception ex, string message, params object[] args)
- {
- Log.LogMessage(LogEventLevel.Verbose, ex, message, device, args);
- }
+ ///
+ /// LogVerbose method
+ ///
+ public static void LogVerbose(this IKeyed device, Exception ex, string message, params object[] args)
+ {
+ Log.LogVerbose(ex, device, message, args);
+ }
- public static void LogVerbose(this IKeyed device, string message, params object[] args)
- {
- Log.LogMessage(LogEventLevel.Verbose, device, message, args);
- }
+ ///
+ /// LogVerbose method
+ ///
+ public static void LogVerbose(this IKeyed device, string message, params object[] args)
+ {
+ Log.LogVerbose(device, message, args);
+ }
- public static void LogDebug(this IKeyed device, Exception ex, string message, params object[] args)
- {
- Log.LogMessage(LogEventLevel.Debug, ex, message, device, args);
- }
+ ///
+ /// LogDebug method
+ ///
+ public static void LogDebug(this IKeyed device, Exception ex, string message, params object[] args)
+ {
+ Log.LogDebug(ex, device, message, args);
+ }
- public static void LogDebug(this IKeyed device, string message, params object[] args)
- {
- Log.LogMessage(LogEventLevel.Debug, device, message, args);
- }
+ ///
+ /// LogDebug method
+ ///
+ public static void LogDebug(this IKeyed device, string message, params object[] args)
+ {
+ Log.LogDebug(device, message, args);
+ }
- public static void LogInformation(this IKeyed device, Exception ex, string message, params object[] args)
- {
- Log.LogMessage(LogEventLevel.Information, ex, message, device, args);
- }
+ ///
+ /// LogInformation method
+ ///
+ public static void LogInformation(this IKeyed device, Exception ex, string message, params object[] args)
+ {
+ Log.LogInformation(ex, device, message, args);
+ }
- public static void LogInformation(this IKeyed device, string message, params object[] args)
- {
- Log.LogMessage(LogEventLevel.Information, device, message, args);
- }
+ ///
+ /// LogInformation method
+ ///
+ public static void LogInformation(this IKeyed device, string message, params object[] args)
+ {
+ Log.LogInformation(device, message, args);
+ }
- public static void LogWarning(this IKeyed device, Exception ex, string message, params object[] args)
- {
- Log.LogMessage(LogEventLevel.Warning, ex, message, device, args);
- }
+ ///
+ /// LogWarning method
+ ///
+ public static void LogWarning(this IKeyed device, Exception ex, string message, params object[] args)
+ {
+ Log.LogWarning(ex, device, message, args);
+ }
- public static void LogWarning(this IKeyed device, string message, params object[] args)
- {
- Log.LogMessage(LogEventLevel.Warning, device, message, args);
- }
+ ///
+ /// LogWarning method
+ ///
+ public static void LogWarning(this IKeyed device, string message, params object[] args)
+ {
+ Log.LogWarning(device, message, args);
+ }
- public static void LogError(this IKeyed device, Exception ex, string message, params object[] args)
- {
- Log.LogMessage(LogEventLevel.Error, ex, message, device, args);
- }
+ ///
+ /// LogError method
+ ///
+ public static void LogError(this IKeyed device, Exception ex, string message, params object[] args)
+ {
+ Log.LogError(ex, device, message, args);
+ }
- public static void LogError(this IKeyed device, string message, params object[] args)
- {
- Log.LogMessage(LogEventLevel.Error, device, message, args);
- }
+ ///
+ /// LogError method
+ ///
+ public static void LogError(this IKeyed device, string message, params object[] args)
+ {
+ Log.LogError(device, message, args);
+ }
- public static void LogFatal(this IKeyed device, Exception ex, string message, params object[] args)
- {
- Log.LogMessage(LogEventLevel.Fatal, ex, message, device, args);
- }
+ ///
+ /// LogFatal method
+ ///
+ public static void LogFatal(this IKeyed device, Exception ex, string message, params object[] args)
+ {
+ Log.LogFatal(ex, device, message, args);
+ }
- public static void LogFatal(this IKeyed device, string message, params object[] args)
- {
- Log.LogMessage(LogEventLevel.Fatal, device, message, args);
+ ///
+ /// LogFatal method
+ ///
+ public static void LogFatal(this IKeyed device, string message, params object[] args)
+ {
+ Log.LogFatal(device, message, args);
+ }
}
}
diff --git a/src/PepperDash.Core/Logging/DebugMemory.cs b/src/PepperDash.Core/Logging/DebugMemory.cs
index ebd8c5b3..286707b8 100644
--- a/src/PepperDash.Core/Logging/DebugMemory.cs
+++ b/src/PepperDash.Core/Logging/DebugMemory.cs
@@ -1,116 +1,127 @@
-extern alias NewtonsoftJson;
-
-using System.Collections.Generic;
-using System.Threading;
+using System.Collections.Generic;
using Crestron.SimplSharp;
-using JsonProperty = NewtonsoftJson::Newtonsoft.Json.JsonPropertyAttribute;
+using Newtonsoft.Json;
-namespace PepperDash.Core.Logging;
-
-///
-/// Class to persist current Debug settings across program restarts
-///
-public class DebugContextCollection
+namespace PepperDash.Core.Logging
{
- ///
- /// To prevent threading issues with the DeviceDebugSettings collection
- ///
- private readonly object _deviceDebugSettingsLock = new();
+ ///
+ /// Represents a DebugContextCollection
+ ///
+ public class DebugContextCollection
+ {
+ ///
+ /// To prevent threading issues with the DeviceDebugSettings collection
+ ///
+ private readonly CCriticalSection _deviceDebugSettingsLock;
- [JsonProperty("items")]
- private readonly Dictionary _items = new Dictionary();
+ [JsonProperty("items")] private readonly Dictionary _items;
- ///
- /// Collection of the debug settings for each device where the dictionary key is the device key
- ///
- [JsonProperty("deviceDebugSettings")]
- private Dictionary DeviceDebugSettings { get; set; } = new Dictionary();
+ ///
+ /// Collection of the debug settings for each device where the dictionary key is the device key
+ ///
+ [JsonProperty("deviceDebugSettings")]
+ private Dictionary DeviceDebugSettings { get; set; }
- ///
- /// Default constructor
- ///
- public DebugContextCollection()
- {
+ ///
+ /// Default constructor
+ ///
+ public DebugContextCollection()
+ {
+ _deviceDebugSettingsLock = new CCriticalSection();
+ DeviceDebugSettings = new Dictionary();
+ _items = new Dictionary();
+ }
- }
+ ///
+ /// Sets the level of a given context item, and adds that item if it does not
+ /// exist
+ ///
+ ///
+ ///
+ ///
+ /// SetLevel method
+ ///
+ public void SetLevel(string contextKey, int level)
+ {
+ if (level < 0 || level > 2)
+ return;
+ GetOrCreateItem(contextKey).Level = level;
+ }
- ///
- /// Sets the level of a given context item, and adds that item if it does not
- /// exist
- ///
- ///
- ///
- ///
- /// SetLevel method
- ///
- public void SetLevel(string contextKey, int level)
- {
- if (level < 0 || level > 2)
- return;
- GetOrCreateItem(contextKey).Level = level;
- }
-
- ///
- /// Gets a level or creates it if not existing
- ///
- ///
- ///
- ///
- /// GetOrCreateItem method
- ///
- public DebugContextItem GetOrCreateItem(string contextKey)
- {
- if (!_items.ContainsKey(contextKey))
- _items[contextKey] = new DebugContextItem { Level = 0 };
- return _items[contextKey];
- }
+ ///
+ /// Gets a level or creates it if not existing
+ ///
+ ///
+ ///
+ ///
+ /// GetOrCreateItem method
+ ///
+ public DebugContextItem GetOrCreateItem(string contextKey)
+ {
+ if (!_items.ContainsKey(contextKey))
+ _items[contextKey] = new DebugContextItem { Level = 0 };
+ return _items[contextKey];
+ }
- ///
- /// sets the settings for a device or creates a new entry
- ///
- ///
- ///
- ///
- public void SetDebugSettingsForKey(string deviceKey, object settings)
- {
- lock (_deviceDebugSettingsLock)
+ ///
+ /// sets the settings for a device or creates a new entry
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// SetDebugSettingsForKey method
+ ///
+ public void SetDebugSettingsForKey(string deviceKey, object settings)
{
- if (DeviceDebugSettings.ContainsKey(deviceKey))
+ try
{
- DeviceDebugSettings[deviceKey] = settings;
+ _deviceDebugSettingsLock.Enter();
+
+ if (DeviceDebugSettings.ContainsKey(deviceKey))
+ {
+ DeviceDebugSettings[deviceKey] = settings;
+ }
+ else
+ DeviceDebugSettings.Add(deviceKey, settings);
+ }
+ finally
+ {
+ _deviceDebugSettingsLock.Leave();
}
- else
- DeviceDebugSettings.Add(deviceKey, settings);
}
- }
- ///
- /// Gets the device settings for a device by key or returns null
- ///
- ///
- ///
- public object GetDebugSettingsForKey(string deviceKey)
- {
- return DeviceDebugSettings[deviceKey];
- }
-}
+ ///
+ /// Gets the device settings for a device by key or returns null
+ ///
+ ///
+ ///
+ ///
+ /// GetDebugSettingsForKey method
+ ///
+ public object GetDebugSettingsForKey(string deviceKey)
+ {
+ return DeviceDebugSettings[deviceKey];
+ }
+ }
-///
-/// Contains information about
-///
-public class DebugContextItem
-{
- ///
- /// The level of debug messages to print
- ///
- [JsonProperty("level")]
- public int Level { get; set; }
+ ///
+ /// Contains information about
+ ///
+ public class DebugContextItem
+ {
+ ///
+ /// The level of debug messages to print
+ ///
+ [JsonProperty("level")]
+ public int Level { get; set; }
- ///
- /// Property to tell the program not to intitialize when it boots, if desired
- ///
- [JsonProperty("doNotLoadOnNextBoot")]
- public bool DoNotLoadOnNextBoot { get; set; }
+ ///
+ /// Property to tell the program not to intitialize when it boots, if desired
+ ///
+ [JsonProperty("doNotLoadOnNextBoot")]
+ public bool DoNotLoadOnNextBoot { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Logging/DebugWebsocketSink.cs b/src/PepperDash.Core/Logging/DebugWebsocketSink.cs
index 6214505b..cfbf5785 100644
--- a/src/PepperDash.Core/Logging/DebugWebsocketSink.cs
+++ b/src/PepperDash.Core/Logging/DebugWebsocketSink.cs
@@ -1,436 +1,479 @@
-extern alias NewtonsoftJson;
-
-using System;
-using System.Security.Authentication;
-using System.Security.Cryptography;
-using System.Security.Cryptography.X509Certificates;
-using Crestron.SimplSharp;
+using System;
using Serilog;
-using Serilog.Configuration;
using Serilog.Core;
using Serilog.Events;
-using Serilog.Formatting;
-using JObject = NewtonsoftJson::Newtonsoft.Json.Linq.JObject;
-using Serilog.Formatting.Json;
-using System.IO;
-using WebSocketSharp;
+using Serilog.Configuration;
using WebSocketSharp.Server;
+using Crestron.SimplSharp;
+using WebSocketSharp;
+using System.Security.Authentication;
using WebSocketSharp.Net;
+using System.Security.Cryptography.X509Certificates;
+using System.IO;
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Operators;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Pkcs;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.X509;
+using Serilog.Formatting;
+using Serilog.Formatting.Json;
-namespace PepperDash.Core;
-
-///
-/// Provides a WebSocket-based logging sink for debugging purposes, allowing log events to be broadcast to connected
-/// WebSocket clients.
-///
-/// This class implements the interface and is designed to send
-/// formatted log events to WebSocket clients connected to a secure WebSocket server. The server is hosted locally
-/// and uses a self-signed certificate for SSL/TLS encryption.
-public class DebugWebsocketSink : ILogEventSink, IKeyed
+namespace PepperDash.Core
{
- private HttpServer _httpsServer;
-
- private readonly string _path = "/debug/join/";
- private const string _certificateName = "selfCres";
- private const string _certificatePassword = "cres12345";
- private static string CertPath =>
- $"{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}{_certificateName}.pfx";
-
///
- /// Gets the port number on which the HTTPS server is currently running.
+ /// Represents a DebugWebsocketSink
///
- public int Port
+ public class DebugWebsocketSink : ILogEventSink
{
- get
+ private HttpServer _httpsServer;
+
+ private string _path = "/debug/join/";
+ private const string _certificateName = "selfCres";
+ private const string _certificatePassword = "cres12345";
+
+ private static string CertPath =>
+ $"{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}{_certificateName}.pfx";
+
+
+ public int Port
{
-
- if (_httpsServer == null) return 0;
- return _httpsServer.Port;
- }
- }
-
- ///
- /// Gets the WebSocket URL for the current server instance.
- ///
- /// The URL is dynamically constructed based on the server's current IP address, port,
- /// and WebSocket path.
- public string Url
- {
- get
- {
- if (_httpsServer == null || !_httpsServer.IsListening) return "";
- var service = _httpsServer.WebSocketServices[_path];
- if (service == null) return "";
-
- // Use CSLAN IP if available, otherwise fallback to primary IP. This ensures we provide a reachable URL in dual-stack environments.
- if (!string.IsNullOrEmpty(CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 1)))
- return $"wss://{CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 1)}:{_httpsServer.Port}{service.Path}";
- else
- return $"wss://{CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)}:{_httpsServer.Port}{service.Path}";
- }
- }
-
- ///
- /// Gets a value indicating whether the HTTPS server is currently listening for incoming connections.
- ///
- public bool IsRunning { get => _httpsServer?.IsListening ?? false; }
-
- ///
- public string Key => "DebugWebsocketSink";
-
- private readonly ITextFormatter _textFormatter;
-
- ///
- /// Initializes a new instance of the class with the specified text formatter.
- ///
- /// This constructor initializes the WebSocket sink and ensures that a certificate is
- /// available for secure communication. If the required certificate does not exist, it will be created
- /// automatically. Additionally, the sink is configured to stop the server when the program is
- /// stopping.
- /// The text formatter used to format log messages. If null, a default JSON formatter is used.
- public DebugWebsocketSink(ITextFormatter formatProvider)
- {
-
- _textFormatter = formatProvider ?? new JsonFormatter();
-
- if (!File.Exists(CertPath))
- CreateCert();
-
- try
- {
- CrestronEnvironment.ProgramStatusEventHandler += type =>
+ get
{
- if (type == eProgramStatusEventType.Stopping)
- StopServer();
- };
- }
- catch
- {
- // CrestronEnvironment is not available in test / dev environments — safe to skip.
- }
- }
- private static void CreateCert()
- {
- // NOTE: This method is called from the constructor, which is itself called during Debug's static
- // constructor before _logger is assigned. Do NOT call any Debug.Log* methods here — use
- // CrestronConsole.PrintLine only, to avoid a NullReferenceException that would poison the Debug type.
- try
- {
- var ipAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);
- var hostName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0);
- var domainName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0);
-
- CrestronConsole.PrintLine(string.Format("CreateCert: DomainName: {0} | HostName: {1} | {1}.{0}@{2}", domainName, hostName, ipAddress));
-
- var subjectName = string.Format("CN={0}.{1}", hostName, domainName);
- var fqdn = string.Format("{0}.{1}", hostName, domainName);
-
- using var rsa = RSA.Create(2048);
-
- var request = new CertificateRequest(
- subjectName,
- rsa,
- HashAlgorithmName.SHA256,
- RSASignaturePadding.Pkcs1);
-
- // Subject Key Identifier
- request.CertificateExtensions.Add(
- new X509SubjectKeyIdentifierExtension(request.PublicKey, false));
-
- // Extended Key Usage: server + client auth
- request.CertificateExtensions.Add(
- new X509EnhancedKeyUsageExtension(
- new OidCollection
- {
- new Oid("1.3.6.1.5.5.7.3.1"), // id-kp-serverAuth
- new Oid("1.3.6.1.5.5.7.3.2") // id-kp-clientAuth
- },
- false));
-
- // Subject Alternative Names: DNS + IP
- var sanBuilder = new SubjectAlternativeNameBuilder();
- sanBuilder.AddDnsName(fqdn);
- if (System.Net.IPAddress.TryParse(ipAddress, out var ip))
- sanBuilder.AddIpAddress(ip);
- request.CertificateExtensions.Add(sanBuilder.Build());
-
- var notBefore = DateTimeOffset.UtcNow;
- var notAfter = notBefore.AddYears(2);
-
- using var cert = request.CreateSelfSigned(notBefore, notAfter);
-
- var separator = Path.DirectorySeparatorChar;
- var outputPath = string.Format("{0}user{1}{2}.pfx", separator, separator, _certificateName);
-
- var pfxBytes = cert.Export(X509ContentType.Pfx, _certificatePassword);
- File.WriteAllBytes(outputPath, pfxBytes);
-
- CrestronConsole.PrintLine(string.Format("CreateCert: Certificate written to {0}", outputPath));
- }
- catch (Exception ex)
- {
- CrestronConsole.PrintLine(string.Format("WSS CreateCert Failed: {0}\r\n{1}", ex.Message, ex.StackTrace));
- }
- }
-
- ///
- /// Sends a log event to all connected WebSocket clients.
- ///
- /// The log event is formatted using the configured text formatter and then broadcasted
- /// to all clients connected to the WebSocket server. If the WebSocket server is not initialized or not
- /// listening, the method exits without performing any action.
- /// The log event to be formatted and broadcasted. Cannot be null.
- public void Emit(LogEvent logEvent)
- {
- if (_httpsServer == null || !_httpsServer.IsListening) return;
-
- var sw = new StringWriter();
- _textFormatter.Format(logEvent, sw);
-
- _httpsServer.WebSocketServices[_path].Sessions.Broadcast(sw.ToString());
- }
-
- ///
- /// Starts the WebSocket server on the specified port and configures it with the appropriate certificate.
- ///
- /// This method initializes the WebSocket server and binds it to the specified port. It
- /// also applies the server's certificate for secure communication. Ensure that the port is not already in use
- /// and that the certificate file is accessible.
- /// The port number on which the WebSocket server will listen. Must be a valid, non-negative port number.
- public void StartServerAndSetPort(int port)
- {
- Debug.LogInformation("Starting Websocket Server on port: {0}", port);
-
-
- Start(port, CertPath, _certificatePassword);
- }
-
- private static X509Certificate2 LoadOrRecreateCert(string certPath, string certPassword)
- {
- try
- {
- // EphemeralKeySet is required on Linux/OpenSSL (Crestron 4-series) to avoid
- // key-container persistence failures, and avoids the private key export restriction.
- return new X509Certificate2(certPath, certPassword, X509KeyStorageFlags.EphemeralKeySet);
- }
- catch (Exception ex)
- {
- // Cert is stale or was generated by an incompatible library (e.g. old BouncyCastle output).
- // Delete it, regenerate with the BCL path, and retry once.
- CrestronConsole.PrintLine(string.Format("SSL cert load failed ({0}); regenerating...", ex.Message));
- try { File.Delete(certPath); } catch { }
- CreateCert();
- return new X509Certificate2(certPath, certPassword, X509KeyStorageFlags.EphemeralKeySet);
- }
- }
-
- private void Start(int port, string certPath = "", string certPassword = "")
- {
- try
- {
- _httpsServer = new HttpServer(port, true);
-
- if (!string.IsNullOrWhiteSpace(certPath))
- {
- Debug.LogInformation("Assigning SSL Configuration");
-
- _httpsServer.SslConfiguration.ServerCertificate = LoadOrRecreateCert(certPath, certPassword);
- _httpsServer.SslConfiguration.ClientCertificateRequired = false;
- _httpsServer.SslConfiguration.CheckCertificateRevocation = false;
- _httpsServer.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls12;
- //this is just to test, you might want to actually validate
- _httpsServer.SslConfiguration.ClientCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
- {
- Debug.LogInformation("HTTPS ClientCerticateValidation Callback triggered");
- return true;
- };
+ if (_httpsServer == null) return 0;
+ return _httpsServer.Port;
}
- Debug.LogInformation("Adding Debug Client Service");
- _httpsServer.AddWebSocketService(_path);
- Debug.LogInformation("Assigning Log Info");
- _httpsServer.Log.Level = LogLevel.Trace;
- _httpsServer.Log.Output = WriteWebSocketInternalLog;
- Debug.LogInformation("Starting");
-
- _httpsServer.Start();
- Debug.LogInformation("Ready");
}
- catch (Exception ex)
+
+ public string Url
{
- Debug.LogError(ex, "WebSocket Failed to start {0}", ex.Message);
- Debug.LogVerbose("Stack Trace:\r{0}", ex.StackTrace);
- // Null out the server so callers can detect failure via IsRunning / Url null guards.
- _httpsServer = null;
+ get
+ {
+ if (_httpsServer == null || !_httpsServer.IsListening) return "";
+ var service = _httpsServer.WebSocketServices[_path];
+ if (service == null) return "";
+
+ // Use CSLAN IP if available, otherwise fallback to primary IP. This ensures we provide a reachable URL in dual-stack environments.
+ var cslanIp = CrestronEthernetHelper.GetEthernetParameter(
+ CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 1);
+ if (!string.IsNullOrEmpty(cslanIp) && cslanIp != "Invalid Value")
+ return $"wss://{cslanIp}:{_httpsServer.Port}{service.Path}";
+ else
+ return $"wss://{CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)}:{_httpsServer.Port}{service.Path}";
+ }
}
- }
- ///
- /// Stops the WebSocket server if it is currently running.
- ///
- /// This method halts the WebSocket server and releases any associated resources. After
- /// calling this method, the server will no longer accept or process incoming connections.
- public void StopServer()
- {
- Debug.LogInformation("Stopping Websocket Server");
+ ///
+ /// Gets or sets the IsRunning
+ ///
+ public bool IsRunning { get => _httpsServer?.IsListening ?? false; }
- try
+ ///
+ /// Gets a value indicating whether there are active WebSocket connections.
+ ///
+ public bool HasActiveConnections
{
- if (_httpsServer == null || !_httpsServer.IsListening)
+ get
{
- return;
+ if (_httpsServer == null || !_httpsServer.IsListening) return false;
+ var service = _httpsServer.WebSocketServices[_path];
+ if (service == null) return false;
+ return service.Sessions.Count > 0;
}
+ }
- // Prevent close-sequence internal websocket logs from re-entering the logging pipeline.
- _httpsServer.Log.Output = (d, s) => { };
- var serviceHost = _httpsServer.WebSocketServices[_path];
+ private readonly ITextFormatter _textFormatter;
- if (serviceHost == null)
+ ///
+ /// Initializes a new instance of the class with the specified text formatter.
+ ///
+ /// This constructor initializes the WebSocket sink and ensures that a certificate is
+ /// available for secure communication. If the required certificate does not exist, it will be created
+ /// automatically. Additionally, the sink is configured to stop the server when the program is
+ /// stopping.
+ /// The text formatter used to format log messages. If null, a default JSON formatter is used.
+ public DebugWebsocketSink(ITextFormatter formatProvider)
+ {
+
+ _textFormatter = formatProvider ?? new JsonFormatter();
+
+ if (!File.Exists(CertPath))
+ CreateCert();
+
+ try
{
- _httpsServer.Stop();
- _httpsServer = null;
- return;
- }
-
- serviceHost.Sessions.Broadcast("Server is stopping");
-
- foreach (var session in serviceHost.Sessions.Sessions)
- {
- if (session?.Context?.WebSocket != null && session.Context.WebSocket.IsAlive)
+ CrestronEnvironment.ProgramStatusEventHandler += type =>
{
- session.Context.WebSocket.Close(1001, "Server is stopping");
+ if (type == eProgramStatusEventType.Stopping)
+ StopServer();
+ };
+ }
+ catch
+ {
+ // CrestronEnvironment is not available in test / dev environments — safe to skip.
+ }
+ }
+
+ private static void CreateCert()
+ {
+ // NOTE: This method is called from the constructor, which is itself called during Debug's static
+ // constructor before _logger is assigned. Do NOT call any Debug.Log* methods here — use
+ // CrestronConsole.PrintLine only, to avoid a NullReferenceException that would poison the Debug type.
+ try
+ {
+ var ipAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);
+ var hostName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0);
+ var domainName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0);
+
+ CrestronConsole.PrintLine(string.Format("CreateCert: DomainName: {0} | HostName: {1} | {1}.{0}@{2}", domainName, hostName, ipAddress));
+
+ var subjectName = string.Format("CN={0}.{1}", hostName, domainName);
+ var fqdn = string.Format("{0}.{1}", hostName, domainName);
+
+ var random = new SecureRandom();
+
+ // Generate RSA 2048 key pair
+ var keyPairGenerator = new RsaKeyPairGenerator();
+ keyPairGenerator.Init(new KeyGenerationParameters(random, 2048));
+ var keyPair = keyPairGenerator.GenerateKeyPair();
+
+ // Build certificate
+ var certGenerator = new X509V3CertificateGenerator();
+ certGenerator.SetSerialNumber(BigInteger.ValueOf(Math.Abs(DateTime.UtcNow.Ticks)));
+ certGenerator.SetIssuerDN(new X509Name(subjectName));
+ certGenerator.SetSubjectDN(new X509Name(subjectName));
+ certGenerator.SetNotBefore(DateTime.UtcNow);
+ certGenerator.SetNotAfter(DateTime.UtcNow.AddYears(2));
+ certGenerator.SetPublicKey(keyPair.Public);
+
+ // Extended Key Usage: server + client auth
+ certGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, false,
+ new ExtendedKeyUsage(new[] { KeyPurposeID.id_kp_serverAuth, KeyPurposeID.id_kp_clientAuth }));
+
+ // Subject Alternative Names: DNS + IP
+ System.Net.IPAddress parsedIp;
+ if (System.Net.IPAddress.TryParse(ipAddress, out parsedIp))
+ {
+ certGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false,
+ new GeneralNames(new GeneralName[] {
+ new GeneralName(GeneralName.DnsName, fqdn),
+ new GeneralName(GeneralName.IPAddress, ipAddress)
+ }));
+ }
+ else
+ {
+ certGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false,
+ new GeneralNames(new GeneralName(GeneralName.DnsName, fqdn)));
+ }
+
+ // Sign with SHA256withRSA
+ var signatureFactory = new Asn1SignatureFactory("SHA256WITHRSA", keyPair.Private, random);
+ var certificate = certGenerator.Generate(signatureFactory);
+
+ // Export as PKCS12/PFX
+ var pkcs12Store = new Pkcs12StoreBuilder().Build();
+ var certEntry = new X509CertificateEntry(certificate);
+ pkcs12Store.SetCertificateEntry(_certificateName, certEntry);
+ pkcs12Store.SetKeyEntry(_certificateName, new AsymmetricKeyEntry(keyPair.Private), new[] { certEntry });
+
+ var separator = Path.DirectorySeparatorChar;
+ var outputPath = string.Format("{0}user{1}{2}.pfx", separator, separator, _certificateName);
+
+ using (var ms = new MemoryStream())
+ {
+ var passwordChars = _certificatePassword.ToCharArray();
+ try
+ {
+ pkcs12Store.Save(ms, passwordChars, random);
+ }
+ finally
+ {
+ Array.Clear(passwordChars, 0, passwordChars.Length);
+ }
+ File.WriteAllBytes(outputPath, ms.ToArray());
+ }
+
+ CrestronConsole.PrintLine(string.Format("CreateCert: Certificate written to {0}", outputPath));
+ }
+ catch (Exception ex)
+ {
+ CrestronConsole.PrintLine(string.Format("WSS CreateCert Failed: {0}\r\n{1}", ex.Message, ex.StackTrace));
+ }
+ }
+
+ ///
+ /// Emit method
+ ///
+ public void Emit(LogEvent logEvent)
+ {
+ if (_httpsServer == null || !_httpsServer.IsListening) return;
+
+ var sw = new StringWriter();
+ _textFormatter.Format(logEvent, sw);
+
+ _httpsServer.WebSocketServices.Broadcast(sw.ToString());
+
+ }
+
+ ///
+ /// Starts the WebSocket server on the specified port and configures it with the appropriate certificate.
+ ///
+ /// This method initializes the WebSocket server and binds it to the specified port. It
+ /// also applies the server's certificate for secure communication. Ensure that the port is not already in use
+ /// and that the certificate file is accessible.
+ /// The port number on which the WebSocket server will listen. Must be a valid, non-negative port number.
+ public void StartServerAndSetPort(int port)
+ {
+ Debug.LogInformation("Starting Websocket Server on port: {0}", port);
+
+
+
+
+ Start(port, CertPath, _certificatePassword);
+ }
+
+ private static X509Certificate2 LoadOrRecreateCert(string certPath, string certPassword)
+ {
+ if (!File.Exists(certPath))
+ CreateCert();
+
+ try
+ {
+ return LoadCertFromBouncyCastle(certPath, certPassword);
+ }
+ catch (Exception ex)
+ {
+ // Cert is corrupt or was written by an incompatible tool — delete and regenerate once.
+ CrestronConsole.PrintLine(string.Format("SSL cert load failed ({0}); regenerating...", ex.Message));
+ try { File.Delete(certPath); } catch { }
+ CreateCert();
+ return LoadCertFromBouncyCastle(certPath, certPassword);
+ }
+ }
+
+ ///
+ /// Loads a PKCS#12 file written by BouncyCastle and returns an with
+ /// private key attached.
+ /// The PFX is parsed and re-encoded by BouncyCastle (ensuring format compatibility), then passed as
+ /// raw bytes to so neither RSACryptoServiceProvider nor the
+ /// EphemeralKeySet flag (unsupported on the Crestron/Mono runtime) is needed.
+ ///
+ private static X509Certificate2 LoadCertFromBouncyCastle(string certPath, string certPassword)
+ {
+ var passwordChars = certPassword.ToCharArray();
+ try
+ {
+ using (var stream = File.OpenRead(certPath))
+ {
+ var store = new Pkcs12StoreBuilder().Build();
+ store.Load(stream, passwordChars);
+
+ // Re-encode through BouncyCastle to guarantee PKCS#12 format compatibility,
+ // then hand raw bytes to X509Certificate2 — no RSACryptoServiceProvider needed.
+ using (var ms = new MemoryStream())
+ {
+ store.Save(ms, passwordChars, new SecureRandom());
+ var cert = new X509Certificate2(ms.ToArray(), certPassword);
+
+ if (!cert.HasPrivateKey)
+ throw new InvalidOperationException(
+ string.Format("Certificate loaded from '{0}' does not contain a private key and cannot be used as a server certificate.", certPath));
+
+ return cert;
+ }
}
}
-
- _httpsServer.Stop();
-
- _httpsServer = null;
-
- }
- catch (Exception ex)
- {
- Debug.LogError(ex, "WebSocket Failed to stop gracefully {0}", ex.Message);
- Debug.LogVerbose("Stack Trace\r\n{0}", ex.StackTrace);
- }
- }
-
- private static void WriteWebSocketInternalLog(LogData data, string supplemental)
- {
- try
- {
- if (data == null)
+ finally
{
- return;
+ Array.Clear(passwordChars, 0, passwordChars.Length);
}
-
- var message = string.IsNullOrWhiteSpace(data.Message) ? "" : data.Message;
- var details = string.IsNullOrWhiteSpace(supplemental) ? string.Empty : string.Format(" | details: {0}", supplemental);
-
- // Use direct console output to avoid recursive log sink calls.
- CrestronConsole.PrintLine(string.Format("WS[{0}] {1} | message: {2}{3}", data.Level, data.Date, message, details));
}
- catch
+
+ private void Start(int port, string certPath = "", string certPassword = "")
{
- // Never throw from websocket log callback.
- }
- }
-
-}
-
-///
-/// Configures the logger to write log events to a debug WebSocket sink.
-///
-/// This extension method allows you to direct log events to a WebSocket sink for debugging
-/// purposes.
-public static class DebugWebsocketSinkExtensions
-{
- ///
- /// Configures a logger to write log events to a debug WebSocket sink.
- ///
- /// This method adds a sink that writes log events to a WebSocket for debugging purposes.
- /// It is typically used during development to stream log events in real-time.
- /// The logger sink configuration to apply the WebSocket sink to.
- /// An optional text formatter to format the log events. If not provided, a default formatter will be used.
- /// A object that can be used to further configure the logger.
- public static LoggerConfiguration DebugWebsocketSink(
- this LoggerSinkConfiguration loggerConfiguration,
- ITextFormatter formatProvider = null)
- {
- return loggerConfiguration.Sink(new DebugWebsocketSink(formatProvider));
- }
-}
-
-///
-/// Represents a WebSocket client for debugging purposes, providing connection lifecycle management and message
-/// handling functionality.
-///
-/// The class extends to handle
-/// WebSocket connections, including events for opening, closing, receiving messages, and errors. It tracks the
-/// duration of the connection and logs relevant events for debugging.
-public class DebugClient : WebSocketBehavior
-{
- private DateTime _connectionTime;
-
- ///
- /// Gets the duration of time the WebSocket connection has been active.
- ///
- public TimeSpan ConnectedDuration
- {
- get
- {
- if (Context.WebSocket.IsAlive)
+ try
{
- return DateTime.Now - _connectionTime;
+ _httpsServer = new HttpServer(port, true);
+
+ if (!string.IsNullOrWhiteSpace(certPath))
+ {
+ Debug.LogInformation("Assigning SSL Configuration");
+
+ _httpsServer.SslConfiguration.ServerCertificate = LoadOrRecreateCert(certPath, certPassword);
+ _httpsServer.SslConfiguration.ClientCertificateRequired = false;
+ _httpsServer.SslConfiguration.CheckCertificateRevocation = false;
+ _httpsServer.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls12;
+ //this is just to test, you might want to actually validate
+ _httpsServer.SslConfiguration.ClientCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
+ {
+ Debug.LogInformation("HTTPS ClientCerticateValidation Callback triggered");
+ return true;
+ };
+ }
+ Debug.LogInformation("Adding Debug Client Service");
+ _httpsServer.AddWebSocketService(_path);
+ Debug.LogInformation("Assigning Log Info");
+ _httpsServer.Log.Level = LogLevel.Trace;
+ _httpsServer.Log.Output = WriteWebSocketInternalLog;
+ Debug.LogInformation("Starting");
+
+ _httpsServer.Start();
+ Debug.LogInformation("Ready");
}
- else
+ catch (Exception ex)
{
- return new TimeSpan(0);
+ Debug.LogError(ex, "WebSocket Failed to start {0}", ex.Message);
+ Debug.LogVerbose("Stack Trace:\r{0}", ex.StackTrace);
+ // Null out the server so callers can detect failure via IsRunning / Url null guards.
+ _httpsServer = null;
+ }
+ }
+
+ ///
+ /// StopServer method
+ ///
+ public void StopServer()
+ {
+ Debug.LogInformation("Stopping Websocket Server");
+
+ try
+ {
+ if (_httpsServer == null || !_httpsServer.IsListening)
+ {
+ return;
+ }
+
+ // Prevent close-sequence internal websocket logs from re-entering the logging pipeline.
+ _httpsServer.Log.Output = (d, s) => { };
+
+ var serviceHost = _httpsServer.WebSocketServices[_path];
+
+ if (serviceHost == null)
+ {
+ _httpsServer.Stop();
+ _httpsServer = null;
+ return;
+ }
+
+ serviceHost.Sessions.Broadcast("Server is stopping");
+
+ foreach (var session in serviceHost.Sessions.Sessions)
+ {
+ if (session?.Context?.WebSocket != null && session.Context.WebSocket.IsAlive)
+ {
+ session.Context.WebSocket.Close(1001, "Server is stopping");
+ }
+ }
+
+ _httpsServer.Stop();
+
+ _httpsServer = null;
+
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError(ex, "WebSocket Failed to stop gracefully {0}", ex.Message);
+ Debug.LogVerbose("Stack Trace\r\n{0}", ex.StackTrace);
+ }
+ }
+
+ private static void WriteWebSocketInternalLog(LogData data, string supplemental)
+ {
+ try
+ {
+ if (data == null)
+ {
+ return;
+ }
+
+ var message = string.IsNullOrWhiteSpace(data.Message) ? "" : data.Message;
+ var details = string.IsNullOrWhiteSpace(supplemental) ? string.Empty : string.Format(" | details: {0}", supplemental);
+
+ // Use direct console output to avoid recursive log sink calls.
+ CrestronConsole.PrintLine(string.Format("WS[{0}] {1} | message: {2}{3}", data.Level, data.Date, message, details));
+ }
+ catch
+ {
+ // Never throw from websocket log callback.
}
}
}
+ public static class DebugWebsocketSinkExtensions
+ {
+ ///
+ /// DebugWebsocketSink method
+ ///
+ public static LoggerConfiguration DebugWebsocketSink(
+ this LoggerSinkConfiguration loggerConfiguration,
+ ITextFormatter formatProvider = null)
+ {
+ return loggerConfiguration.Sink(new DebugWebsocketSink(formatProvider));
+ }
+ }
+
///
- /// Initializes a new instance of the class.
+ /// Represents a DebugClient
///
- public DebugClient()
+ public class DebugClient : WebSocketBehavior
{
- Debug.LogInformation("DebugClient Created");
- }
+ private DateTime _connectionTime;
- ///
- protected override void OnOpen()
- {
- base.OnOpen();
+ public TimeSpan ConnectedDuration
+ {
+ get
+ {
+ if (Context.WebSocket.IsAlive)
+ {
+ return DateTime.Now - _connectionTime;
+ }
+ else
+ {
+ return new TimeSpan(0);
+ }
+ }
+ }
- var url = Context.WebSocket.Url;
- Debug.LogInformation("New WebSocket Connection from: {0}", url);
+ public DebugClient()
+ {
+ Debug.Console(0, "DebugClient Created");
+ }
- _connectionTime = DateTime.Now;
- }
+ protected override void OnOpen()
+ {
+ base.OnOpen();
- ///
- protected override void OnMessage(MessageEventArgs e)
- {
- base.OnMessage(e);
+ var url = Context.WebSocket.Url;
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "New WebSocket Connection from: {0}", url);
- Debug.LogVerbose("WebSocket UiClient Message: {0}", e.Data);
- }
+ _connectionTime = DateTime.Now;
+ }
- ///
- protected override void OnClose(CloseEventArgs e)
- {
- base.OnClose(e);
+ protected override void OnMessage(MessageEventArgs e)
+ {
+ base.OnMessage(e);
- Debug.LogDebug("WebSocket UiClient Closing: {0} reason: {1}", e.Code, e.Reason);
- }
+ Debug.Console(0, "WebSocket UiClient Message: {0}", e.Data);
+ }
- ///
- protected override void OnError(WebSocketSharp.ErrorEventArgs e)
- {
- base.OnError(e);
+ protected override void OnClose(CloseEventArgs e)
+ {
+ base.OnClose(e);
- Debug.LogError(e.Exception, "WebSocket UiClient Error: {0} message: {1}", e.Exception, e.Message);
- Debug.LogVerbose("Stack Trace:\r{0}", e.Exception.StackTrace);
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "WebSocket UiClient Closing: {0} reason: {1}", e.Code, e.Reason);
+
+ }
+
+ protected override void OnError(WebSocketSharp.ErrorEventArgs e)
+ {
+ base.OnError(e);
+
+ Debug.Console(2, Debug.ErrorLogLevel.Notice, "WebSocket UiClient Error: {0} message: {1}", e.Exception, e.Message);
+ }
}
}
diff --git a/src/PepperDash.Core/Network/DiscoveryThings.cs b/src/PepperDash.Core/Network/DiscoveryThings.cs
index c01613b7..973c03a4 100644
--- a/src/PepperDash.Core/Network/DiscoveryThings.cs
+++ b/src/PepperDash.Core/Network/DiscoveryThings.cs
@@ -4,17 +4,19 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Core;
-
-///
-/// Not in use
-///
- public static class NetworkComm
- {
+namespace PepperDash.Core
+{
///
/// Not in use
///
+ public static class NetworkComm
+ {
+ ///
+ /// Not in use
+ ///
static NetworkComm()
{
}
- }
\ No newline at end of file
+ }
+
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/PasswordManagement/Config.cs b/src/PepperDash.Core/PasswordManagement/Config.cs
index a5f071a4..22aa4881 100644
--- a/src/PepperDash.Core/PasswordManagement/Config.cs
+++ b/src/PepperDash.Core/PasswordManagement/Config.cs
@@ -4,8 +4,8 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Core.PasswordManagement;
-
+namespace PepperDash.Core.PasswordManagement
+{
///
/// JSON password configuration
///
@@ -22,4 +22,5 @@ namespace PepperDash.Core.PasswordManagement;
{
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/PasswordManagement/Constants.cs b/src/PepperDash.Core/PasswordManagement/Constants.cs
index d4cf1e0b..65a1bf45 100644
--- a/src/PepperDash.Core/PasswordManagement/Constants.cs
+++ b/src/PepperDash.Core/PasswordManagement/Constants.cs
@@ -4,8 +4,8 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Core.PasswordManagement;
-
+namespace PepperDash.Core.PasswordManagement
+{
///
/// Constants
///
@@ -53,4 +53,5 @@ namespace PepperDash.Core.PasswordManagement;
/// Generic string value change constant
///
public const ushort StringValueChange = 201;
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/PasswordManagement/PasswordClient.cs b/src/PepperDash.Core/PasswordManagement/PasswordClient.cs
index 4a6c7368..9443be0b 100644
--- a/src/PepperDash.Core/PasswordManagement/PasswordClient.cs
+++ b/src/PepperDash.Core/PasswordManagement/PasswordClient.cs
@@ -1,10 +1,10 @@
using System;
-namespace PepperDash.Core.PasswordManagement;
-
-///
-/// A class to allow user interaction with the PasswordManager
-///
+namespace PepperDash.Core.PasswordManagement
+{
+ ///
+ /// Represents a PasswordClient
+ ///
public class PasswordClient
{
///
@@ -192,4 +192,5 @@ namespace PepperDash.Core.PasswordManagement;
GetPasswordByIndex(args.Index);
}
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/PasswordManagement/PasswordManager.cs b/src/PepperDash.Core/PasswordManagement/PasswordManager.cs
index a1c869a8..9ba008e4 100644
--- a/src/PepperDash.Core/PasswordManagement/PasswordManager.cs
+++ b/src/PepperDash.Core/PasswordManagement/PasswordManager.cs
@@ -1,252 +1,247 @@
using System;
using System.Collections.Generic;
-using System.Timers;
+using Crestron.SimplSharp;
-namespace PepperDash.Core.PasswordManagement;
-
-///
-/// Allows passwords to be stored and managed
-///
-public class PasswordManager
+namespace PepperDash.Core.PasswordManagement
{
- ///
- /// Public dictionary of known passwords
- ///
- public static Dictionary Passwords = new Dictionary();
- ///
- /// Private dictionary, used when passwords are updated
- ///
- private Dictionary _passwords = new Dictionary();
-
- ///
- /// Timer used to wait until password changes have stopped before updating the dictionary
- ///
- Timer PasswordTimer;
- ///
- /// Timer length
- ///
- public long PasswordTimerElapsedMs = 5000;
-
- ///
- /// Boolean event
- ///
- public event EventHandler BoolChange;
- ///
- /// Ushort event
- ///
- public event EventHandler UshrtChange;
- ///
- /// String event
- ///
- public event EventHandler StringChange;
- ///
- /// Event to notify clients of an updated password at the specified index (uint)
- ///
- public static event EventHandler PasswordChange;
-
- ///
- /// Constructor
- ///
- public PasswordManager()
+ ///
+ /// Represents a PasswordManager
+ ///
+ public class PasswordManager
{
+ ///
+ /// Public dictionary of known passwords
+ ///
+ public static Dictionary Passwords = new Dictionary();
+ ///
+ /// Private dictionary, used when passwords are updated
+ ///
+ private Dictionary _passwords = new Dictionary();
- }
+ ///
+ /// Timer used to wait until password changes have stopped before updating the dictionary
+ ///
+ CTimer PasswordTimer;
+ ///
+ /// Timer length
+ ///
+ public long PasswordTimerElapsedMs = 5000;
- ///
- /// Initialize password manager
- ///
- public void Initialize()
- {
- if (Passwords == null)
- Passwords = new Dictionary();
+ ///
+ /// Boolean event
+ ///
+ public event EventHandler BoolChange;
+ ///
+ /// Ushort event
+ ///
+ public event EventHandler UshrtChange;
+ ///
+ /// String event
+ ///
+ public event EventHandler StringChange;
+ ///
+ /// Event to notify clients of an updated password at the specified index (uint)
+ ///
+ public static event EventHandler PasswordChange;
- if (_passwords == null)
- _passwords = new Dictionary();
-
- OnBoolChange(true, 0, PasswordManagementConstants.PasswordInitializedChange);
- }
-
- ///
- /// Updates password stored in the dictonary
- ///
- ///
- ///
- ///
- /// UpdatePassword method
- ///
- public void UpdatePassword(ushort key, string password)
- {
- // validate the parameters
- if (key > 0 && string.IsNullOrEmpty(password))
+ ///
+ /// Constructor
+ ///
+ public PasswordManager()
{
- Debug.LogDebug("PasswordManager.UpdatePassword: key [{0}] or password are not valid", key, password);
- return;
+
}
- try
+ ///
+ /// Initialize password manager
+ ///
+ public void Initialize()
{
- // if key exists, update the value
- if (_passwords.ContainsKey(key))
- _passwords[key] = password;
- // else add the key & value
- else
- _passwords.Add(key, password);
+ if (Passwords == null)
+ Passwords = new Dictionary();
- Debug.LogDebug("PasswordManager.UpdatePassword: _password[{0}] = {1}", key, _passwords[key]);
+ if (_passwords == null)
+ _passwords = new Dictionary();
- if (PasswordTimer == null)
+ OnBoolChange(true, 0, PasswordManagementConstants.PasswordInitializedChange);
+ }
+
+ ///
+ /// Updates password stored in the dictonary
+ ///
+ ///
+ ///
+ ///
+ /// UpdatePassword method
+ ///
+ public void UpdatePassword(ushort key, string password)
+ {
+ // validate the parameters
+ if (key > 0 && string.IsNullOrEmpty(password))
{
- PasswordTimer = new Timer(PasswordTimerElapsedMs) { AutoReset = false };
- PasswordTimer.Elapsed += (s, e) => PasswordTimerElapsed(s, e);
- PasswordTimer.Start();
- Debug.LogDebug("PasswordManager.UpdatePassword: Timer Started");
- OnBoolChange(true, 0, PasswordManagementConstants.PasswordUpdateBusyChange);
+ Debug.Console(1, string.Format("PasswordManager.UpdatePassword: key [{0}] or password are not valid", key, password));
+ return;
}
- else
- {
- PasswordTimer.Stop();
- PasswordTimer.Interval = PasswordTimerElapsedMs;
- PasswordTimer.Start();
- Debug.LogDebug("PasswordManager.UpdatePassword: Timer Reset");
- }
- }
- catch (Exception e)
- {
- var msg = string.Format("PasswordManager.UpdatePassword key-value[{0}, {1}] failed:\r{2}", key, password, e);
- Debug.LogError(e, msg);
- Debug.LogVerbose("Stack Trace:\r{0}", e.StackTrace);
- }
- }
- ///
- /// Timer callback function
- ///
- private void PasswordTimerElapsed(object sender, ElapsedEventArgs e)
- {
- try
- {
- PasswordTimer.Stop();
- Debug.LogDebug("PasswordManager.PasswordTimerElapsed: Timer Stopped");
- OnBoolChange(false, 0, PasswordManagementConstants.PasswordUpdateBusyChange);
- foreach (var pw in _passwords)
+ try
{
- // if key exists, continue
- if (Passwords.ContainsKey(pw.Key))
- {
- Debug.LogDebug("PasswordManager.PasswordTimerElapsed: pw.key[{0}] = {1}", pw.Key, pw.Value);
- if (Passwords[pw.Key] != _passwords[pw.Key])
- {
- Passwords[pw.Key] = _passwords[pw.Key];
- Debug.LogDebug("PasswordManager.PasswordTimerElapsed: Updated Password[{0} = {1}", pw.Key, Passwords[pw.Key]);
- OnPasswordChange(Passwords[pw.Key], (ushort)pw.Key, PasswordManagementConstants.StringValueChange);
- }
- }
+ // if key exists, update the value
+ if(_passwords.ContainsKey(key))
+ _passwords[key] = password;
// else add the key & value
else
+ _passwords.Add(key, password);
+
+ Debug.Console(1, string.Format("PasswordManager.UpdatePassword: _password[{0}] = {1}", key, _passwords[key]));
+
+ if (PasswordTimer == null)
{
- Passwords.Add(pw.Key, pw.Value);
+ PasswordTimer = new CTimer((o) => PasswordTimerElapsed(), PasswordTimerElapsedMs);
+ Debug.Console(1, string.Format("PasswordManager.UpdatePassword: CTimer Started"));
+ OnBoolChange(true, 0, PasswordManagementConstants.PasswordUpdateBusyChange);
+ }
+ else
+ {
+ PasswordTimer.Reset(PasswordTimerElapsedMs);
+ Debug.Console(1, string.Format("PasswordManager.UpdatePassword: CTimer Reset"));
}
}
- OnUshrtChange((ushort)Passwords.Count, 0, PasswordManagementConstants.PasswordManagerCountChange);
+ catch (Exception e)
+ {
+ var msg = string.Format("PasswordManager.UpdatePassword key-value[{0}, {1}] failed:\r{2}", key, password, e);
+ Debug.Console(1, msg);
+ }
}
- catch (Exception ex)
+
+ ///
+ /// CTimer callback function
+ ///
+ private void PasswordTimerElapsed()
{
- var msg = string.Format("PasswordManager.PasswordTimerElapsed failed:\r{0}", ex.Message);
- Debug.LogError(ex, msg);
- Debug.LogVerbose("Stack Trace:\r{0}", ex.StackTrace);
+ try
+ {
+ PasswordTimer.Stop();
+ Debug.Console(1, string.Format("PasswordManager.PasswordTimerElapsed: CTimer Stopped"));
+ OnBoolChange(false, 0, PasswordManagementConstants.PasswordUpdateBusyChange);
+ foreach (var pw in _passwords)
+ {
+ // if key exists, continue
+ if (Passwords.ContainsKey(pw.Key))
+ {
+ Debug.Console(1, string.Format("PasswordManager.PasswordTimerElapsed: pw.key[{0}] = {1}", pw.Key, pw.Value));
+ if (Passwords[pw.Key] != _passwords[pw.Key])
+ {
+ Passwords[pw.Key] = _passwords[pw.Key];
+ Debug.Console(1, string.Format("PasswordManager.PasswordTimerElapsed: Updated Password[{0} = {1}", pw.Key, Passwords[pw.Key]));
+ OnPasswordChange(Passwords[pw.Key], (ushort)pw.Key, PasswordManagementConstants.StringValueChange);
+ }
+ }
+ // else add the key & value
+ else
+ {
+ Passwords.Add(pw.Key, pw.Value);
+ }
+ }
+ OnUshrtChange((ushort)Passwords.Count, 0, PasswordManagementConstants.PasswordManagerCountChange);
+ }
+ catch (Exception e)
+ {
+ var msg = string.Format("PasswordManager.PasswordTimerElapsed failed:\r{0}", e);
+ Debug.Console(1, msg);
+ }
}
- }
- ///
- /// Method to change the default timer value, (default 5000ms/5s)
- ///
- ///
- ///
- /// PasswordTimerMs method
- ///
- public void PasswordTimerMs(ushort time)
- {
- PasswordTimerElapsedMs = Convert.ToInt64(time);
- }
-
- ///
- /// Helper method for debugging to see what passwords are in the lists
- ///
- public void ListPasswords()
- {
- Debug.LogInformation("PasswordManager.ListPasswords:\r");
- foreach (var pw in Passwords)
- Debug.LogInformation("Passwords[{0}]: {1}\r", pw.Key, pw.Value);
- Debug.LogInformation("\n");
- foreach (var pw in _passwords)
- Debug.LogInformation("_passwords[{0}]: {1}\r", pw.Key, pw.Value);
- }
-
- ///
- /// Protected boolean change event handler
- ///
- ///
- ///
- ///
- protected void OnBoolChange(bool state, ushort index, ushort type)
- {
- var handler = BoolChange;
- if (handler != null)
+ ///
+ /// Method to change the default timer value, (default 5000ms/5s)
+ ///
+ ///
+ ///
+ /// PasswordTimerMs method
+ ///
+ public void PasswordTimerMs(ushort time)
{
- var args = new BoolChangeEventArgs(state, type);
- args.Index = index;
- BoolChange(this, args);
+ PasswordTimerElapsedMs = Convert.ToInt64(time);
}
- }
- ///
- /// Protected ushort change event handler
- ///
- ///
- ///
- ///
- protected void OnUshrtChange(ushort value, ushort index, ushort type)
- {
- var handler = UshrtChange;
- if (handler != null)
+ ///
+ /// Helper method for debugging to see what passwords are in the lists
+ ///
+ public void ListPasswords()
{
- var args = new UshrtChangeEventArgs(value, type);
- args.Index = index;
- UshrtChange(this, args);
+ Debug.Console(0, "PasswordManager.ListPasswords:\r");
+ foreach (var pw in Passwords)
+ Debug.Console(0, "Passwords[{0}]: {1}\r", pw.Key, pw.Value);
+ Debug.Console(0, "\n");
+ foreach (var pw in _passwords)
+ Debug.Console(0, "_passwords[{0}]: {1}\r", pw.Key, pw.Value);
}
- }
- ///
- /// Protected string change event handler
- ///
- ///
- ///
- ///
- protected void OnStringChange(string value, ushort index, ushort type)
- {
- var handler = StringChange;
- if (handler != null)
+ ///
+ /// Protected boolean change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnBoolChange(bool state, ushort index, ushort type)
{
- var args = new StringChangeEventArgs(value, type);
- args.Index = index;
- StringChange(this, args);
+ var handler = BoolChange;
+ if (handler != null)
+ {
+ var args = new BoolChangeEventArgs(state, type);
+ args.Index = index;
+ BoolChange(this, args);
+ }
}
- }
- ///
- /// Protected password change event handler
- ///
- ///
- ///
- ///
- protected void OnPasswordChange(string value, ushort index, ushort type)
- {
- var handler = PasswordChange;
- if (handler != null)
+ ///
+ /// Protected ushort change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnUshrtChange(ushort value, ushort index, ushort type)
{
- var args = new StringChangeEventArgs(value, type);
- args.Index = index;
- PasswordChange(this, args);
+ var handler = UshrtChange;
+ if (handler != null)
+ {
+ var args = new UshrtChangeEventArgs(value, type);
+ args.Index = index;
+ UshrtChange(this, args);
+ }
+ }
+
+ ///
+ /// Protected string change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnStringChange(string value, ushort index, ushort type)
+ {
+ var handler = StringChange;
+ if (handler != null)
+ {
+ var args = new StringChangeEventArgs(value, type);
+ args.Index = index;
+ StringChange(this, args);
+ }
+ }
+
+ ///
+ /// Protected password change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnPasswordChange(string value, ushort index, ushort type)
+ {
+ var handler = PasswordChange;
+ if (handler != null)
+ {
+ var args = new StringChangeEventArgs(value, type);
+ args.Index = index;
+ PasswordChange(this, args);
+ }
}
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/PepperDash.Core.csproj b/src/PepperDash.Core/PepperDash.Core.csproj
index 4cd091ef..747aefb6 100644
--- a/src/PepperDash.Core/PepperDash.Core.csproj
+++ b/src/PepperDash.Core/PepperDash.Core.csproj
@@ -5,7 +5,7 @@
PepperDash.Core
PepperDashCore
- net8
+ net472
true
en
bin\$(Configuration)\
@@ -35,23 +35,25 @@
-
-
-
-
-
- global,NewtonsoftJson
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/PepperDash.Core/SystemInfo/EventArgs and Constants.cs b/src/PepperDash.Core/SystemInfo/EventArgs and Constants.cs
index e6d697f3..a83f8901 100644
--- a/src/PepperDash.Core/SystemInfo/EventArgs and Constants.cs
+++ b/src/PepperDash.Core/SystemInfo/EventArgs and Constants.cs
@@ -4,68 +4,68 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Core.SystemInfo;
-
+namespace PepperDash.Core.SystemInfo
+{
///
/// Constants
///
public class SystemInfoConstants
{
- ///
- ///
- ///
+ ///
+ ///
+ ///
public const ushort BoolValueChange = 1;
///
///
///
- public const ushort CompleteBoolChange = 2;
+ public const ushort CompleteBoolChange = 2;
///
///
///
- public const ushort BusyBoolChange = 3;
-
- ///
- ///
- ///
+ public const ushort BusyBoolChange = 3;
+
+ ///
+ ///
+ ///
public const ushort UshortValueChange = 101;
- ///
- ///
- ///
+ ///
+ ///
+ ///
public const ushort StringValueChange = 201;
///
///
///
- public const ushort ConsoleResponseChange = 202;
+ public const ushort ConsoleResponseChange = 202;
///
///
///
- public const ushort ProcessorUptimeChange = 203;
+ public const ushort ProcessorUptimeChange = 203;
///
///
///
- public const ushort ProgramUptimeChange = 204;
+ public const ushort ProgramUptimeChange = 204;
- ///
- ///
- ///
+ ///
+ ///
+ ///
public const ushort ObjectChange = 301;
///
///
///
- public const ushort ProcessorConfigChange = 302;
+ public const ushort ProcessorConfigChange = 302;
///
///
///
- public const ushort EthernetConfigChange = 303;
+ public const ushort EthernetConfigChange = 303;
///
///
///
- public const ushort ControlSubnetConfigChange = 304;
+ public const ushort ControlSubnetConfigChange = 304;
///
///
///
- public const ushort ProgramConfigChange = 305;
+ public const ushort ProgramConfigChange = 305;
}
///
@@ -73,18 +73,18 @@ namespace PepperDash.Core.SystemInfo;
///
public class ProcessorChangeEventArgs : EventArgs
{
- ///
- ///
- ///
+ ///
+ ///
+ ///
public ProcessorInfo Processor { get; set; }
///
///
///
- public ushort Type { get; set; }
+ public ushort Type { get; set; }
///
///
///
- public ushort Index { get; set; }
+ public ushort Index { get; set; }
///
/// Constructor
@@ -119,18 +119,18 @@ namespace PepperDash.Core.SystemInfo;
///
public class EthernetChangeEventArgs : EventArgs
{
- ///
- ///
- ///
+ ///
+ ///
+ ///
public EthernetInfo Adapter { get; set; }
///
///
///
- public ushort Type { get; set; }
+ public ushort Type { get; set; }
///
///
///
- public ushort Index { get; set; }
+ public ushort Index { get; set; }
///
/// Constructor
@@ -143,7 +143,7 @@ namespace PepperDash.Core.SystemInfo;
///
/// Constructor overload
///
- ///
+ ///
///
public EthernetChangeEventArgs(EthernetInfo ethernet, ushort type)
{
@@ -154,9 +154,9 @@ namespace PepperDash.Core.SystemInfo;
///
/// Constructor overload
///
- ///
+ ///
///
- ///
+ ///
public EthernetChangeEventArgs(EthernetInfo ethernet, ushort type, ushort index)
{
Adapter = ethernet;
@@ -170,18 +170,18 @@ namespace PepperDash.Core.SystemInfo;
///
public class ControlSubnetChangeEventArgs : EventArgs
{
- ///
- ///
- ///
+ ///
+ ///
+ ///
public ControlSubnetInfo Adapter { get; set; }
///
///
///
- public ushort Type { get; set; }
+ public ushort Type { get; set; }
///
///
///
- public ushort Index { get; set; }
+ public ushort Index { get; set; }
///
/// Constructor
@@ -216,18 +216,18 @@ namespace PepperDash.Core.SystemInfo;
///
public class ProgramChangeEventArgs : EventArgs
{
- ///
- ///
- ///
+ ///
+ ///
+ ///
public ProgramInfo Program { get; set; }
///
///
///
- public ushort Type { get; set; }
+ public ushort Type { get; set; }
///
///
///
- public ushort Index { get; set; }
+ public ushort Index { get; set; }
///
/// Constructor
@@ -240,7 +240,7 @@ namespace PepperDash.Core.SystemInfo;
///
/// Constructor overload
///
- ///
+ ///
///
public ProgramChangeEventArgs(ProgramInfo program, ushort type)
{
@@ -251,13 +251,14 @@ namespace PepperDash.Core.SystemInfo;
///
/// Constructor overload
///
- ///
+ ///
///
- ///
+ ///
public ProgramChangeEventArgs(ProgramInfo program, ushort type, ushort index)
{
Program = program;
Type = type;
Index = index;
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/SystemInfo/SystemInfoConfig.cs b/src/PepperDash.Core/SystemInfo/SystemInfoConfig.cs
index 950abecb..8dc3acaf 100644
--- a/src/PepperDash.Core/SystemInfo/SystemInfoConfig.cs
+++ b/src/PepperDash.Core/SystemInfo/SystemInfoConfig.cs
@@ -4,52 +4,52 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Core.SystemInfo;
-
+namespace PepperDash.Core.SystemInfo
+{
///
/// Processor info class
///
public class ProcessorInfo
{
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string Model { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string SerialNumber { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string Firmware { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string FirmwareDate { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string OsVersion { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string RuntimeEnvironment { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string DevicePlatform { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string ModuleDirectory { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string LocalTimeZone { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string ProgramIdTag { get; set; }
///
@@ -66,45 +66,45 @@ namespace PepperDash.Core.SystemInfo;
///
public class EthernetInfo
{
- ///
- ///
- ///
+ ///
+ ///
+ ///
public ushort DhcpIsOn { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string Hostname { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string MacAddress { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string IpAddress { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string Subnet { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string Gateway { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string Dns1 { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string Dns2 { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string Dns3 { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string Domain { get; set; }
///
@@ -121,29 +121,29 @@ namespace PepperDash.Core.SystemInfo;
///
public class ControlSubnetInfo
{
- ///
- ///
- ///
+ ///
+ ///
+ ///
public ushort Enabled { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public ushort IsInAutomaticMode { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string MacAddress { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string IpAddress { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string Subnet { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string RouterPrefix { get; set; }
///
@@ -160,37 +160,37 @@ namespace PepperDash.Core.SystemInfo;
///
public class ProgramInfo
{
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string Name { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string Header { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string System { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string ProgramIdTag { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string CompileTime { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string Database { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string Environment { get; set; }
- ///
- ///
- ///
+ ///
+ ///
+ ///
public string Programmer { get; set; }
///
@@ -200,4 +200,5 @@ namespace PepperDash.Core.SystemInfo;
{
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/SystemInfo/SystemInfoToSimpl.cs b/src/PepperDash.Core/SystemInfo/SystemInfoToSimpl.cs
index 00309676..45f787f5 100644
--- a/src/PepperDash.Core/SystemInfo/SystemInfoToSimpl.cs
+++ b/src/PepperDash.Core/SystemInfo/SystemInfoToSimpl.cs
@@ -4,37 +4,37 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Core.SystemInfo;
-
+namespace PepperDash.Core.SystemInfo
+{
///
/// System Info class
///
public class SystemInfoToSimpl
{
- ///
- /// Notifies of bool change
- ///
+ ///
+ /// Notifies of bool change
+ ///
public event EventHandler BoolChange;
- ///
- /// Notifies of string change
- ///
+ ///
+ /// Notifies of string change
+ ///
public event EventHandler StringChange;
- ///
- /// Notifies of processor change
- ///
+ ///
+ /// Notifies of processor change
+ ///
public event EventHandler ProcessorChange;
- ///
- /// Notifies of ethernet change
- ///
+ ///
+ /// Notifies of ethernet change
+ ///
public event EventHandler EthernetChange;
- ///
- /// Notifies of control subnet change
- ///
+ ///
+ /// Notifies of control subnet change
+ ///
public event EventHandler ControlSubnetChange;
- ///
- /// Notifies of program change
- ///
+ ///
+ /// Notifies of program change
+ ///
public event EventHandler ProgramChange;
///
@@ -336,10 +336,10 @@ namespace PepperDash.Core.SystemInfo;
///
/// private method to parse console messages
///
- ///
+ ///
///
- ///
- ///
+ ///
+ ///
///
private string ParseConsoleResponse(string data, string line, string dataStart, string dataEnd)
{
@@ -467,4 +467,5 @@ namespace PepperDash.Core.SystemInfo;
ProgramChange(this, args);
}
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Web/BouncyCertificate.cs b/src/PepperDash.Core/Web/BouncyCertificate.cs
index 148b6eb1..9e885094 100644
--- a/src/PepperDash.Core/Web/BouncyCertificate.cs
+++ b/src/PepperDash.Core/Web/BouncyCertificate.cs
@@ -20,336 +20,352 @@ using Org.BouncyCastle.Crypto.Operators;
using BigInteger = Org.BouncyCastle.Math.BigInteger;
using X509Certificate = Org.BouncyCastle.X509.X509Certificate;
-namespace PepperDash.Core;
-
-///
-/// Taken From https://github.com/rlipscombe/bouncy-castle-csharp/
-///
-internal class BouncyCertificate
+namespace PepperDash.Core
{
- public string CertificatePassword { get; set; } = "password";
- public X509Certificate2 LoadCertificate(string issuerFileName, string password)
- {
- // We need to pass 'Exportable', otherwise we can't get the private key.
- var issuerCertificate = new X509Certificate2(issuerFileName, password, X509KeyStorageFlags.Exportable);
- return issuerCertificate;
- }
-
- public X509Certificate2 IssueCertificate(string subjectName, X509Certificate2 issuerCertificate, string[] subjectAlternativeNames, KeyPurposeID[] usages)
- {
- // It's self-signed, so these are the same.
- var issuerName = issuerCertificate.Subject;
-
- var random = GetSecureRandom();
- var subjectKeyPair = GenerateKeyPair(random, 2048);
-
- var issuerKeyPair = DotNetUtilities.GetKeyPair(issuerCertificate.PrivateKey);
-
- var serialNumber = GenerateSerialNumber(random);
- var issuerSerialNumber = new BigInteger(issuerCertificate.GetSerialNumber());
-
- const bool isCertificateAuthority = false;
- var certificate = GenerateCertificate(random, subjectName, subjectKeyPair, serialNumber,
- subjectAlternativeNames, issuerName, issuerKeyPair,
- issuerSerialNumber, isCertificateAuthority,
- usages);
- return ConvertCertificate(certificate, subjectKeyPair, random);
- }
-
- public X509Certificate2 CreateCertificateAuthorityCertificate(string subjectName, string[] subjectAlternativeNames, KeyPurposeID[] usages)
- {
- // It's self-signed, so these are the same.
- var issuerName = subjectName;
-
- var random = GetSecureRandom();
- var subjectKeyPair = GenerateKeyPair(random, 2048);
-
- // It's self-signed, so these are the same.
- var issuerKeyPair = subjectKeyPair;
-
- var serialNumber = GenerateSerialNumber(random);
- var issuerSerialNumber = serialNumber; // Self-signed, so it's the same serial number.
-
- const bool isCertificateAuthority = true;
- var certificate = GenerateCertificate(random, subjectName, subjectKeyPair, serialNumber,
- subjectAlternativeNames, issuerName, issuerKeyPair,
- issuerSerialNumber, isCertificateAuthority,
- usages);
- return ConvertCertificate(certificate, subjectKeyPair, random);
- }
-
- public X509Certificate2 CreateSelfSignedCertificate(string subjectName, string[] subjectAlternativeNames, KeyPurposeID[] usages)
- {
- // It's self-signed, so these are the same.
- var issuerName = subjectName;
-
- var random = GetSecureRandom();
- var subjectKeyPair = GenerateKeyPair(random, 2048);
-
- // It's self-signed, so these are the same.
- var issuerKeyPair = subjectKeyPair;
-
- var serialNumber = GenerateSerialNumber(random);
- var issuerSerialNumber = serialNumber; // Self-signed, so it's the same serial number.
-
- const bool isCertificateAuthority = false;
- var certificate = GenerateCertificate(random, subjectName, subjectKeyPair, serialNumber,
- subjectAlternativeNames, issuerName, issuerKeyPair,
- issuerSerialNumber, isCertificateAuthority,
- usages);
- return ConvertCertificate(certificate, subjectKeyPair, random);
- }
-
- private SecureRandom GetSecureRandom()
- {
- // Since we're on Windows, we'll use the CryptoAPI one (on the assumption
- // that it might have access to better sources of entropy than the built-in
- // Bouncy Castle ones):
- var randomGenerator = new CryptoApiRandomGenerator();
- var random = new SecureRandom(randomGenerator);
- return random;
- }
-
- private X509Certificate GenerateCertificate(SecureRandom random,
- string subjectName,
- AsymmetricCipherKeyPair subjectKeyPair,
- BigInteger subjectSerialNumber,
- string[] subjectAlternativeNames,
- string issuerName,
- AsymmetricCipherKeyPair issuerKeyPair,
- BigInteger issuerSerialNumber,
- bool isCertificateAuthority,
- KeyPurposeID[] usages)
- {
- var certificateGenerator = new X509V3CertificateGenerator();
-
- certificateGenerator.SetSerialNumber(subjectSerialNumber);
-
- var issuerDN = new X509Name(issuerName);
- certificateGenerator.SetIssuerDN(issuerDN);
-
- // Note: The subject can be omitted if you specify a subject alternative name (SAN).
- var subjectDN = new X509Name(subjectName);
- certificateGenerator.SetSubjectDN(subjectDN);
-
- // Our certificate needs valid from/to values.
- var notBefore = DateTime.UtcNow.Date;
- var notAfter = notBefore.AddYears(2);
-
- certificateGenerator.SetNotBefore(notBefore);
- certificateGenerator.SetNotAfter(notAfter);
-
- // The subject's public key goes in the certificate.
- certificateGenerator.SetPublicKey(subjectKeyPair.Public);
-
- AddAuthorityKeyIdentifier(certificateGenerator, issuerDN, issuerKeyPair, issuerSerialNumber);
- AddSubjectKeyIdentifier(certificateGenerator, subjectKeyPair);
- //AddBasicConstraints(certificateGenerator, isCertificateAuthority);
-
- if (usages != null && usages.Any())
- AddExtendedKeyUsage(certificateGenerator, usages);
-
- if (subjectAlternativeNames != null && subjectAlternativeNames.Any())
- AddSubjectAlternativeNames(certificateGenerator, subjectAlternativeNames);
-
- // Set the signature algorithm. This is used to generate the thumbprint which is then signed
- // with the issuer's private key. We'll use SHA-256, which is (currently) considered fairly strong.
- const string signatureAlgorithm = "SHA256WithRSA";
-
- // The certificate is signed with the issuer's private key.
- ISignatureFactory signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, issuerKeyPair.Private, random);
- var certificate = certificateGenerator.Generate(signatureFactory);
- return certificate;
- }
-
///
- /// The certificate needs a serial number. This is used for revocation,
- /// and usually should be an incrementing index (which makes it easier to revoke a range of certificates).
- /// Since we don't have anywhere to store the incrementing index, we can just use a random number.
+ /// Taken From https://github.com/rlipscombe/bouncy-castle-csharp/
///
- ///
- ///
- private BigInteger GenerateSerialNumber(SecureRandom random)
+ internal class BouncyCertificate
{
- var serialNumber =
- BigIntegers.CreateRandomInRange(
- BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
- return serialNumber;
- }
-
- ///
- /// Generate a key pair.
- ///
- /// The random number generator.
- /// The key length in bits. For RSA, 2048 bits should be considered the minimum acceptable these days.
- ///
- private AsymmetricCipherKeyPair GenerateKeyPair(SecureRandom random, int strength)
- {
- var keyGenerationParameters = new KeyGenerationParameters(random, strength);
-
- var keyPairGenerator = new RsaKeyPairGenerator();
- keyPairGenerator.Init(keyGenerationParameters);
- var subjectKeyPair = keyPairGenerator.GenerateKeyPair();
- return subjectKeyPair;
- }
-
- ///
- /// Add the Authority Key Identifier. According to http://www.alvestrand.no/objectid/2.5.29.35.html, this
- /// identifies the public key to be used to verify the signature on this certificate.
- /// In a certificate chain, this corresponds to the "Subject Key Identifier" on the *issuer* certificate.
- /// The Bouncy Castle documentation, at http://www.bouncycastle.org/wiki/display/JA1/X.509+Public+Key+Certificate+and+Certification+Request+Generation,
- /// shows how to create this from the issuing certificate. Since we're creating a self-signed certificate, we have to do this slightly differently.
- ///
- ///
- ///
- ///
- ///
- private void AddAuthorityKeyIdentifier(X509V3CertificateGenerator certificateGenerator,
- X509Name issuerDN,
- AsymmetricCipherKeyPair issuerKeyPair,
- BigInteger issuerSerialNumber)
- {
- var authorityKeyIdentifierExtension =
- new AuthorityKeyIdentifier(
- SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(issuerKeyPair.Public),
- new GeneralNames(new GeneralName(issuerDN)),
- issuerSerialNumber);
- certificateGenerator.AddExtension(
- X509Extensions.AuthorityKeyIdentifier.Id, false, authorityKeyIdentifierExtension);
- }
-
- ///
- /// Add the "Subject Alternative Names" extension. Note that you have to repeat
- /// the value from the "Subject Name" property.
- ///
- ///
- ///
- private void AddSubjectAlternativeNames(X509V3CertificateGenerator certificateGenerator,
- IEnumerable subjectAlternativeNames)
- {
- var subjectAlternativeNamesExtension =
- new DerSequence(
- subjectAlternativeNames.Select(name => new GeneralName(GeneralName.DnsName, name))
- .ToArray());
- certificateGenerator.AddExtension(
- X509Extensions.SubjectAlternativeName.Id, false, subjectAlternativeNamesExtension);
- }
-
- ///
- /// Add the "Extended Key Usage" extension, specifying (for example) "server authentication".
- ///
- ///
- ///
- private void AddExtendedKeyUsage(X509V3CertificateGenerator certificateGenerator, KeyPurposeID[] usages)
- {
- certificateGenerator.AddExtension(
- X509Extensions.ExtendedKeyUsage.Id, false, new ExtendedKeyUsage(usages));
- }
-
- ///
- /// Add the "Basic Constraints" extension.
- ///
- ///
- ///
- private void AddBasicConstraints(X509V3CertificateGenerator certificateGenerator,
- bool isCertificateAuthority)
- {
- certificateGenerator.AddExtension(
- X509Extensions.BasicConstraints.Id, true, new BasicConstraints(isCertificateAuthority));
- }
-
- ///
- /// Add the Subject Key Identifier.
- ///
- ///
- ///
- private void AddSubjectKeyIdentifier(X509V3CertificateGenerator certificateGenerator,
- AsymmetricCipherKeyPair subjectKeyPair)
- {
- var subjectKeyIdentifierExtension =
- new SubjectKeyIdentifier(
- SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectKeyPair.Public));
- certificateGenerator.AddExtension(
- X509Extensions.SubjectKeyIdentifier.Id, false, subjectKeyIdentifierExtension);
- }
-
- private X509Certificate2 ConvertCertificate(X509Certificate certificate,
- AsymmetricCipherKeyPair subjectKeyPair,
- SecureRandom random)
- {
- // Now to convert the Bouncy Castle certificate to a .NET certificate.
- // See http://web.archive.org/web/20100504192226/http://www.fkollmann.de/v2/post/Creating-certificates-using-BouncyCastle.aspx
- // ...but, basically, we create a PKCS12 store (a .PFX file) in memory, and add the public and private key to that.
- var store = new Pkcs12StoreBuilder().Build();
-
- // What Bouncy Castle calls "alias" is the same as what Windows terms the "friendly name".
- string friendlyName = certificate.SubjectDN.ToString();
-
- // Add the certificate.
- var certificateEntry = new X509CertificateEntry(certificate);
- store.SetCertificateEntry(friendlyName, certificateEntry);
-
- // Add the private key.
- store.SetKeyEntry(friendlyName, new AsymmetricKeyEntry(subjectKeyPair.Private), new[] { certificateEntry });
-
- // Convert it to an X509Certificate2 object by saving/loading it from a MemoryStream.
- // It needs a password. Since we'll remove this later, it doesn't particularly matter what we use.
-
- var stream = new MemoryStream();
- store.Save(stream, CertificatePassword.ToCharArray(), random);
-
- var convertedCertificate =
- new X509Certificate2(stream.ToArray(),
- CertificatePassword,
- X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
- return convertedCertificate;
- }
-
- public void WriteCertificate(X509Certificate2 certificate, string outputDirectory, string certName)
- {
- // This password is the one attached to the PFX file. Use 'null' for no password.
- // Create PFX (PKCS #12) with private key
- try
+ public string CertificatePassword { get; set; } = "password";
+ public X509Certificate2 LoadCertificate(string issuerFileName, string password)
{
- var pfx = certificate.Export(X509ContentType.Pfx, CertificatePassword);
- File.WriteAllBytes(string.Format("{0}.pfx", Path.Combine(outputDirectory, certName)), pfx);
+ // We need to pass 'Exportable', otherwise we can't get the private key.
+ var issuerCertificate = new X509Certificate2(issuerFileName, password, X509KeyStorageFlags.Exportable);
+ return issuerCertificate;
}
- catch (Exception ex)
+
+ ///
+ /// IssueCertificate method
+ ///
+ public X509Certificate2 IssueCertificate(string subjectName, X509Certificate2 issuerCertificate, string[] subjectAlternativeNames, KeyPurposeID[] usages)
{
- CrestronConsole.PrintLine(string.Format("Failed to write x509 cert pfx\r\n{0}", ex.Message));
+ // It's self-signed, so these are the same.
+ var issuerName = issuerCertificate.Subject;
+
+ var random = GetSecureRandom();
+ var subjectKeyPair = GenerateKeyPair(random, 2048);
+
+ var issuerKeyPair = DotNetUtilities.GetKeyPair(issuerCertificate.PrivateKey);
+
+ var serialNumber = GenerateSerialNumber(random);
+ var issuerSerialNumber = new BigInteger(issuerCertificate.GetSerialNumber());
+
+ const bool isCertificateAuthority = false;
+ var certificate = GenerateCertificate(random, subjectName, subjectKeyPair, serialNumber,
+ subjectAlternativeNames, issuerName, issuerKeyPair,
+ issuerSerialNumber, isCertificateAuthority,
+ usages);
+ return ConvertCertificate(certificate, subjectKeyPair, random);
}
- // Create Base 64 encoded CER (public key only)
- using (var writer = new StreamWriter($"{Path.Combine(outputDirectory, certName)}.cer", false))
+
+ ///
+ /// CreateCertificateAuthorityCertificate method
+ ///
+ public X509Certificate2 CreateCertificateAuthorityCertificate(string subjectName, string[] subjectAlternativeNames, KeyPurposeID[] usages)
{
+ // It's self-signed, so these are the same.
+ var issuerName = subjectName;
+
+ var random = GetSecureRandom();
+ var subjectKeyPair = GenerateKeyPair(random, 2048);
+
+ // It's self-signed, so these are the same.
+ var issuerKeyPair = subjectKeyPair;
+
+ var serialNumber = GenerateSerialNumber(random);
+ var issuerSerialNumber = serialNumber; // Self-signed, so it's the same serial number.
+
+ const bool isCertificateAuthority = true;
+ var certificate = GenerateCertificate(random, subjectName, subjectKeyPair, serialNumber,
+ subjectAlternativeNames, issuerName, issuerKeyPair,
+ issuerSerialNumber, isCertificateAuthority,
+ usages);
+ return ConvertCertificate(certificate, subjectKeyPair, random);
+ }
+
+ ///
+ /// CreateSelfSignedCertificate method
+ ///
+ public X509Certificate2 CreateSelfSignedCertificate(string subjectName, string[] subjectAlternativeNames, KeyPurposeID[] usages)
+ {
+ // It's self-signed, so these are the same.
+ var issuerName = subjectName;
+
+ var random = GetSecureRandom();
+ var subjectKeyPair = GenerateKeyPair(random, 2048);
+
+ // It's self-signed, so these are the same.
+ var issuerKeyPair = subjectKeyPair;
+
+ var serialNumber = GenerateSerialNumber(random);
+ var issuerSerialNumber = serialNumber; // Self-signed, so it's the same serial number.
+
+ const bool isCertificateAuthority = false;
+ var certificate = GenerateCertificate(random, subjectName, subjectKeyPair, serialNumber,
+ subjectAlternativeNames, issuerName, issuerKeyPair,
+ issuerSerialNumber, isCertificateAuthority,
+ usages);
+ return ConvertCertificate(certificate, subjectKeyPair, random);
+ }
+
+ private SecureRandom GetSecureRandom()
+ {
+ // Since we're on Windows, we'll use the CryptoAPI one (on the assumption
+ // that it might have access to better sources of entropy than the built-in
+ // Bouncy Castle ones):
+ var randomGenerator = new CryptoApiRandomGenerator();
+ var random = new SecureRandom(randomGenerator);
+ return random;
+ }
+
+ private X509Certificate GenerateCertificate(SecureRandom random,
+ string subjectName,
+ AsymmetricCipherKeyPair subjectKeyPair,
+ BigInteger subjectSerialNumber,
+ string[] subjectAlternativeNames,
+ string issuerName,
+ AsymmetricCipherKeyPair issuerKeyPair,
+ BigInteger issuerSerialNumber,
+ bool isCertificateAuthority,
+ KeyPurposeID[] usages)
+ {
+ var certificateGenerator = new X509V3CertificateGenerator();
+
+ certificateGenerator.SetSerialNumber(subjectSerialNumber);
+
+ var issuerDN = new X509Name(issuerName);
+ certificateGenerator.SetIssuerDN(issuerDN);
+
+ // Note: The subject can be omitted if you specify a subject alternative name (SAN).
+ var subjectDN = new X509Name(subjectName);
+ certificateGenerator.SetSubjectDN(subjectDN);
+
+ // Our certificate needs valid from/to values.
+ var notBefore = DateTime.UtcNow.Date;
+ var notAfter = notBefore.AddYears(2);
+
+ certificateGenerator.SetNotBefore(notBefore);
+ certificateGenerator.SetNotAfter(notAfter);
+
+ // The subject's public key goes in the certificate.
+ certificateGenerator.SetPublicKey(subjectKeyPair.Public);
+
+ AddAuthorityKeyIdentifier(certificateGenerator, issuerDN, issuerKeyPair, issuerSerialNumber);
+ AddSubjectKeyIdentifier(certificateGenerator, subjectKeyPair);
+ //AddBasicConstraints(certificateGenerator, isCertificateAuthority);
+
+ if (usages != null && usages.Any())
+ AddExtendedKeyUsage(certificateGenerator, usages);
+
+ if (subjectAlternativeNames != null && subjectAlternativeNames.Any())
+ AddSubjectAlternativeNames(certificateGenerator, subjectAlternativeNames);
+
+ // Set the signature algorithm. This is used to generate the thumbprint which is then signed
+ // with the issuer's private key. We'll use SHA-256, which is (currently) considered fairly strong.
+ const string signatureAlgorithm = "SHA256WithRSA";
+
+ // The certificate is signed with the issuer's private key.
+ ISignatureFactory signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, issuerKeyPair.Private, random);
+ var certificate = certificateGenerator.Generate(signatureFactory);
+ return certificate;
+ }
+
+ ///
+ /// The certificate needs a serial number. This is used for revocation,
+ /// and usually should be an incrementing index (which makes it easier to revoke a range of certificates).
+ /// Since we don't have anywhere to store the incrementing index, we can just use a random number.
+ ///
+ ///
+ ///
+ private BigInteger GenerateSerialNumber(SecureRandom random)
+ {
+ var serialNumber =
+ BigIntegers.CreateRandomInRange(
+ BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
+ return serialNumber;
+ }
+
+ ///
+ /// Generate a key pair.
+ ///
+ /// The random number generator.
+ /// The key length in bits. For RSA, 2048 bits should be considered the minimum acceptable these days.
+ ///
+ private AsymmetricCipherKeyPair GenerateKeyPair(SecureRandom random, int strength)
+ {
+ var keyGenerationParameters = new KeyGenerationParameters(random, strength);
+
+ var keyPairGenerator = new RsaKeyPairGenerator();
+ keyPairGenerator.Init(keyGenerationParameters);
+ var subjectKeyPair = keyPairGenerator.GenerateKeyPair();
+ return subjectKeyPair;
+ }
+
+ ///
+ /// Add the Authority Key Identifier. According to http://www.alvestrand.no/objectid/2.5.29.35.html, this
+ /// identifies the public key to be used to verify the signature on this certificate.
+ /// In a certificate chain, this corresponds to the "Subject Key Identifier" on the *issuer* certificate.
+ /// The Bouncy Castle documentation, at http://www.bouncycastle.org/wiki/display/JA1/X.509+Public+Key+Certificate+and+Certification+Request+Generation,
+ /// shows how to create this from the issuing certificate. Since we're creating a self-signed certificate, we have to do this slightly differently.
+ ///
+ ///
+ ///
+ ///
+ ///
+ private void AddAuthorityKeyIdentifier(X509V3CertificateGenerator certificateGenerator,
+ X509Name issuerDN,
+ AsymmetricCipherKeyPair issuerKeyPair,
+ BigInteger issuerSerialNumber)
+ {
+ var authorityKeyIdentifierExtension =
+ new AuthorityKeyIdentifier(
+ SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(issuerKeyPair.Public),
+ new GeneralNames(new GeneralName(issuerDN)),
+ issuerSerialNumber);
+ certificateGenerator.AddExtension(
+ X509Extensions.AuthorityKeyIdentifier.Id, false, authorityKeyIdentifierExtension);
+ }
+
+ ///
+ /// Add the "Subject Alternative Names" extension. Note that you have to repeat
+ /// the value from the "Subject Name" property.
+ ///
+ ///
+ ///
+ private void AddSubjectAlternativeNames(X509V3CertificateGenerator certificateGenerator,
+ IEnumerable subjectAlternativeNames)
+ {
+ var subjectAlternativeNamesExtension =
+ new DerSequence(
+ subjectAlternativeNames.Select(name => new GeneralName(GeneralName.DnsName, name))
+ .ToArray());
+ certificateGenerator.AddExtension(
+ X509Extensions.SubjectAlternativeName.Id, false, subjectAlternativeNamesExtension);
+ }
+
+ ///
+ /// Add the "Extended Key Usage" extension, specifying (for example) "server authentication".
+ ///
+ ///
+ ///
+ private void AddExtendedKeyUsage(X509V3CertificateGenerator certificateGenerator, KeyPurposeID[] usages)
+ {
+ certificateGenerator.AddExtension(
+ X509Extensions.ExtendedKeyUsage.Id, false, new ExtendedKeyUsage(usages));
+ }
+
+ ///
+ /// Add the "Basic Constraints" extension.
+ ///
+ ///
+ ///
+ private void AddBasicConstraints(X509V3CertificateGenerator certificateGenerator,
+ bool isCertificateAuthority)
+ {
+ certificateGenerator.AddExtension(
+ X509Extensions.BasicConstraints.Id, true, new BasicConstraints(isCertificateAuthority));
+ }
+
+ ///
+ /// Add the Subject Key Identifier.
+ ///
+ ///
+ ///
+ private void AddSubjectKeyIdentifier(X509V3CertificateGenerator certificateGenerator,
+ AsymmetricCipherKeyPair subjectKeyPair)
+ {
+ var subjectKeyIdentifierExtension =
+ new SubjectKeyIdentifier(
+ SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectKeyPair.Public));
+ certificateGenerator.AddExtension(
+ X509Extensions.SubjectKeyIdentifier.Id, false, subjectKeyIdentifierExtension);
+ }
+
+ private X509Certificate2 ConvertCertificate(X509Certificate certificate,
+ AsymmetricCipherKeyPair subjectKeyPair,
+ SecureRandom random)
+ {
+ // Now to convert the Bouncy Castle certificate to a .NET certificate.
+ // See http://web.archive.org/web/20100504192226/http://www.fkollmann.de/v2/post/Creating-certificates-using-BouncyCastle.aspx
+ // ...but, basically, we create a PKCS12 store (a .PFX file) in memory, and add the public and private key to that.
+ var store = new Pkcs12StoreBuilder().Build();
+
+ // What Bouncy Castle calls "alias" is the same as what Windows terms the "friendly name".
+ string friendlyName = certificate.SubjectDN.ToString();
+
+ // Add the certificate.
+ var certificateEntry = new X509CertificateEntry(certificate);
+ store.SetCertificateEntry(friendlyName, certificateEntry);
+
+ // Add the private key.
+ store.SetKeyEntry(friendlyName, new AsymmetricKeyEntry(subjectKeyPair.Private), new[] { certificateEntry });
+
+ // Convert it to an X509Certificate2 object by saving/loading it from a MemoryStream.
+ // It needs a password. Since we'll remove this later, it doesn't particularly matter what we use.
+
+ var stream = new MemoryStream();
+ store.Save(stream, CertificatePassword.ToCharArray(), random);
+
+ var convertedCertificate =
+ new X509Certificate2(stream.ToArray(),
+ CertificatePassword,
+ X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
+ return convertedCertificate;
+ }
+
+ ///
+ /// WriteCertificate method
+ ///
+ public void WriteCertificate(X509Certificate2 certificate, string outputDirectory, string certName)
+ {
+ // This password is the one attached to the PFX file. Use 'null' for no password.
+ // Create PFX (PKCS #12) with private key
try
{
- var contents = string.Format("-----BEGIN CERTIFICATE-----\r\n{0}\r\n-----END CERTIFICATE-----", Convert.ToBase64String(certificate.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks));
- writer.Write(contents);
+ var pfx = certificate.Export(X509ContentType.Pfx, CertificatePassword);
+ File.WriteAllBytes(string.Format("{0}.pfx", Path.Combine(outputDirectory, certName)), pfx);
}
catch (Exception ex)
{
- CrestronConsole.PrintLine(string.Format("Failed to write x509 cert cer\r\n{0}", ex.Message));
+ CrestronConsole.PrintLine(string.Format("Failed to write x509 cert pfx\r\n{0}", ex.Message));
+ }
+ // Create Base 64 encoded CER (public key only)
+ using (var writer = new StreamWriter($"{Path.Combine(outputDirectory, certName)}.cer", false))
+ {
+ try
+ {
+ var contents = string.Format("-----BEGIN CERTIFICATE-----\r\n{0}\r\n-----END CERTIFICATE-----", Convert.ToBase64String(certificate.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks));
+ writer.Write(contents);
+ }
+ catch (Exception ex)
+ {
+ CrestronConsole.PrintLine(string.Format("Failed to write x509 cert cer\r\n{0}", ex.Message));
+ }
}
}
- }
- public bool AddCertToStore(X509Certificate2 cert, System.Security.Cryptography.X509Certificates.StoreName st, System.Security.Cryptography.X509Certificates.StoreLocation sl)
- {
- bool bRet = false;
-
- try
+ ///
+ /// AddCertToStore method
+ ///
+ public bool AddCertToStore(X509Certificate2 cert, System.Security.Cryptography.X509Certificates.StoreName st, System.Security.Cryptography.X509Certificates.StoreLocation sl)
{
- var store = new System.Security.Cryptography.X509Certificates.X509Store(st, sl);
- store.Open(System.Security.Cryptography.X509Certificates.OpenFlags.ReadWrite);
- store.Add(cert);
+ bool bRet = false;
- store.Close();
- bRet = true;
- }
- catch (Exception ex)
- {
- CrestronConsole.PrintLine(string.Format("AddCertToStore Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace));
- }
+ try
+ {
+ var store = new System.Security.Cryptography.X509Certificates.X509Store(st, sl);
+ store.Open(System.Security.Cryptography.X509Certificates.OpenFlags.ReadWrite);
+ store.Add(cert);
- return bRet;
+ store.Close();
+ bRet = true;
+ }
+ catch (Exception ex)
+ {
+ CrestronConsole.PrintLine(string.Format("AddCertToStore Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace));
+ }
+
+ return bRet;
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Web/RequestHandlers/DefaultRequestHandler.cs b/src/PepperDash.Core/Web/RequestHandlers/DefaultRequestHandler.cs
index 2fee0a62..154dc01e 100644
--- a/src/PepperDash.Core/Web/RequestHandlers/DefaultRequestHandler.cs
+++ b/src/PepperDash.Core/Web/RequestHandlers/DefaultRequestHandler.cs
@@ -1,10 +1,10 @@
using Crestron.SimplSharp.WebScripting;
-namespace PepperDash.Core.Web.RequestHandlers;
-
- ///
- /// Web API default request handler
- ///
+namespace PepperDash.Core.Web.RequestHandlers
+{
+ ///
+ /// Represents a DefaultRequestHandler
+ ///
public class DefaultRequestHandler : WebApiBaseRequestHandler
{
///
@@ -13,4 +13,5 @@ namespace PepperDash.Core.Web.RequestHandlers;
public DefaultRequestHandler()
: base(true)
{ }
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestAsyncHandler.cs b/src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestAsyncHandler.cs
index e8b3e85b..927092db 100644
--- a/src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestAsyncHandler.cs
+++ b/src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestAsyncHandler.cs
@@ -3,160 +3,164 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
-namespace PepperDash.Core.Web.RequestHandlers;
-
-public abstract class WebApiBaseRequestAsyncHandler:IHttpCwsHandler
+namespace PepperDash.Core.Web.RequestHandlers
{
- private readonly Dictionary> _handlers;
- protected readonly bool EnableCors;
-
- ///
- /// Constructor
- ///
- protected WebApiBaseRequestAsyncHandler(bool enableCors)
+ public abstract class WebApiBaseRequestAsyncHandler:IHttpCwsHandler
{
- EnableCors = enableCors;
+ private readonly Dictionary> _handlers;
+ protected readonly bool EnableCors;
- _handlers = new Dictionary>
+ ///
+ /// Constructor
+ ///
+ protected WebApiBaseRequestAsyncHandler(bool enableCors)
{
- {"CONNECT", HandleConnect},
- {"DELETE", HandleDelete},
- {"GET", HandleGet},
- {"HEAD", HandleHead},
- {"OPTIONS", HandleOptions},
- {"PATCH", HandlePatch},
- {"POST", HandlePost},
- {"PUT", HandlePut},
- {"TRACE", HandleTrace}
- };
- }
+ EnableCors = enableCors;
- ///
- /// Constructor
- ///
- protected WebApiBaseRequestAsyncHandler()
- : this(false)
- {
- }
-
- ///
- /// Handles CONNECT method requests
- ///
- ///
- protected virtual async Task HandleConnect(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Handles DELETE method requests
- ///
- ///
- protected virtual async Task HandleDelete(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Handles GET method requests
- ///
- ///
- protected virtual async Task HandleGet(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Handles HEAD method requests
- ///
- ///
- protected virtual async Task HandleHead(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Handles OPTIONS method requests
- ///
- ///
- protected virtual async Task HandleOptions(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Handles PATCH method requests
- ///
- ///
- protected virtual async Task HandlePatch(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Handles POST method requests
- ///
- ///
- protected virtual async Task HandlePost(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Handles PUT method requests
- ///
- ///
- protected virtual async Task HandlePut(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Handles TRACE method requests
- ///
- ///
- protected virtual async Task HandleTrace(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Process request
- ///
- ///
- public void ProcessRequest(HttpCwsContext context)
- {
- if (!_handlers.TryGetValue(context.Request.HttpMethod, out Func handler))
- {
- return;
+ _handlers = new Dictionary>
+ {
+ {"CONNECT", HandleConnect},
+ {"DELETE", HandleDelete},
+ {"GET", HandleGet},
+ {"HEAD", HandleHead},
+ {"OPTIONS", HandleOptions},
+ {"PATCH", HandlePatch},
+ {"POST", HandlePost},
+ {"PUT", HandlePut},
+ {"TRACE", HandleTrace}
+ };
}
- if (EnableCors)
+ ///
+ /// Constructor
+ ///
+ protected WebApiBaseRequestAsyncHandler()
+ : this(false)
{
- context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
- context.Response.Headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
}
- var handlerTask = handler(context);
+ ///
+ /// Handles CONNECT method requests
+ ///
+ ///
+ protected virtual async Task HandleConnect(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
- handlerTask.GetAwaiter().GetResult();
+ ///
+ /// Handles DELETE method requests
+ ///
+ ///
+ protected virtual async Task HandleDelete(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
+
+ ///
+ /// Handles GET method requests
+ ///
+ ///
+ protected virtual async Task HandleGet(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
+
+ ///
+ /// Handles HEAD method requests
+ ///
+ ///
+ protected virtual async Task HandleHead(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
+
+ ///
+ /// Handles OPTIONS method requests
+ ///
+ ///
+ protected virtual async Task HandleOptions(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
+
+ ///
+ /// Handles PATCH method requests
+ ///
+ ///
+ protected virtual async Task HandlePatch(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
+
+ ///
+ /// Handles POST method requests
+ ///
+ ///
+ protected virtual async Task HandlePost(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
+
+ ///
+ /// Handles PUT method requests
+ ///
+ ///
+ protected virtual async Task HandlePut(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
+
+ ///
+ /// Handles TRACE method requests
+ ///
+ ///
+ protected virtual async Task HandleTrace(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
+
+ ///
+ /// Process request
+ ///
+ ///
+ ///
+ /// ProcessRequest method
+ ///
+ public void ProcessRequest(HttpCwsContext context)
+ {
+ if (!_handlers.TryGetValue(context.Request.HttpMethod, out Func handler))
+ {
+ return;
+ }
+
+ if (EnableCors)
+ {
+ context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
+ context.Response.Headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
+ }
+
+ var handlerTask = handler(context);
+
+ handlerTask.GetAwaiter().GetResult();
+ }
}
}
diff --git a/src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs b/src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs
index 0d36a863..a02f3497 100644
--- a/src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs
+++ b/src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs
@@ -2,8 +2,8 @@
using System.Collections.Generic;
using Crestron.SimplSharp.WebScripting;
-namespace PepperDash.Core.Web.RequestHandlers;
-
+namespace PepperDash.Core.Web.RequestHandlers
+{
///
/// CWS Base Handler, implements IHttpCwsHandler
///
@@ -164,4 +164,5 @@ namespace PepperDash.Core.Web.RequestHandlers;
handler(context);
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Web/WebApiServer.cs b/src/PepperDash.Core/Web/WebApiServer.cs
index 814a6f1d..f07aab42 100644
--- a/src/PepperDash.Core/Web/WebApiServer.cs
+++ b/src/PepperDash.Core/Web/WebApiServer.cs
@@ -1,269 +1,291 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Threading;
using Crestron.SimplSharp;
using Crestron.SimplSharp.WebScripting;
-using PepperDash.Core.Web.RequestHandlers;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
using PepperDash.Core.Logging;
+using PepperDash.Core.Web.RequestHandlers;
-namespace PepperDash.Core.Web;
-
-///
-/// Web API server
-///
-public class WebApiServer : IKeyName
+namespace PepperDash.Core.Web
{
- private const string SplusKey = "Uninitialized Web API Server";
- private const string DefaultName = "Web API Server";
- private const string DefaultBasePath = "/api";
-
- private readonly object _serverLock = new();
- private HttpCwsServer _server;
-
///
- /// Gets or sets the Key
+ /// Web API server
///
- public string Key { get; private set; }
-
- ///
- /// Gets or sets the Name
- ///
- public string Name { get; private set; }
-
- ///
- /// Gets or sets the BasePath
- ///
- public string BasePath { get; private set; }
-
- ///
- /// Gets or sets the IsRegistered
- ///
- public bool IsRegistered { get; private set; }
-
- ///
- /// Constructor for S+. Make sure to set necessary properties using init method
- ///
- public WebApiServer()
- : this(SplusKey, DefaultName, null)
+ public class WebApiServer : IKeyName
{
- }
+ private const string SplusKey = "Uninitialized Web API Server";
+ private const string DefaultName = "Web API Server";
+ private const string DefaultBasePath = "/api";
- ///
- /// Constructor
- ///
- ///
- ///
- public WebApiServer(string key, string basePath)
- : this(key, DefaultName, basePath)
- {
- }
+ private const uint DebugTrace = 0;
+ private const uint DebugInfo = 1;
+ private const uint DebugVerbose = 2;
- ///
- /// Constructor
- ///
- ///
- ///
- ///
- public WebApiServer(string key, string name, string basePath)
- {
- Key = key;
- Name = string.IsNullOrEmpty(name) ? DefaultName : name;
- BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath;
+ private readonly CCriticalSection _serverLock = new CCriticalSection();
+ private HttpCwsServer _server;
- this.LogInformation("Creating Web API Server with Key: {Key}, Name: {Name}, BasePath: {BasePath}", Key, Name, BasePath);
+ ///
+ /// Gets or sets the Key
+ ///
+ public string Key { get; private set; }
- if (_server == null) _server = new HttpCwsServer(BasePath);
+ ///
+ /// Gets or sets the Name
+ ///
+ public string Name { get; private set; }
- _server.AuthenticateAllRoutes = false;
+ ///
+ /// Gets or sets the BasePath
+ ///
+ public string BasePath { get; private set; }
- _server.setProcessName(Key);
- _server.HttpRequestHandler = new DefaultRequestHandler();
- _server.ReceivedRequestEvent += ReceivedRequestEventHandler;
+ ///
+ /// Gets or sets the IsRegistered
+ ///
+ public bool IsRegistered { get; private set; }
- CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
- CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler;
- }
+ //public IHttpCwsHandler HttpRequestHandler
+ //{
+ // get { return _server.HttpRequestHandler; }
+ // set
+ // {
+ // if (_server == null) return;
+ // _server.HttpRequestHandler = value;
+ // }
+ //}
- ///
- /// Program status event handler
- ///
- ///
- void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
- {
- if (programEventType != eProgramStatusEventType.Stopping) return;
+ //public event EventHandler ReceivedRequestEvent
+ //{
+ // add { _server.ReceivedRequestEvent += new HttpCwsRequestEventHandler(value); }
+ // remove { _server.ReceivedRequestEvent -= new HttpCwsRequestEventHandler(value); }
+ //}
- this.LogInformation("Program stopping. stopping server");
-
- Stop();
- }
-
- ///
- /// Ethernet event handler
- ///
- ///
- void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
- {
- if (ethernetEventArgs.EthernetEventType != eEthernetEventType.LinkUp)
+ ///
+ /// Constructor for S+. Make sure to set necessary properties using init method
+ ///
+ public WebApiServer()
+ : this(SplusKey, DefaultName, null)
{
- return;
}
- if (IsRegistered)
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ public WebApiServer(string key, string basePath)
+ : this(key, DefaultName, basePath)
{
- this.LogInformation("Ethernet link up. Server is already registered.");
- return;
}
- this.LogInformation("Ethernet link up. Starting server");
-
- Start();
- }
-
- // ///
- // /// Initialize method
- // ///
- // public void Initialize(string key, string basePath)
- // {
- // Key = key;
- // BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath;
- // }
-
- ///
- /// Adds a route to CWS
- ///
- public void AddRoute(HttpCwsRoute route)
- {
- if (route == null)
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ ///
+ public WebApiServer(string key, string name, string basePath)
{
- this.LogWarning("Failed to add route, route parameter is null");
- return;
+ Key = key;
+ Name = string.IsNullOrEmpty(name) ? DefaultName : name;
+ BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath;
+
+ if (_server == null) _server = new HttpCwsServer(BasePath);
+
+ _server.AuthenticateAllRoutes = false;
+
+ _server.setProcessName(Key);
+ _server.HttpRequestHandler = new DefaultRequestHandler();
+
+ CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
+ CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler;
}
- _server.Routes.Add(route);
-
- }
-
- ///
- /// Removes a route from CWS
- ///
- ///
- ///
- /// RemoveRoute method
- ///
- public void RemoveRoute(HttpCwsRoute route)
- {
- if (route == null)
+ ///
+ /// Program status event handler
+ ///
+ ///
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
{
- this.LogWarning("Failed to remove route, route parameter is null");
- return;
+ if (programEventType != eProgramStatusEventType.Stopping) return;
+
+ this.LogInformation("Program stopping. stopping server");
+
+ Stop();
}
- _server.Routes.Remove(route);
- }
-
- ///
- /// Sets the fallback request handler that is invoked when no registered route
- /// matches an incoming request. Must be called before .
- ///
- /// The handler to use as the server-level fallback.
- public void SetFallbackHandler(IHttpCwsHandler handler)
- {
- if (handler == null)
+ ///
+ /// Ethernet event handler
+ ///
+ ///
+ void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
{
- this.LogWarning("SetFallbackHandler: handler parameter is null, ignoring");
- return;
+ // Re-enable the server if the link comes back up and the status should be connected
+ if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp && IsRegistered)
+ {
+ this.LogInformation("Ethernet link up. Server is alreedy registered.");
+ return;
+ }
+
+ this.LogInformation("Ethernet link up. Starting server");
+
+ Start();
}
- _server.HttpRequestHandler = handler;
- }
+ ///
+ /// Initialize method
+ ///
+ public void Initialize(string key, string basePath)
+ {
+ Key = key;
+ BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath;
+ }
- ///
- /// GetRouteCollection method
- ///
- public HttpCwsRouteCollection GetRouteCollection()
- {
- return _server.Routes;
- }
+ ///
+ /// Adds a route to CWS
+ ///
+ public void AddRoute(HttpCwsRoute route)
+ {
+ if (route == null)
+ {
+ this.LogWarning("Failed to add route, route parameter is null");
+ return;
+ }
- ///
- /// Starts CWS instance
- ///
- public void Start()
- {
- lock (_serverLock)
+ _server.Routes.Add(route);
+
+ }
+
+ ///
+ /// Removes a route from CWS
+ ///
+ ///
+ public void RemoveRoute(HttpCwsRoute route)
+ {
+ if (route == null)
+ {
+ this.LogWarning("Failed to remove route, route parameter is null");
+ return;
+ }
+
+ _server.Routes.Remove(route);
+ }
+
+ ///
+ /// Sets the fallback request handler that is invoked when no registered route
+ /// matches an incoming request. Must be called before .
+ ///
+ /// The handler to use as the server-level fallback.
+ public void SetFallbackHandler(IHttpCwsHandler handler)
+ {
+ if (handler == null)
+ {
+ this.LogWarning("SetFallbackHandler: handler parameter is null, ignoring");
+ return;
+ }
+
+ _server.HttpRequestHandler = handler;
+ }
+
+ ///
+ /// GetRouteCollection method
+ ///
+ public HttpCwsRouteCollection GetRouteCollection()
+ {
+ return _server.Routes;
+ }
+
+ ///
+ /// Starts CWS instance
+ ///
+ public void Start()
{
try
{
+ _serverLock.Enter();
+
if (_server == null)
{
- this.LogDebug("Server is null, unable to start");
+ this.LogWarning("Server is null, unable to start");
return;
}
if (IsRegistered)
{
- this.LogDebug("Server has already been started");
+ this.LogWarning("Server has already been started");
return;
}
IsRegistered = _server.Register();
- this.LogDebug("Starting server, registration {0}", IsRegistered ? "was successful" : "failed");
+ this.LogInformation("Starting server, registration {registrationResult}", IsRegistered ? "was successful" : "failed");
}
catch (Exception ex)
{
- this.LogException(ex, "Start Exception Message: {0}", ex.Message);
- this.LogVerbose("Start Exception StackTrace: {0}", ex.StackTrace);
+ this.LogError("Start Exception Message: {message}", ex.Message);
+ this.LogDebug(ex, "Start Exception StackTrace");
}
- } // end lock
- }
+ finally
+ {
+ _serverLock.Leave();
+ }
+ }
- ///
- /// Stop method
- ///
- public void Stop()
- {
- lock (_serverLock)
+ ///
+ /// Stop method
+ ///
+ public void Stop()
{
try
{
+ _serverLock.Enter();
+
if (_server == null)
{
- this.LogDebug("Server is null or has already been stopped");
+ this.LogWarning("Server is null or has already been stopped");
return;
}
- var unregistered = _server.Unregister();
- IsRegistered = !unregistered;
+ IsRegistered = _server.Unregister() == false;
- this.LogDebug("Stopping server, unregistration {0}", unregistered ? "was successful" : "failed");
+ this.LogInformation("Stopping server, unregistration {unregistrationResult}", IsRegistered ? "failed" : "was successful");
+
+ _server.Dispose();
+ _server = null;
}
catch (Exception ex)
{
- this.LogException(ex, "Server Stop Exception Message: {0}", ex.Message);
+ this.LogError("Server Stop Exception Message: {message}", ex.Message);
+ this.LogDebug(ex, "Server Stop Exception StackTrace");
+ }
+ finally
+ {
+ _serverLock.Leave();
}
- } // end lock
- }
-
- ///
- /// Received request handler
- ///
- ///
- /// This is here for development and testing
- ///
- ///
- ///
- public void ReceivedRequestEventHandler(object sender, HttpCwsRequestEventArgs args)
- {
- try
- {
- var req = args.Context?.Request;
- this.LogVerbose("ReceivedRequestEventHandler: {Method} {Path}", req?.HttpMethod, req?.Path);
}
- catch (Exception ex)
+
+ ///
+ /// Received request handler
+ ///
+ ///
+ /// This is here for development and testing
+ ///
+ ///
+ ///
+ public void ReceivedRequestEventHandler(object sender, HttpCwsRequestEventArgs args)
{
- this.LogException(ex, "ReceivedRequestEventHandler Exception Message: {0}", ex.Message);
+ try
+ {
+ var j = JsonConvert.SerializeObject(args.Context, Formatting.Indented);
+ this.LogVerbose("RecieveRequestEventHandler Context:\x0d\x0a{0}", j);
+ }
+ catch (Exception ex)
+ {
+ this.LogError("ReceivedRequestEventHandler Exception Message: {message}", ex.Message);
+ this.LogDebug(ex, "ReceivedRequestEventHandler Exception StackTrace: {stackTrace}", ex.StackTrace);
+ }
}
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/WebApi/Presets/Preset.cs b/src/PepperDash.Core/WebApi/Presets/Preset.cs
new file mode 100644
index 00000000..88df890c
--- /dev/null
+++ b/src/PepperDash.Core/WebApi/Presets/Preset.cs
@@ -0,0 +1,87 @@
+using System;
+
+namespace PepperDash.Core.WebApi.Presets
+{
+ ///
+ /// Represents a Preset
+ ///
+ public class Preset
+ {
+ ///
+ /// ID of preset
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// Gets or sets the UserId
+ ///
+ public int UserId { get; set; }
+
+ ///
+ /// Gets or sets the RoomTypeId
+ ///
+ public int RoomTypeId { get; set; }
+
+ ///
+ /// Gets or sets the PresetName
+ ///
+ public string PresetName { get; set; }
+
+ ///
+ /// Gets or sets the PresetNumber
+ ///
+ public int PresetNumber { get; set; }
+
+ ///
+ /// Gets or sets the Data
+ ///
+ public string Data { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ public Preset()
+ {
+ PresetName = "";
+ PresetNumber = 1;
+ Data = "{}";
+ }
+ }
+
+ ///
+ /// Represents a PresetReceivedEventArgs
+ ///
+ public class PresetReceivedEventArgs : EventArgs
+ {
+ ///
+ /// True when the preset is found
+ ///
+ public bool LookupSuccess { get; private set; }
+
+ ///
+ /// Gets or sets the ULookupSuccess
+ ///
+ public ushort ULookupSuccess { get { return (ushort)(LookupSuccess ? 1 : 0); } }
+
+ ///
+ /// Gets or sets the Preset
+ ///
+ public Preset Preset { get; private set; }
+
+ ///
+ /// For Simpl+
+ ///
+ public PresetReceivedEventArgs() { }
+
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ public PresetReceivedEventArgs(Preset preset, bool success)
+ {
+ LookupSuccess = success;
+ Preset = preset;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/WebApi/Presets/User.cs b/src/PepperDash.Core/WebApi/Presets/User.cs
new file mode 100644
index 00000000..4044ae7b
--- /dev/null
+++ b/src/PepperDash.Core/WebApi/Presets/User.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Core.WebApi.Presets
+{
+ ///
+ ///
+ ///
+ public class User
+ {
+ ///
+ ///
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// Gets or sets the ExternalId
+ ///
+ public string ExternalId { get; set; }
+
+ ///
+ /// Gets or sets the FirstName
+ ///
+ public string FirstName { get; set; }
+
+ ///
+ /// Gets or sets the LastName
+ ///
+ public string LastName { get; set; }
+ }
+
+
+ ///
+ ///
+ ///
+ public class UserReceivedEventArgs : EventArgs
+ {
+ ///
+ /// True when user is found
+ ///
+ public bool LookupSuccess { get; private set; }
+
+ ///
+ /// Gets or sets the ULookupSuccess
+ ///
+ public ushort ULookupSuccess { get { return (ushort)(LookupSuccess ? 1 : 0); } }
+
+ ///
+ /// Gets or sets the User
+ ///
+ public User User { get; private set; }
+
+ ///
+ /// For Simpl+
+ ///
+ public UserReceivedEventArgs() { }
+
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ public UserReceivedEventArgs(User user, bool success)
+ {
+ LookupSuccess = success;
+ User = user;
+ }
+ }
+
+ ///
+ /// Represents a UserAndRoomMessage
+ ///
+ public class UserAndRoomMessage
+ {
+ ///
+ ///
+ ///
+ public int UserId { get; set; }
+
+ ///
+ /// Gets or sets the RoomTypeId
+ ///
+ public int RoomTypeId { get; set; }
+
+ ///
+ /// Gets or sets the PresetNumber
+ ///
+ public int PresetNumber { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/WebApi/Presets/WebApiPasscodeClient.cs b/src/PepperDash.Core/WebApi/Presets/WebApiPasscodeClient.cs
new file mode 100644
index 00000000..131430a2
--- /dev/null
+++ b/src/PepperDash.Core/WebApi/Presets/WebApiPasscodeClient.cs
@@ -0,0 +1,283 @@
+using System;
+using Crestron.SimplSharp; // For Basic SIMPL# Classes
+using Crestron.SimplSharp.CrestronIO;
+using Crestron.SimplSharp.Net.Http;
+using Crestron.SimplSharp.Net.Https;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core.JsonToSimpl;
+
+
+namespace PepperDash.Core.WebApi.Presets
+{
+ ///
+ /// Passcode client for the WebApi
+ ///
+ [Obsolete("WebApiPasscodeClient is no longer supported and will be removed in a future release.")]
+ public class WebApiPasscodeClient : IKeyed
+ {
+ ///
+ /// Notifies when user received
+ ///
+ public event EventHandler UserReceived;
+
+ ///
+ /// Notifies when Preset received
+ ///
+ public event EventHandler PresetReceived;
+
+ ///
+ /// Gets or sets the Key
+ ///
+ public string Key { get; private set; }
+
+ //string JsonMasterKey;
+
+ ///
+ /// An embedded JsonToSimpl master object.
+ ///
+ JsonToSimplGenericMaster J2SMaster;
+
+ string UrlBase;
+
+ string DefaultPresetJsonFilePath;
+
+ User CurrentUser;
+
+ Preset CurrentPreset;
+
+
+ ///
+ /// SIMPL+ can only execute the default constructor. If you have variables that require initialization, please
+ /// use an Initialize method
+ ///
+ public WebApiPasscodeClient()
+ {
+ }
+
+ ///
+ /// Initializes the instance
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void Initialize(string key, string jsonMasterKey, string urlBase, string defaultPresetJsonFilePath)
+ {
+ Key = key;
+ //JsonMasterKey = jsonMasterKey;
+ UrlBase = urlBase;
+ DefaultPresetJsonFilePath = defaultPresetJsonFilePath;
+
+ J2SMaster = new JsonToSimplGenericMaster();
+ J2SMaster.SaveCallback = this.SaveCallback;
+ J2SMaster.Initialize(jsonMasterKey);
+ }
+
+ ///
+ /// Gets the user for a passcode
+ ///
+ ///
+ ///
+ /// GetUserForPasscode method
+ ///
+ public void GetUserForPasscode(string passcode)
+ {
+ // Bullshit duplicate code here... These two cases should be the same
+ // except for https/http and the certificate ignores
+ if (!UrlBase.StartsWith("https"))
+ return;
+ var req = new HttpsClientRequest();
+ req.Url = new UrlParser(UrlBase + "/api/users/dopin");
+ req.RequestType = Crestron.SimplSharp.Net.Https.RequestType.Post;
+ req.Header.AddHeader(new HttpsHeader("Content-Type", "application/json"));
+ req.Header.AddHeader(new HttpsHeader("Accept", "application/json"));
+ var jo = new JObject();
+ jo.Add("pin", passcode);
+ req.ContentString = jo.ToString();
+
+ var client = new HttpsClient();
+ client.HostVerification = false;
+ client.PeerVerification = false;
+ var resp = client.Dispatch(req);
+ var handler = UserReceived;
+ if (resp.Code == 200)
+ {
+ //CrestronConsole.PrintLine("Received: {0}", resp.ContentString);
+ var user = JsonConvert.DeserializeObject(resp.ContentString);
+ CurrentUser = user;
+ if (handler != null)
+ handler(this, new UserReceivedEventArgs(user, true));
+ }
+ else
+ if (handler != null)
+ handler(this, new UserReceivedEventArgs(null, false));
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// GetPresetForThisUser method
+ ///
+ public void GetPresetForThisUser(int roomTypeId, int presetNumber)
+ {
+ if (CurrentUser == null)
+ {
+ CrestronConsole.PrintLine("GetPresetForThisUser no user loaded");
+ return;
+ }
+
+ var msg = new UserAndRoomMessage
+ {
+ UserId = CurrentUser.Id,
+ RoomTypeId = roomTypeId,
+ PresetNumber = presetNumber
+ };
+
+ var handler = PresetReceived;
+ try
+ {
+ if (!UrlBase.StartsWith("https"))
+ return;
+ var req = new HttpsClientRequest();
+ req.Url = new UrlParser(UrlBase + "/api/presets/userandroom");
+ req.RequestType = Crestron.SimplSharp.Net.Https.RequestType.Post;
+ req.Header.AddHeader(new HttpsHeader("Content-Type", "application/json"));
+ req.Header.AddHeader(new HttpsHeader("Accept", "application/json"));
+ req.ContentString = JsonConvert.SerializeObject(msg);
+
+ var client = new HttpsClient();
+ client.HostVerification = false;
+ client.PeerVerification = false;
+
+ // ask for the preset
+ var resp = client.Dispatch(req);
+ if (resp.Code == 200) // got it
+ {
+ //Debug.Console(1, this, "Received: {0}", resp.ContentString);
+ var preset = JsonConvert.DeserializeObject(resp.ContentString);
+ CurrentPreset = preset;
+
+ //if there's no preset data, load the template
+ if (preset.Data == null || preset.Data.Trim() == string.Empty || JObject.Parse(preset.Data).Count == 0)
+ {
+ //Debug.Console(1, this, "Loaded preset has no data. Loading default template.");
+ LoadDefaultPresetData();
+ return;
+ }
+
+ J2SMaster.LoadWithJson(preset.Data);
+ if (handler != null)
+ handler(this, new PresetReceivedEventArgs(preset, true));
+ }
+ else // no existing preset
+ {
+ CurrentPreset = new Preset();
+ LoadDefaultPresetData();
+ if (handler != null)
+ handler(this, new PresetReceivedEventArgs(null, false));
+ }
+ }
+ catch (HttpException e)
+ {
+ var resp = e.Response;
+ Debug.Console(1, this, "No preset received (code {0}). Loading default template", resp.Code);
+ LoadDefaultPresetData();
+ if (handler != null)
+ PresetReceived(this, new PresetReceivedEventArgs(null, false));
+ }
+ }
+
+ void LoadDefaultPresetData()
+ {
+ CurrentPreset = null;
+ if (!File.Exists(DefaultPresetJsonFilePath))
+ {
+ Debug.Console(0, this, "Cannot load default preset file. Saving will not work");
+ return;
+ }
+ using (StreamReader sr = new StreamReader(DefaultPresetJsonFilePath))
+ {
+ try
+ {
+ var data = sr.ReadToEnd();
+ J2SMaster.SetJsonWithoutEvaluating(data);
+ CurrentPreset = new Preset() { Data = data, UserId = CurrentUser.Id };
+ }
+ catch (Exception e)
+ {
+ Debug.Console(0, this, "Error reading default preset JSON: \r{0}", e);
+ }
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// SavePresetForThisUser method
+ ///
+ public void SavePresetForThisUser(int roomTypeId, int presetNumber)
+ {
+ if (CurrentPreset == null)
+ LoadDefaultPresetData();
+ //return;
+
+ //// A new preset needs to have its numbers set
+ //if (CurrentPreset.IsNewPreset)
+ //{
+ CurrentPreset.UserId = CurrentUser.Id;
+ CurrentPreset.RoomTypeId = roomTypeId;
+ CurrentPreset.PresetNumber = presetNumber;
+ //}
+ J2SMaster.Save(); // Will trigger callback when ready
+ }
+
+ ///
+ /// After save operation on JSON master happens, send it to server
+ ///
+ ///
+ void SaveCallback(string json)
+ {
+ CurrentPreset.Data = json;
+
+ if (!UrlBase.StartsWith("https"))
+ return;
+ var req = new HttpsClientRequest();
+ req.RequestType = Crestron.SimplSharp.Net.Https.RequestType.Post;
+ req.Url = new UrlParser(string.Format("{0}/api/presets/addorchange", UrlBase));
+ req.Header.AddHeader(new HttpsHeader("Content-Type", "application/json"));
+ req.Header.AddHeader(new HttpsHeader("Accept", "application/json"));
+ req.ContentString = JsonConvert.SerializeObject(CurrentPreset);
+
+ var client = new HttpsClient();
+ client.HostVerification = false;
+ client.PeerVerification = false;
+ try
+ {
+ var resp = client.Dispatch(req);
+
+ // 201=created
+ // 204=empty content
+ if (resp.Code == 201)
+ CrestronConsole.PrintLine("Preset added");
+ else if (resp.Code == 204)
+ CrestronConsole.PrintLine("Preset updated");
+ else if (resp.Code == 209)
+ CrestronConsole.PrintLine("Preset already exists. Cannot save as new.");
+ else
+ CrestronConsole.PrintLine("Preset save failed: {0}\r", resp.Code, resp.ContentString);
+ }
+ catch (HttpException e)
+ {
+
+ CrestronConsole.PrintLine("Preset save exception {0}", e.Response.Code);
+ }
+ }
+ }
+}
diff --git a/src/PepperDash.Core/XSigUtility/Serialization/IXSigSerialization.cs b/src/PepperDash.Core/XSigUtility/Serialization/IXSigSerialization.cs
index b1dcae6b..8303731e 100644
--- a/src/PepperDash.Core/XSigUtility/Serialization/IXSigSerialization.cs
+++ b/src/PepperDash.Core/XSigUtility/Serialization/IXSigSerialization.cs
@@ -1,24 +1,25 @@
using System.Collections.Generic;
using PepperDash.Core.Intersystem.Tokens;
-namespace PepperDash.Core.Intersystem.Serialization;
-
-///
-/// Interface to determine XSig serialization for an object.
-///
-public interface IXSigSerialization
+namespace PepperDash.Core.Intersystem.Serialization
{
///
- /// Serialize the sig data
+ /// Interface to determine XSig serialization for an object.
///
- ///
- IEnumerable Serialize();
+ public interface IXSigSerialization
+ {
+ ///
+ /// Serialize the sig data
+ ///
+ ///
+ IEnumerable Serialize();
- ///
- /// Deserialize the sig data
- ///
- ///
- ///
- ///
- T Deserialize(IEnumerable tokens) where T : class, IXSigSerialization;
+ ///
+ /// Deserialize the sig data
+ ///
+ ///
+ ///
+ ///
+ T Deserialize(IEnumerable tokens) where T : class, IXSigSerialization;
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/XSigUtility/Serialization/XSigSerializationException.cs b/src/PepperDash.Core/XSigUtility/Serialization/XSigSerializationException.cs
index 4db0e970..8f3fc047 100644
--- a/src/PepperDash.Core/XSigUtility/Serialization/XSigSerializationException.cs
+++ b/src/PepperDash.Core/XSigUtility/Serialization/XSigSerializationException.cs
@@ -1,27 +1,28 @@
using System;
-namespace PepperDash.Core.Intersystem.Serialization;
-
-///
-/// Class to handle this specific exception type
-///
-public class XSigSerializationException : Exception
+namespace PepperDash.Core.Intersystem.Serialization
{
///
- /// default constructor
+ /// Class to handle this specific exception type
///
- public XSigSerializationException() { }
+ public class XSigSerializationException : Exception
+ {
+ ///
+ /// default constructor
+ ///
+ public XSigSerializationException() { }
- ///
- /// constructor with message
- ///
- ///
- public XSigSerializationException(string message) : base(message) { }
+ ///
+ /// constructor with message
+ ///
+ ///
+ public XSigSerializationException(string message) : base(message) { }
- ///
- /// constructor with message and innner exception
- ///
- ///
- ///
- public XSigSerializationException(string message, Exception inner) : base(message, inner) { }
+ ///
+ /// constructor with message and innner exception
+ ///
+ ///
+ ///
+ public XSigSerializationException(string message, Exception inner) : base(message, inner) { }
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/XSigUtility/Tokens/XSigAnalogToken.cs b/src/PepperDash.Core/XSigUtility/Tokens/XSigAnalogToken.cs
index b5cb2e68..68c61d90 100644
--- a/src/PepperDash.Core/XSigUtility/Tokens/XSigAnalogToken.cs
+++ b/src/PepperDash.Core/XSigUtility/Tokens/XSigAnalogToken.cs
@@ -1,87 +1,98 @@
using System;
-namespace PepperDash.Core.Intersystem.Tokens;
-
-///
-/// Represents an XSigAnalogToken
-///
-public sealed class XSigAnalogToken : XSigToken, IFormattable
+namespace PepperDash.Core.Intersystem.Tokens
{
- private readonly ushort _value;
-
///
- /// Constructor
+ /// Represents an XSigAnalogToken
///
- ///
- ///
- public XSigAnalogToken(int index, ushort value)
- : base(index)
+ public sealed class XSigAnalogToken : XSigToken, IFormattable
{
- // 10-bits available for analog encoded data
- if (index >= 1024 || index < 0)
- throw new ArgumentOutOfRangeException("index");
+ private readonly ushort _value;
- _value = value;
- }
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ public XSigAnalogToken(int index, ushort value)
+ : base(index)
+ {
+ // 10-bits available for analog encoded data
+ if (index >= 1024 || index < 0)
+ throw new ArgumentOutOfRangeException("index");
- ///
- ///
- ///
- public ushort Value
- {
- get { return _value; }
- }
+ _value = value;
+ }
- ///
- ///
- ///
- public override XSigTokenType TokenType
- {
- get { return XSigTokenType.Analog; }
- }
+ ///
+ ///
+ ///
+ public ushort Value
+ {
+ get { return _value; }
+ }
- ///
- ///
- ///
- ///
- public override byte[] GetBytes()
- {
- return new[] {
- (byte)(0xC0 | ((Value & 0xC000) >> 10) | (Index - 1 >> 7)),
- (byte)((Index - 1) & 0x7F),
- (byte)((Value & 0x3F80) >> 7),
- (byte)(Value & 0x7F)
- };
- }
+ ///
+ ///
+ ///
+ public override XSigTokenType TokenType
+ {
+ get { return XSigTokenType.Analog; }
+ }
- ///
- ///
- ///
- ///
- ///
- public override XSigToken GetTokenWithOffset(int offset)
- {
- if (offset == 0) return this;
- return new XSigAnalogToken(Index + offset, Value);
- }
+ ///
+ ///
+ ///
+ ///
+ public override byte[] GetBytes()
+ {
+ return new[] {
+ (byte)(0xC0 | ((Value & 0xC000) >> 10) | (Index - 1 >> 7)),
+ (byte)((Index - 1) & 0x7F),
+ (byte)((Value & 0x3F80) >> 7),
+ (byte)(Value & 0x7F)
+ };
+ }
- ///
- ///
- ///
- ///
- public override string ToString()
- {
- return Index + " = 0x" + Value.ToString("X4");
- }
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// GetTokenWithOffset method
+ ///
+ public override XSigToken GetTokenWithOffset(int offset)
+ {
+ if (offset == 0) return this;
+ return new XSigAnalogToken(Index + offset, Value);
+ }
- ///
- ///
- ///
- ///
- ///
- ///
- public string ToString(string format, IFormatProvider formatProvider)
- {
- return Value.ToString(format, formatProvider);
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// ToString method
+ ///
+ ///
+ public override string ToString()
+ {
+ return Index + " = 0x" + Value.ToString("X4");
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// ToString method
+ ///
+ public string ToString(string format, IFormatProvider formatProvider)
+ {
+ return Value.ToString(format, formatProvider);
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/XSigUtility/Tokens/XSigDigitalToken.cs b/src/PepperDash.Core/XSigUtility/Tokens/XSigDigitalToken.cs
index a82d0eb2..50fac7fc 100644
--- a/src/PepperDash.Core/XSigUtility/Tokens/XSigDigitalToken.cs
+++ b/src/PepperDash.Core/XSigUtility/Tokens/XSigDigitalToken.cs
@@ -1,84 +1,95 @@
using System;
-namespace PepperDash.Core.Intersystem.Tokens;
-
-///
-/// Represents an XSigDigitalToken
-///
-public sealed class XSigDigitalToken : XSigToken
+namespace PepperDash.Core.Intersystem.Tokens
{
- private readonly bool _value;
-
///
- ///
+ /// Represents an XSigDigitalToken
///
- ///
- ///
- public XSigDigitalToken(int index, bool value)
- : base(index)
+ public sealed class XSigDigitalToken : XSigToken
{
- // 12-bits available for digital encoded data
- if (index >= 4096 || index < 0)
- throw new ArgumentOutOfRangeException("index");
+ private readonly bool _value;
- _value = value;
- }
+ ///
+ ///
+ ///
+ ///
+ ///
+ public XSigDigitalToken(int index, bool value)
+ : base(index)
+ {
+ // 12-bits available for digital encoded data
+ if (index >= 4096 || index < 0)
+ throw new ArgumentOutOfRangeException("index");
- ///
- ///
- ///
- public bool Value
- {
- get { return _value; }
- }
+ _value = value;
+ }
- ///
- ///
- ///
- public override XSigTokenType TokenType
- {
- get { return XSigTokenType.Digital; }
- }
+ ///
+ ///
+ ///
+ public bool Value
+ {
+ get { return _value; }
+ }
- ///
- ///
- ///
- ///
- public override byte[] GetBytes()
- {
- return new[] {
- (byte)(0x80 | (Value ? 0 : 0x20) | ((Index - 1) >> 7)),
- (byte)((Index - 1) & 0x7F)
- };
- }
+ ///
+ ///
+ ///
+ public override XSigTokenType TokenType
+ {
+ get { return XSigTokenType.Digital; }
+ }
- ///
- ///
- ///
- ///
- ///
- public override XSigToken GetTokenWithOffset(int offset)
- {
- if (offset == 0) return this;
- return new XSigDigitalToken(Index + offset, Value);
- }
+ ///
+ ///
+ ///
+ ///
+ public override byte[] GetBytes()
+ {
+ return new[] {
+ (byte)(0x80 | (Value ? 0 : 0x20) | ((Index - 1) >> 7)),
+ (byte)((Index - 1) & 0x7F)
+ };
+ }
- ///
- ///
- ///
- ///
- public override string ToString()
- {
- return Index + " = " + (Value ? "High" : "Low");
- }
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// GetTokenWithOffset method
+ ///
+ public override XSigToken GetTokenWithOffset(int offset)
+ {
+ if (offset == 0) return this;
+ return new XSigDigitalToken(Index + offset, Value);
+ }
- ///
- ///
- ///
- ///
- ///
- public string ToString(IFormatProvider formatProvider)
- {
- return Value.ToString(formatProvider);
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// ToString method
+ ///
+ ///
+ public override string ToString()
+ {
+ return Index + " = " + (Value ? "High" : "Low");
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// ToString method
+ ///
+ public string ToString(IFormatProvider formatProvider)
+ {
+ return Value.ToString(formatProvider);
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/XSigUtility/Tokens/XSigSerialToken.cs b/src/PepperDash.Core/XSigUtility/Tokens/XSigSerialToken.cs
index 3b6a3f5f..635d40e3 100644
--- a/src/PepperDash.Core/XSigUtility/Tokens/XSigSerialToken.cs
+++ b/src/PepperDash.Core/XSigUtility/Tokens/XSigSerialToken.cs
@@ -1,80 +1,88 @@
using System;
using System.Text;
-namespace PepperDash.Core.Intersystem.Tokens;
-
-///
-/// Represents an XSigSerialToken
-///
-public sealed class XSigSerialToken : XSigToken
+namespace PepperDash.Core.Intersystem.Tokens
{
- private readonly string _value;
-
///
- /// Constructor
+ /// Represents an XSigSerialToken
///
- ///
- ///
- public XSigSerialToken(int index, string value)
- : base(index)
+ public sealed class XSigSerialToken : XSigToken
{
- // 10-bits available for serial encoded data
- if (index >= 1024 || index < 0)
- throw new ArgumentOutOfRangeException("index");
+ private readonly string _value;
- _value = value;
- }
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ public XSigSerialToken(int index, string value)
+ : base(index)
+ {
+ // 10-bits available for serial encoded data
+ if (index >= 1024 || index < 0)
+ throw new ArgumentOutOfRangeException("index");
- ///
- ///
- ///
- public string Value
- {
- get { return _value; }
- }
+ _value = value;
+ }
- ///
- ///
- ///
- public override XSigTokenType TokenType
- {
- get { return XSigTokenType.Serial; }
- }
+ ///
+ ///
+ ///
+ public string Value
+ {
+ get { return _value; }
+ }
- ///
- ///
- ///
- ///
- public override byte[] GetBytes()
- {
- var serialBytes = String.IsNullOrEmpty(Value) ? new byte[0] : Encoding.GetEncoding(28591).GetBytes(Value);
-
- var xsig = new byte[serialBytes.Length + 3];
- xsig[0] = (byte)(0xC8 | (Index - 1 >> 7));
- xsig[1] = (byte)((Index - 1) & 0x7F);
- xsig[xsig.Length - 1] = 0xFF;
+ ///
+ ///
+ ///
+ public override XSigTokenType TokenType
+ {
+ get { return XSigTokenType.Serial; }
+ }
- Buffer.BlockCopy(serialBytes, 0, xsig, 2, serialBytes.Length);
- return xsig;
- }
+ ///
+ ///
+ ///
+ ///
+ public override byte[] GetBytes()
+ {
+ var serialBytes = String.IsNullOrEmpty(Value) ? new byte[0] : Encoding.GetEncoding(28591).GetBytes(Value);
+
+ var xsig = new byte[serialBytes.Length + 3];
+ xsig[0] = (byte)(0xC8 | (Index - 1 >> 7));
+ xsig[1] = (byte)((Index - 1) & 0x7F);
+ xsig[xsig.Length - 1] = 0xFF;
- ///
- ///
- ///
- ///
- ///
- public override XSigToken GetTokenWithOffset(int offset)
- {
- if (offset == 0) return this;
- return new XSigSerialToken(Index + offset, Value);
- }
+ Buffer.BlockCopy(serialBytes, 0, xsig, 2, serialBytes.Length);
+ return xsig;
+ }
- ///
- ///
- ///
- ///
- public override string ToString()
- {
- return Index + " = \"" + Value + "\"";
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// GetTokenWithOffset method
+ ///
+ public override XSigToken GetTokenWithOffset(int offset)
+ {
+ if (offset == 0) return this;
+ return new XSigSerialToken(Index + offset, Value);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// ToString method
+ ///
+ ///
+ public override string ToString()
+ {
+ return Index + " = \"" + Value + "\"";
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/XSigUtility/Tokens/XSigToken.cs b/src/PepperDash.Core/XSigUtility/Tokens/XSigToken.cs
index cd706bd4..4c00a2ed 100644
--- a/src/PepperDash.Core/XSigUtility/Tokens/XSigToken.cs
+++ b/src/PepperDash.Core/XSigUtility/Tokens/XSigToken.cs
@@ -1,44 +1,45 @@
-namespace PepperDash.Core.Intersystem.Tokens;
-
-///
-/// Represents the base class for all XSig datatypes.
-///
-public abstract class XSigToken
+namespace PepperDash.Core.Intersystem.Tokens
{
- private readonly int _index;
-
///
- /// Constructs an XSigToken with the specified index.
+ /// Represents the base class for all XSig datatypes.
///
- /// Index for the data.
- protected XSigToken(int index)
+ public abstract class XSigToken
{
- _index = index;
+ private readonly int _index;
+
+ ///
+ /// Constructs an XSigToken with the specified index.
+ ///
+ /// Index for the data.
+ protected XSigToken(int index)
+ {
+ _index = index;
+ }
+
+ ///
+ /// XSig 1-based index.
+ ///
+ public int Index
+ {
+ get { return _index; }
+ }
+
+ ///
+ /// XSigToken type.
+ ///
+ public abstract XSigTokenType TokenType { get; }
+
+ ///
+ /// Generates the XSig bytes for the corresponding token.
+ ///
+ /// XSig byte array.
+ public abstract byte[] GetBytes();
+
+ ///
+ /// Returns a new token if necessary with an updated index based on the specified offset.
+ ///
+ /// Offset to adjust the index with.
+ /// XSigToken
+ public abstract XSigToken GetTokenWithOffset(int offset);
}
-
- ///
- /// XSig 1-based index.
- ///
- public int Index
- {
- get { return _index; }
- }
-
- ///
- /// XSigToken type.
- ///
- public abstract XSigTokenType TokenType { get; }
-
- ///
- /// Generates the XSig bytes for the corresponding token.
- ///
- /// XSig byte array.
- public abstract byte[] GetBytes();
-
- ///
- /// Returns a new token if necessary with an updated index based on the specified offset.
- ///
- /// Offset to adjust the index with.
- /// XSigToken
- public abstract XSigToken GetTokenWithOffset(int offset);
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/XSigUtility/Tokens/XSigTokenType.cs b/src/PepperDash.Core/XSigUtility/Tokens/XSigTokenType.cs
index 60641b4f..26d6c123 100644
--- a/src/PepperDash.Core/XSigUtility/Tokens/XSigTokenType.cs
+++ b/src/PepperDash.Core/XSigUtility/Tokens/XSigTokenType.cs
@@ -1,22 +1,23 @@
-namespace PepperDash.Core.Intersystem.Tokens;
-
-///
-/// XSig token types.
-///
-public enum XSigTokenType
+namespace PepperDash.Core.Intersystem.Tokens
{
///
- /// Digital signal datatype.
+ /// XSig token types.
///
- Digital,
+ public enum XSigTokenType
+ {
+ ///
+ /// Digital signal datatype.
+ ///
+ Digital,
- ///
- /// Analog signal datatype.
- ///
- Analog,
+ ///
+ /// Analog signal datatype.
+ ///
+ Analog,
- ///
- /// Serial signal datatype.
- ///
- Serial
+ ///
+ /// Serial signal datatype.
+ ///
+ Serial
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/XSigUtility/XSigHelpers.cs b/src/PepperDash.Core/XSigUtility/XSigHelpers.cs
index 4f970be2..d93cc32b 100644
--- a/src/PepperDash.Core/XSigUtility/XSigHelpers.cs
+++ b/src/PepperDash.Core/XSigUtility/XSigHelpers.cs
@@ -18,221 +18,264 @@ using PepperDash.Core.Intersystem.Tokens;
11111111 <- denotes end of data
*/
-namespace PepperDash.Core.Intersystem;
-
-///
-/// Helper methods for creating XSig byte sequences compatible with the Intersystem Communications (ISC) symbol.
-///
-///
-/// Indexing is not from the start of each signal type but rather from the beginning of the first defined signal
-/// the Intersystem Communications (ISC) symbol.
-///
-public static class XSigHelpers
+namespace PepperDash.Core.Intersystem
{
///
- /// Forces all outputs to 0.
+ /// Helper methods for creating XSig byte sequences compatible with the Intersystem Communications (ISC) symbol.
///
- /// Bytes in XSig format for clear outputs trigger.
- public static byte[] ClearOutputs()
+ ///
+ /// Indexing is not from the start of each signal type but rather from the beginning of the first defined signal
+ /// the Intersystem Communications (ISC) symbol.
+ ///
+ public static class XSigHelpers
{
- return new byte[] { 0xFC };
- }
-
- ///
- /// Evaluate all inputs and re-transmit any digital, analog, and permanent serail signals not set to 0.
- ///
- /// Bytes in XSig format for send status trigger.
- public static byte[] SendStatus()
- {
- return new byte[] { 0xFD };
- }
-
- ///
- /// Get bytes for an IXSigStateResolver object.
- ///
- /// XSig state resolver.
- /// Bytes in XSig format for each token within the state representation.
- public static byte[] GetBytes(IXSigSerialization xSigSerialization)
- {
- return GetBytes(xSigSerialization, 0);
- }
-
- ///
- /// Get bytes for an IXSigStateResolver object, with a specified offset.
- ///
- /// XSig state resolver.
- /// Offset to which the data will be aligned.
- /// Bytes in XSig format for each token within the state representation.
- public static byte[] GetBytes(IXSigSerialization xSigSerialization, int offset)
- {
- var tokens = xSigSerialization.Serialize();
- if (tokens == null) return new byte[0];
- using (var memoryStream = new MemoryStream())
+ ///
+ /// Forces all outputs to 0.
+ ///
+ /// Bytes in XSig format for clear outputs trigger.
+ public static byte[] ClearOutputs()
{
- using (var tokenWriter = new XSigTokenStreamWriter(memoryStream))
- tokenWriter.WriteXSigData(xSigSerialization, offset);
-
- return memoryStream.ToArray();
- }
- }
-
- ///
- /// Get bytes for a single digital signal.
- ///
- /// 1-based digital index
- /// Digital data to be encoded
- /// Bytes in XSig format for digtial information.
- public static byte[] GetBytes(int index, bool value)
- {
- return GetBytes(index, 0, value);
- }
-
- ///
- /// Get bytes for a single digital signal.
- ///
- /// 1-based digital index
- /// Index offset.
- /// Digital data to be encoded
- /// Bytes in XSig format for digtial information.
- public static byte[] GetBytes(int index, int offset, bool value)
- {
- return new XSigDigitalToken(index + offset, value).GetBytes();
- }
-
- ///
- /// Get byte sequence for multiple digital signals.
- ///
- /// Starting index of the sequence.
- /// Digital signal value array.
- /// Byte sequence in XSig format for digital signal information.
- public static byte[] GetBytes(int startIndex, bool[] values)
- {
- return GetBytes(startIndex, 0, values);
- }
-
- ///
- /// Get byte sequence for multiple digital signals.
- ///
- /// Starting index of the sequence.
- /// Index offset.
- /// Digital signal value array.
- /// Byte sequence in XSig format for digital signal information.
- public static byte[] GetBytes(int startIndex, int offset, bool[] values)
- {
- // Digital XSig data is 2 bytes per value
- const int fixedLength = 2;
- var bytes = new byte[values.Length * fixedLength];
- for (var i = 0; i < values.Length; i++)
- Buffer.BlockCopy(GetBytes(startIndex++, offset, values[i]), 0, bytes, i * fixedLength, fixedLength);
-
- return bytes;
- }
-
- ///
- /// Get bytes for a single analog signal.
- ///
- /// 1-based analog index
- /// Analog data to be encoded
- /// Bytes in XSig format for analog signal information.
- public static byte[] GetBytes(int index, ushort value)
- {
- return GetBytes(index, 0, value);
- }
-
- ///
- /// Get bytes for a single analog signal.
- ///
- /// 1-based analog index
- /// Index offset.
- /// Analog data to be encoded
- /// Bytes in XSig format for analog signal information.
- public static byte[] GetBytes(int index, int offset, ushort value)
- {
- return new XSigAnalogToken(index + offset, value).GetBytes();
- }
-
- ///
- /// Get byte sequence for multiple analog signals.
- ///
- /// Starting index of the sequence.
- /// Analog signal value array.
- /// Byte sequence in XSig format for analog signal information.
- public static byte[] GetBytes(int startIndex, ushort[] values)
- {
- return GetBytes(startIndex, 0, values);
- }
-
- ///
- /// Get byte sequence for multiple analog signals.
- ///
- /// Starting index of the sequence.
- /// Index offset.
- /// Analog signal value array.
- /// Byte sequence in XSig format for analog signal information.
- public static byte[] GetBytes(int startIndex, int offset, ushort[] values)
- {
- // Analog XSig data is 4 bytes per value
- const int fixedLength = 4;
- var bytes = new byte[values.Length * fixedLength];
- for (var i = 0; i < values.Length; i++)
- Buffer.BlockCopy(GetBytes(startIndex++, offset, values[i]), 0, bytes, i * fixedLength, fixedLength);
-
- return bytes;
- }
-
- ///
- /// Get bytes for a single serial signal.
- ///
- /// 1-based serial index
- /// Serial data to be encoded
- /// Bytes in XSig format for serial signal information.
- public static byte[] GetBytes(int index, string value)
- {
- return GetBytes(index, 0, value);
- }
-
- ///
- /// Get bytes for a single serial signal.
- ///
- /// 1-based serial index
- /// Index offset.
- /// Serial data to be encoded
- /// Bytes in XSig format for serial signal information.
- public static byte[] GetBytes(int index, int offset, string value)
- {
- return new XSigSerialToken(index + offset, value).GetBytes();
- }
-
- ///
- /// Get byte sequence for multiple serial signals.
- ///
- /// Starting index of the sequence.
- /// Serial signal value array.
- /// Byte sequence in XSig format for serial signal information.
- public static byte[] GetBytes(int startIndex, string[] values)
- {
- return GetBytes(startIndex, 0, values);
- }
-
- ///
- /// Get byte sequence for multiple serial signals.
- ///
- /// Starting index of the sequence.
- /// Index offset.
- /// Serial signal value array.
- /// Byte sequence in XSig format for serial signal information.
- public static byte[] GetBytes(int startIndex, int offset, string[] values)
- {
- // Serial XSig data is not fixed-length like the other formats
- var dstOffset = 0;
- var bytes = new byte[values.Sum(v => v.Length + 3)];
- for (var i = 0; i < values.Length; i++)
- {
- var data = GetBytes(startIndex++, offset, values[i]);
- Buffer.BlockCopy(data, 0, bytes, dstOffset, data.Length);
- dstOffset += data.Length;
+ return new byte[] { 0xFC };
}
- return bytes;
+ ///
+ /// Evaluate all inputs and re-transmit any digital, analog, and permanent serail signals not set to 0.
+ ///
+ /// Bytes in XSig format for send status trigger.
+ public static byte[] SendStatus()
+ {
+ return new byte[] { 0xFD };
+ }
+
+ ///
+ /// Get bytes for an IXSigStateResolver object.
+ ///
+ /// XSig state resolver.
+ /// Bytes in XSig format for each token within the state representation.
+ ///
+ /// GetBytes method
+ ///
+ public static byte[] GetBytes(IXSigSerialization xSigSerialization)
+ {
+ return GetBytes(xSigSerialization, 0);
+ }
+
+ ///
+ /// Get bytes for an IXSigStateResolver object, with a specified offset.
+ ///
+ /// XSig state resolver.
+ /// Offset to which the data will be aligned.
+ /// Bytes in XSig format for each token within the state representation.
+ ///
+ /// GetBytes method
+ ///
+ public static byte[] GetBytes(IXSigSerialization xSigSerialization, int offset)
+ {
+ var tokens = xSigSerialization.Serialize();
+ if (tokens == null) return new byte[0];
+ using (var memoryStream = new MemoryStream())
+ {
+ using (var tokenWriter = new XSigTokenStreamWriter(memoryStream))
+ tokenWriter.WriteXSigData(xSigSerialization, offset);
+
+ return memoryStream.ToArray();
+ }
+ }
+
+ ///
+ /// Get bytes for a single digital signal.
+ ///
+ /// 1-based digital index
+ /// Digital data to be encoded
+ /// Bytes in XSig format for digtial information.
+ ///
+ /// GetBytes method
+ ///
+ public static byte[] GetBytes(int index, bool value)
+ {
+ return GetBytes(index, 0, value);
+ }
+
+ ///
+ /// Get bytes for a single digital signal.
+ ///
+ /// 1-based digital index
+ /// Index offset.
+ /// Digital data to be encoded
+ /// Bytes in XSig format for digtial information.
+ ///
+ /// GetBytes method
+ ///
+ public static byte[] GetBytes(int index, int offset, bool value)
+ {
+ return new XSigDigitalToken(index + offset, value).GetBytes();
+ }
+
+ ///
+ /// Get byte sequence for multiple digital signals.
+ ///
+ /// Starting index of the sequence.
+ /// Digital signal value array.
+ /// Byte sequence in XSig format for digital signal information.
+ ///
+ /// GetBytes method
+ ///
+ public static byte[] GetBytes(int startIndex, bool[] values)
+ {
+ return GetBytes(startIndex, 0, values);
+ }
+
+ ///
+ /// Get byte sequence for multiple digital signals.
+ ///
+ /// Starting index of the sequence.
+ /// Index offset.
+ /// Digital signal value array.
+ /// Byte sequence in XSig format for digital signal information.
+ ///
+ /// GetBytes method
+ ///
+ public static byte[] GetBytes(int startIndex, int offset, bool[] values)
+ {
+ // Digital XSig data is 2 bytes per value
+ const int fixedLength = 2;
+ var bytes = new byte[values.Length * fixedLength];
+ for (var i = 0; i < values.Length; i++)
+ Buffer.BlockCopy(GetBytes(startIndex++, offset, values[i]), 0, bytes, i * fixedLength, fixedLength);
+
+ return bytes;
+ }
+
+ ///
+ /// Get bytes for a single analog signal.
+ ///
+ /// 1-based analog index
+ /// Analog data to be encoded
+ /// Bytes in XSig format for analog signal information.
+ ///
+ /// GetBytes method
+ ///
+ public static byte[] GetBytes(int index, ushort value)
+ {
+ return GetBytes(index, 0, value);
+ }
+
+ ///
+ /// Get bytes for a single analog signal.
+ ///
+ /// 1-based analog index
+ /// Index offset.
+ /// Analog data to be encoded
+ /// Bytes in XSig format for analog signal information.
+ ///
+ /// GetBytes method
+ ///
+ public static byte[] GetBytes(int index, int offset, ushort value)
+ {
+ return new XSigAnalogToken(index + offset, value).GetBytes();
+ }
+
+ ///
+ /// Get byte sequence for multiple analog signals.
+ ///
+ /// Starting index of the sequence.
+ /// Analog signal value array.
+ /// Byte sequence in XSig format for analog signal information.
+ ///
+ /// GetBytes method
+ ///
+ public static byte[] GetBytes(int startIndex, ushort[] values)
+ {
+ return GetBytes(startIndex, 0, values);
+ }
+
+ ///
+ /// Get byte sequence for multiple analog signals.
+ ///
+ /// Starting index of the sequence.
+ /// Index offset.
+ /// Analog signal value array.
+ /// Byte sequence in XSig format for analog signal information.
+ ///
+ /// GetBytes method
+ ///
+ public static byte[] GetBytes(int startIndex, int offset, ushort[] values)
+ {
+ // Analog XSig data is 4 bytes per value
+ const int fixedLength = 4;
+ var bytes = new byte[values.Length * fixedLength];
+ for (var i = 0; i < values.Length; i++)
+ Buffer.BlockCopy(GetBytes(startIndex++, offset, values[i]), 0, bytes, i * fixedLength, fixedLength);
+
+ return bytes;
+ }
+
+ ///
+ /// Get bytes for a single serial signal.
+ ///
+ /// 1-based serial index
+ /// Serial data to be encoded
+ /// Bytes in XSig format for serial signal information.
+ ///
+ /// GetBytes method
+ ///
+ public static byte[] GetBytes(int index, string value)
+ {
+ return GetBytes(index, 0, value);
+ }
+
+ ///
+ /// Get bytes for a single serial signal.
+ ///
+ /// 1-based serial index
+ /// Index offset.
+ /// Serial data to be encoded
+ /// Bytes in XSig format for serial signal information.
+ ///
+ /// GetBytes method
+ ///
+ public static byte[] GetBytes(int index, int offset, string value)
+ {
+ return new XSigSerialToken(index + offset, value).GetBytes();
+ }
+
+ ///
+ /// Get byte sequence for multiple serial signals.
+ ///
+ /// Starting index of the sequence.
+ /// Serial signal value array.
+ /// Byte sequence in XSig format for serial signal information.
+ ///
+ /// GetBytes method
+ ///
+ public static byte[] GetBytes(int startIndex, string[] values)
+ {
+ return GetBytes(startIndex, 0, values);
+ }
+
+ ///
+ /// Get byte sequence for multiple serial signals.
+ ///
+ /// Starting index of the sequence.
+ /// Index offset.
+ /// Serial signal value array.
+ /// Byte sequence in XSig format for serial signal information.
+ ///
+ /// GetBytes method
+ ///
+ public static byte[] GetBytes(int startIndex, int offset, string[] values)
+ {
+ // Serial XSig data is not fixed-length like the other formats
+ var dstOffset = 0;
+ var bytes = new byte[values.Sum(v => v.Length + 3)];
+ for (var i = 0; i < values.Length; i++)
+ {
+ var data = GetBytes(startIndex++, offset, values[i]);
+ Buffer.BlockCopy(data, 0, bytes, dstOffset, data.Length);
+ dstOffset += data.Length;
+ }
+
+ return bytes;
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/XSigUtility/XSigTokenStreamReader.cs b/src/PepperDash.Core/XSigUtility/XSigTokenStreamReader.cs
index 3c222960..03e79946 100644
--- a/src/PepperDash.Core/XSigUtility/XSigTokenStreamReader.cs
+++ b/src/PepperDash.Core/XSigUtility/XSigTokenStreamReader.cs
@@ -4,143 +4,153 @@ using Crestron.SimplSharp.CrestronIO;
using PepperDash.Core.Intersystem.Serialization;
using PepperDash.Core.Intersystem.Tokens;
-namespace PepperDash.Core.Intersystem;
-
-///
-/// XSigToken stream reader.
-///
-public sealed class XSigTokenStreamReader : IDisposable
+namespace PepperDash.Core.Intersystem
{
- private readonly Stream _stream;
- private readonly bool _leaveOpen;
-
- ///
///
- /// XSigToken stream reader constructor.
+ /// XSigToken stream reader.
///
- /// Input stream to read from.
- /// Stream is null.
- /// Stream cannot be read from.
- public XSigTokenStreamReader(Stream stream)
- : this(stream, false) { }
-
- ///
- /// XSigToken stream reader constructor.
- ///
- /// Input stream to read from.
- /// Determines whether to leave the stream open or not.
- /// Stream is null.
- /// Stream cannot be read from.
- public XSigTokenStreamReader(Stream stream, bool leaveOpen)
+ public sealed class XSigTokenStreamReader : IDisposable
{
- if (stream == null)
- throw new ArgumentNullException("stream");
- if (!stream.CanRead)
- throw new ArgumentException("The specified stream cannot be read from.");
+ private readonly Stream _stream;
+ private readonly bool _leaveOpen;
- _stream = stream;
- _leaveOpen = leaveOpen;
- }
+ ///
+ ///
+ /// XSigToken stream reader constructor.
+ ///
+ /// Input stream to read from.
+ /// Stream is null.
+ /// Stream cannot be read from.
+ public XSigTokenStreamReader(Stream stream)
+ : this(stream, false) { }
- ///
- /// Reads a 16-bit unsigned integer from the specified stream using Big Endian byte order.
- ///
- /// Input stream
- /// Result
- /// True if successful, otherwise false.
- public static bool TryReadUInt16BE(Stream stream, out ushort value)
- {
- value = 0;
- if (stream.Length < 2)
- return false;
-
- var buffer = new byte[2];
- stream.Read(buffer, 0, 2);
- value = (ushort)((buffer[0] << 8) | buffer[1]);
- return true;
- }
-
- ///
- /// Read XSig token from the stream.
- ///
- /// XSigToken
- /// Offset is less than 0.
- public XSigToken ReadXSigToken()
- {
- ushort prefix;
- if (!TryReadUInt16BE(_stream, out prefix))
- return null;
-
- if ((prefix & 0xF880) == 0xC800) // Serial data
+ ///
+ /// XSigToken stream reader constructor.
+ ///
+ /// Input stream to read from.
+ /// Determines whether to leave the stream open or not.
+ /// Stream is null.
+ /// Stream cannot be read from.
+ public XSigTokenStreamReader(Stream stream, bool leaveOpen)
{
- var index = ((prefix & 0x0700) >> 1) | (prefix & 0x7F);
- var n = 0;
- const int maxSerialDataLength = 252;
- var chars = new char[maxSerialDataLength];
- int ch;
- while ((ch = _stream.ReadByte()) != 0xFF)
- {
- if (ch == -1) // Reached end of stream without end of data marker
- return null;
-
- chars[n++] = (char)ch;
- }
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+ if (!stream.CanRead)
+ throw new ArgumentException("The specified stream cannot be read from.");
- return new XSigSerialToken((ushort)(index + 1), new string(chars, 0, n));
+ _stream = stream;
+ _leaveOpen = leaveOpen;
}
- if ((prefix & 0xC880) == 0xC000) // Analog data
+ ///
+ /// Reads a 16-bit unsigned integer from the specified stream using Big Endian byte order.
+ ///
+ /// Input stream
+ /// Result
+ /// True if successful, otherwise false.
+ ///
+ /// TryReadUInt16BE method
+ ///
+ public static bool TryReadUInt16BE(Stream stream, out ushort value)
{
- ushort data;
- if (!TryReadUInt16BE(_stream, out data))
+ value = 0;
+ if (stream.Length < 2)
+ return false;
+
+ var buffer = new byte[2];
+ stream.Read(buffer, 0, 2);
+ value = (ushort)((buffer[0] << 8) | buffer[1]);
+ return true;
+ }
+
+ ///
+ /// Read XSig token from the stream.
+ ///
+ /// XSigToken
+ /// Offset is less than 0.
+ ///
+ /// ReadXSigToken method
+ ///
+ public XSigToken ReadXSigToken()
+ {
+ ushort prefix;
+ if (!TryReadUInt16BE(_stream, out prefix))
return null;
- var index = ((prefix & 0x0700) >> 1) | (prefix & 0x7F);
- var value = ((prefix & 0x3000) << 2) | ((data & 0x7F00) >> 1) | (data & 0x7F);
- return new XSigAnalogToken((ushort)(index + 1), (ushort)value);
+ if ((prefix & 0xF880) == 0xC800) // Serial data
+ {
+ var index = ((prefix & 0x0700) >> 1) | (prefix & 0x7F);
+ var n = 0;
+ const int maxSerialDataLength = 252;
+ var chars = new char[maxSerialDataLength];
+ int ch;
+ while ((ch = _stream.ReadByte()) != 0xFF)
+ {
+ if (ch == -1) // Reached end of stream without end of data marker
+ return null;
+
+ chars[n++] = (char)ch;
+ }
+
+ return new XSigSerialToken((ushort)(index + 1), new string(chars, 0, n));
+ }
+
+ if ((prefix & 0xC880) == 0xC000) // Analog data
+ {
+ ushort data;
+ if (!TryReadUInt16BE(_stream, out data))
+ return null;
+
+ var index = ((prefix & 0x0700) >> 1) | (prefix & 0x7F);
+ var value = ((prefix & 0x3000) << 2) | ((data & 0x7F00) >> 1) | (data & 0x7F);
+ return new XSigAnalogToken((ushort)(index + 1), (ushort)value);
+ }
+
+ if ((prefix & 0xC080) == 0x8000) // Digital data
+ {
+ var index = ((prefix & 0x1F00) >> 1) | (prefix & 0x7F);
+ var value = (prefix & 0x2000) == 0;
+ return new XSigDigitalToken((ushort)(index + 1), value);
+ }
+
+ return null;
}
- if ((prefix & 0xC080) == 0x8000) // Digital data
+ ///
+ /// Reads all available XSig tokens from the stream.
+ ///
+ /// XSigToken collection.
+ ///
+ /// ReadAllXSigTokens method
+ ///
+ public IEnumerable ReadAllXSigTokens()
{
- var index = ((prefix & 0x1F00) >> 1) | (prefix & 0x7F);
- var value = (prefix & 0x2000) == 0;
- return new XSigDigitalToken((ushort)(index + 1), value);
+ var tokens = new List();
+ XSigToken token;
+ while ((token = ReadXSigToken()) != null)
+ tokens.Add(token);
+
+ return tokens;
}
- return null;
- }
+ ///
+ /// Attempts to deserialize all XSig data within the stream from the current position.
+ ///
+ /// Type to deserialize the information to.
+ /// Deserialized object.
+ public T DeserializeStream()
+ where T : class, IXSigSerialization, new()
+ {
+ return new T().Deserialize(ReadAllXSigTokens());
+ }
- ///
- /// Reads all available XSig tokens from the stream.
- ///
- /// XSigToken collection.
- public IEnumerable ReadAllXSigTokens()
- {
- var tokens = new List();
- XSigToken token;
- while ((token = ReadXSigToken()) != null)
- tokens.Add(token);
-
- return tokens;
- }
-
- ///
- /// Attempts to deserialize all XSig data within the stream from the current position.
- ///
- /// Type to deserialize the information to.
- /// Deserialized object.
- public T DeserializeStream()
- where T : class, IXSigSerialization, new()
- {
- return new T().Deserialize(ReadAllXSigTokens());
- }
-
- ///
- /// Disposes of the internal stream if specified to not leave open.
- ///
- public void Dispose()
- {
- if (!_leaveOpen)
- _stream.Dispose();
+ ///
+ /// Disposes of the internal stream if specified to not leave open.
+ ///
+ public void Dispose()
+ {
+ if (!_leaveOpen)
+ _stream.Dispose();
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/XSigUtility/XSigTokenStreamWriter.cs b/src/PepperDash.Core/XSigUtility/XSigTokenStreamWriter.cs
index da973acd..12eeaf91 100644
--- a/src/PepperDash.Core/XSigUtility/XSigTokenStreamWriter.cs
+++ b/src/PepperDash.Core/XSigUtility/XSigTokenStreamWriter.cs
@@ -5,131 +5,147 @@ using Crestron.SimplSharp.CrestronIO;
using PepperDash.Core.Intersystem.Serialization;
using PepperDash.Core.Intersystem.Tokens;
-namespace PepperDash.Core.Intersystem;
-
-///
-/// XSigToken stream writer.
-///
-public sealed class XSigTokenStreamWriter : IDisposable
+namespace PepperDash.Core.Intersystem
{
- private readonly Stream _stream;
- private readonly bool _leaveOpen;
-
- ///
///
- /// XSigToken stream writer constructor.
+ /// XSigToken stream writer.
///
- /// Input stream to write to.
- /// Stream is null.
- /// Stream cannot be written to.
- public XSigTokenStreamWriter(Stream stream)
- : this(stream, false) { }
-
- ///
- /// XSigToken stream writer constructor.
- ///
- /// Input stream to write to.
- /// Determines whether to leave the stream open or not.
- /// Stream is null.
- /// Stream cannot be written to.
- public XSigTokenStreamWriter(Stream stream, bool leaveOpen)
+ public sealed class XSigTokenStreamWriter : IDisposable
{
- if (stream == null)
- throw new ArgumentNullException("stream");
- if (!stream.CanWrite)
- throw new ArgumentException("The specified stream cannot be written to.");
+ private readonly Stream _stream;
+ private readonly bool _leaveOpen;
- _stream = stream;
- _leaveOpen = leaveOpen;
- }
+ ///
+ ///
+ /// XSigToken stream writer constructor.
+ ///
+ /// Input stream to write to.
+ /// Stream is null.
+ /// Stream cannot be written to.
+ public XSigTokenStreamWriter(Stream stream)
+ : this(stream, false) { }
- ///
- /// Write XSig data gathered from an IXSigStateResolver to the stream.
- ///
- /// IXSigStateResolver object.
- public void WriteXSigData(IXSigSerialization xSigSerialization)
- {
- WriteXSigData(xSigSerialization, 0);
- }
-
- ///
- /// Write XSig data gathered from an IXSigStateResolver to the stream.
- ///
- /// IXSigStateResolver object.
- /// Index offset for each XSigToken.
- public void WriteXSigData(IXSigSerialization xSigSerialization, int offset)
- {
- if (xSigSerialization == null)
- throw new ArgumentNullException("xSigSerialization");
-
- var tokens = xSigSerialization.Serialize();
- WriteXSigData(tokens, offset);
- }
-
- ///
- /// Write XSigToken to the stream.
- ///
- /// XSigToken object.
- public void WriteXSigData(XSigToken token)
- {
- WriteXSigData(token, 0);
- }
-
- ///
- /// Write XSigToken to the stream.
- ///
- /// XSigToken object.
- /// Index offset for each XSigToken.
- public void WriteXSigData(XSigToken token, int offset)
- {
- WriteXSigData(new[] { token }, offset);
- }
-
- ///
- /// Writes an array of XSigTokens to the stream.
- ///
- /// XSigToken objects.
- public void WriteXSigData(XSigToken[] tokens)
- {
- WriteXSigData(tokens.AsEnumerable());
- }
-
- ///
- /// Write an enumerable collection of XSigTokens to the stream.
- ///
- /// XSigToken objects.
- public void WriteXSigData(IEnumerable tokens)
- {
- WriteXSigData(tokens, 0);
- }
-
- ///
- /// Write an enumerable collection of XSigTokens to the stream.
- ///
- /// XSigToken objects.
- /// Index offset for each XSigToken.
- public void WriteXSigData(IEnumerable tokens, int offset)
- {
- if (offset < 0)
- throw new ArgumentOutOfRangeException("offset", "Offset must be greater than or equal to 0.");
-
- if (tokens != null)
+ ///
+ /// XSigToken stream writer constructor.
+ ///
+ /// Input stream to write to.
+ /// Determines whether to leave the stream open or not.
+ /// Stream is null.
+ /// Stream cannot be written to.
+ public XSigTokenStreamWriter(Stream stream, bool leaveOpen)
{
- foreach (var token in tokens)
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+ if (!stream.CanWrite)
+ throw new ArgumentException("The specified stream cannot be written to.");
+
+ _stream = stream;
+ _leaveOpen = leaveOpen;
+ }
+
+ ///
+ /// Write XSig data gathered from an IXSigStateResolver to the stream.
+ ///
+ /// IXSigStateResolver object.
+ ///
+ /// WriteXSigData method
+ ///
+ public void WriteXSigData(IXSigSerialization xSigSerialization)
+ {
+ WriteXSigData(xSigSerialization, 0);
+ }
+
+ ///
+ /// Write XSig data gathered from an IXSigStateResolver to the stream.
+ ///
+ /// IXSigStateResolver object.
+ /// Index offset for each XSigToken.
+ ///
+ /// WriteXSigData method
+ ///
+ public void WriteXSigData(IXSigSerialization xSigSerialization, int offset)
+ {
+ if (xSigSerialization == null)
+ throw new ArgumentNullException("xSigSerialization");
+
+ var tokens = xSigSerialization.Serialize();
+ WriteXSigData(tokens, offset);
+ }
+
+ ///
+ /// Write XSigToken to the stream.
+ ///
+ /// XSigToken object.
+ ///
+ /// WriteXSigData method
+ ///
+ public void WriteXSigData(XSigToken token)
+ {
+ WriteXSigData(token, 0);
+ }
+
+ ///
+ /// Write XSigToken to the stream.
+ ///
+ /// XSigToken object.
+ /// Index offset for each XSigToken.
+ ///
+ /// WriteXSigData method
+ ///
+ public void WriteXSigData(XSigToken token, int offset)
+ {
+ WriteXSigData(new[] { token }, offset);
+ }
+
+ ///
+ /// Writes an array of XSigTokens to the stream.
+ ///
+ /// XSigToken objects.
+ public void WriteXSigData(XSigToken[] tokens)
+ {
+ WriteXSigData(tokens.AsEnumerable());
+ }
+
+ ///
+ /// Write an enumerable collection of XSigTokens to the stream.
+ ///
+ /// XSigToken objects.
+ public void WriteXSigData(IEnumerable tokens)
+ {
+ WriteXSigData(tokens, 0);
+ }
+
+ ///
+ /// Write an enumerable collection of XSigTokens to the stream.
+ ///
+ /// XSigToken objects.
+ /// Index offset for each XSigToken.
+ ///
+ /// WriteXSigData method
+ ///
+ public void WriteXSigData(IEnumerable tokens, int offset)
+ {
+ if (offset < 0)
+ throw new ArgumentOutOfRangeException("offset", "Offset must be greater than or equal to 0.");
+
+ if (tokens != null)
{
- if (token == null) continue;
- var bytes = token.GetTokenWithOffset(offset).GetBytes();
- _stream.Write(bytes, 0, bytes.Length);
+ foreach (var token in tokens)
+ {
+ if (token == null) continue;
+ var bytes = token.GetTokenWithOffset(offset).GetBytes();
+ _stream.Write(bytes, 0, bytes.Length);
+ }
}
}
- }
- ///
- /// Disposes of the internal stream if specified to not leave open.
- ///
- public void Dispose()
- {
- if (!_leaveOpen)
- _stream.Dispose();
+ ///
+ /// Dispose method
+ ///
+ public void Dispose()
+ {
+ if (!_leaveOpen)
+ _stream.Dispose();
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core.Tests/Config/ConfigServiceFakesTests.cs b/src/PepperDash.Essentials.Core.Tests/Config/ConfigServiceFakesTests.cs
deleted file mode 100644
index 1359a450..00000000
--- a/src/PepperDash.Essentials.Core.Tests/Config/ConfigServiceFakesTests.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-using FluentAssertions;
-using PepperDash.Core.Abstractions;
-using PepperDash.Core.Tests.Fakes;
-using Xunit;
-
-namespace PepperDash.Essentials.Core.Tests.Config;
-
-///
-/// Tests for the configuration loading abstractions.
-/// These verify behaviour of the test fakes and interfaces independently of
-/// any Crestron SDK types (ConfigReader itself will be tested here once it
-/// is migrated from Crestron.SimplSharp.CrestronIO to System.IO — see plan Phase 4).
-///
-public class ConfigServiceFakesTests
-{
- [Fact]
- public void DataStore_MultipleKeys_AreStoredIndependently()
- {
- var store = new InMemoryCrestronDataStore();
- store.InitStore();
-
- store.SetLocalInt("KeyA", 1);
- store.SetLocalInt("KeyB", 2);
-
- store.TryGetLocalInt("KeyA", out var a);
- store.TryGetLocalInt("KeyB", out var b);
-
- a.Should().Be(1);
- b.Should().Be(2);
- }
-
- [Fact]
- public void DataStore_OverwriteKey_ReturnsNewValue()
- {
- var store = new InMemoryCrestronDataStore();
- store.SetLocalInt("Level", 1);
- store.SetLocalInt("Level", 5);
-
- store.TryGetLocalInt("Level", out var level);
- level.Should().Be(5);
- }
-
- [Fact]
- public void FakeEnvironment_CanBeConfiguredForServer()
- {
- var env = new FakeCrestronEnvironment
- {
- DevicePlatform = DevicePlatform.Server,
- ApplicationNumber = 1,
- RoomId = 42,
- };
-
- env.DevicePlatform.Should().Be(DevicePlatform.Server);
- env.RoomId.Should().Be(42u);
- }
-
- [Fact]
- public void FakeEthernetHelper_SeedAndRetrieve()
- {
- var eth = new FakeEthernetHelper()
- .Seed(EthernetParameterType.GetCurrentIpAddress, "192.168.1.100")
- .Seed(EthernetParameterType.GetHostname, "MC4-TEST");
-
- eth.GetEthernetParameter(EthernetParameterType.GetCurrentIpAddress, 0)
- .Should().Be("192.168.1.100");
-
- eth.GetEthernetParameter(EthernetParameterType.GetHostname, 0)
- .Should().Be("MC4-TEST");
-
- eth.GetEthernetParameter(EthernetParameterType.GetDomainName, 0)
- .Should().BeEmpty("unseeded parameter should return empty string");
- }
-}
diff --git a/src/PepperDash.Essentials.Core.Tests/PepperDash.Essentials.Core.Tests.csproj b/src/PepperDash.Essentials.Core.Tests/PepperDash.Essentials.Core.Tests.csproj
deleted file mode 100644
index 62582959..00000000
--- a/src/PepperDash.Essentials.Core.Tests/PepperDash.Essentials.Core.Tests.csproj
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
- net9.0
- enable
- enable
- false
- false
- false
-
-
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs b/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs
index 6adf7d37..66e3ccd8 100644
--- a/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/BridgeBase.cs
@@ -13,408 +13,518 @@ using PepperDash.Essentials.Core.Config;
using Serilog.Events;
-namespace PepperDash.Essentials.Core.Bridges;
-
-///
-/// Base class for bridge API variants
-///
-public abstract class BridgeApi : EssentialsDevice
+namespace PepperDash.Essentials.Core.Bridges
{
- protected BridgeApi(string key) :
- base(key)
+ ///
+ /// Base class for bridge API variants
+ ///
+ [Obsolete("Will be removed in v3.0.0")]
+ public abstract class BridgeApi : EssentialsDevice
{
-
- }
-}
-
-///
-/// Bridge API using EISC
-///
-public class EiscApiAdvanced : BridgeApi, ICommunicationMonitor
-{
- public EiscApiPropertiesConfig PropertiesConfig { get; private set; }
-
- public Dictionary JoinMaps { get; private set; }
-
- public BasicTriList Eisc { get; private set; }
-
- public EiscApiAdvanced(DeviceConfig dc, BasicTriList eisc) :
- base(dc.Key)
- {
- JoinMaps = new Dictionary();
-
- PropertiesConfig = dc.Properties.ToObject();
- //PropertiesConfig = JsonConvert.DeserializeObject(dc.Properties.ToString());
-
- Eisc = eisc;
-
- Eisc.SigChange += Eisc_SigChange;
-
- CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, Eisc, 120000, 300000);
-
- AddPostActivationAction(LinkDevices);
- AddPostActivationAction(LinkRooms);
- AddPostActivationAction(RegisterEisc);
- }
-
- protected override bool CustomActivate()
- {
- CommunicationMonitor.Start();
- return base.CustomActivate();
- }
-
- public override bool Deactivate()
- {
- CommunicationMonitor.Stop();
- return base.Deactivate();
- }
-
- private void LinkDevices()
- {
- Debug.LogMessage(LogEventLevel.Debug, this, "Linking Devices...");
-
- if (PropertiesConfig.Devices == null)
+ ///
+ /// Constructor
+ ///
+ /// Device key
+ protected BridgeApi(string key) :
+ base(key)
{
- Debug.LogMessage(LogEventLevel.Debug, this, "No devices linked to this bridge");
- return;
+
}
- foreach (var d in PropertiesConfig.Devices)
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ protected BridgeApi(string key, string name) :
+ base(key, name)
{
- var device = DeviceManager.GetDeviceForKey(d.DeviceKey);
- if (device == null)
+ }
+ }
+
+ ///
+ /// Class to link devices and rooms to an EISC Instance
+ ///
+ public class EiscApiAdvanced : BridgeApi, ICommunicationMonitor
+ {
+ ///
+ /// Gets the PropertiesConfig
+ ///
+ public EiscApiPropertiesConfig PropertiesConfig { get; private set; }
+
+ ///
+ /// Gets the JoinMaps dictionary
+ ///
+ public Dictionary JoinMaps { get; private set; }
+
+ ///
+ /// Gets the EISC instance
+ ///
+ public BasicTriList Eisc { get; private set; }
+
+ ///
+ /// Constructor
+ ///
+ /// Device configuration
+ /// EISC instance
+ public EiscApiAdvanced(DeviceConfig dc, BasicTriList eisc) :
+ base(dc.Key, dc.Name)
+ {
+ JoinMaps = new Dictionary();
+
+ PropertiesConfig = dc.Properties.ToObject();
+
+ Eisc = eisc;
+
+ Eisc.SigChange += Eisc_SigChange;
+
+ CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, Eisc, 120000, 300000);
+
+ AddPostActivationAction(LinkDevices);
+ AddPostActivationAction(LinkRooms);
+ AddPostActivationAction(RegisterEisc);
+ }
+
+ ///
+ /// CustomActivate method
+ ///
+ public override bool CustomActivate()
+ {
+ CommunicationMonitor.Start();
+ return base.CustomActivate();
+ }
+
+ ///
+ /// Deactivate method
+ ///
+ public override bool Deactivate()
+ {
+ CommunicationMonitor.Stop();
+ return base.Deactivate();
+ }
+
+ private void LinkDevices()
+ {
+ Debug.LogMessage(LogEventLevel.Debug, this, "Linking Devices...");
+
+ if (PropertiesConfig.Devices == null)
{
- continue;
+ this.LogDebug("No devices linked to this bridge");
+ return;
}
- Debug.LogMessage(LogEventLevel.Debug, this, "Linking Device: '{0}'", device.Key);
-
- if (device is IBridgeAdvanced bridge)
+ foreach (var d in PropertiesConfig.Devices)
{
- bridge.LinkToApi(Eisc, d.JoinStart, d.JoinMapKey, this);
- continue;
+ var device = DeviceManager.GetDeviceForKey(d.DeviceKey);
+
+ if (device == null)
+ {
+ continue;
+ }
+
+ Debug.LogMessage(LogEventLevel.Debug, this, "Linking Device: '{0}'", device.Key);
+
+ if (device is IBridgeAdvanced bridge)
+ {
+ bridge.LinkToApi(Eisc, d.JoinStart, d.JoinMapKey, this);
+ continue;
+ }
+
+ this.LogWarning("{deviceKey} is not compatible with this bridge type. Please update the device.", device.Key);
+ }
+ }
+
+ private void RegisterEisc()
+ {
+ if (Eisc.Registered)
+ {
+ return;
}
- Debug.LogMessage(LogEventLevel.Information, this,
- "{0} is not compatible with this bridge type. Please use 'eiscapi' instead, or updae the device.",
- device.Key);
- }
- }
+ var registerResult = Eisc.Register();
- private void RegisterEisc()
- {
- if (Eisc.Registered)
- {
- return;
- }
-
- var registerResult = Eisc.Register();
-
- if (registerResult != eDeviceRegistrationUnRegistrationResponse.Success)
- {
- Debug.LogMessage(LogEventLevel.Verbose, this, "Registration result: {0}", registerResult);
- return;
- }
-
- Debug.LogMessage(LogEventLevel.Debug, this, "EISC registration successful");
- }
-
- public void LinkRooms()
- {
- Debug.LogMessage(LogEventLevel.Debug, this, "Linking Rooms...");
-
- if (PropertiesConfig.Rooms == null)
- {
- Debug.LogMessage(LogEventLevel.Debug, this, "No rooms linked to this bridge.");
- return;
- }
-
- foreach (var room in PropertiesConfig.Rooms)
- {
- var rm = DeviceManager.GetDeviceForKey(room.RoomKey) as IBridgeAdvanced;
-
- if (rm == null)
+ if (registerResult != eDeviceRegistrationUnRegistrationResponse.Success)
{
- Debug.LogMessage(LogEventLevel.Debug, this,
- "Room {0} does not implement IBridgeAdvanced. Skipping...", room.RoomKey);
- continue;
+ this.LogVerbose("Registration result: {registerResult}", registerResult);
+ return;
}
- rm.LinkToApi(Eisc, room.JoinStart, room.JoinMapKey, this);
- }
- }
-
- ///
- /// Adds a join map
- ///
- ///
- ///
- public void AddJoinMap(string deviceKey, JoinMapBaseAdvanced joinMap)
- {
- if (!JoinMaps.ContainsKey(deviceKey))
- {
- JoinMaps.Add(deviceKey, joinMap);
- }
- else
- {
- Debug.LogMessage(LogEventLevel.Verbose, this, "Unable to add join map with key '{0}'. Key already exists in JoinMaps dictionary", deviceKey);
- }
- }
-
- ///
- /// Prints all the join maps on this bridge
- ///
- public virtual void PrintJoinMaps()
- {
- CrestronConsole.ConsoleCommandResponse("Join Maps for EISC IPID: {0}\r\n", Eisc.ID.ToString("X"));
-
- foreach (var joinMap in JoinMaps)
- {
- CrestronConsole.ConsoleCommandResponse("Join map for device '{0}':", joinMap.Key);
- joinMap.Value.PrintJoinMapInfo();
- }
- }
- ///
- /// Generates markdown for all join maps on this bridge
- ///
- public virtual void MarkdownForBridge(string bridgeKey)
- {
- Debug.LogMessage(LogEventLevel.Information, this, "Writing Joinmaps to files for EISC IPID: {0}", Eisc.ID.ToString("X"));
-
- foreach (var joinMap in JoinMaps)
- {
- Debug.LogMessage(LogEventLevel.Information, "Generating markdown for device '{0}':", joinMap.Key);
- joinMap.Value.MarkdownJoinMapInfo(joinMap.Key, bridgeKey);
- }
- }
-
- ///
- /// Prints the join map for a device by key
- ///
- ///
- public void PrintJoinMapForDevice(string deviceKey)
- {
- var joinMap = JoinMaps[deviceKey];
-
- if (joinMap == null)
- {
- Debug.LogMessage(LogEventLevel.Information, this, "Unable to find joinMap for device with key: '{0}'", deviceKey);
- return;
+ this.LogDebug("EISC registration successful");
}
- Debug.LogMessage(LogEventLevel.Information, "Join map for device '{0}' on EISC '{1}':", deviceKey, Key);
- joinMap.PrintJoinMapInfo();
- }
- ///
- /// Prints the join map for a device by key
- ///
- ///
- public void MarkdownJoinMapForDevice(string deviceKey, string bridgeKey)
- {
- var joinMap = JoinMaps[deviceKey];
-
- if (joinMap == null)
+ ///
+ /// Link rooms to this EISC. Rooms MUST implement IBridgeAdvanced
+ ///
+ public void LinkRooms()
{
- Debug.LogMessage(LogEventLevel.Information, this, "Unable to find joinMap for device with key: '{0}'", deviceKey);
- return;
- }
+ this.LogDebug("Linking Rooms...");
- Debug.LogMessage(LogEventLevel.Information, "Join map for device '{0}' on EISC '{1}':", deviceKey, Key);
- joinMap.MarkdownJoinMapInfo(deviceKey, bridgeKey);
- }
-
- ///
- /// Used for debugging to trigger an action based on a join number and type
- ///
- ///
- ///
- ///
- public void ExecuteJoinAction(uint join, string type, object state)
- {
- try
- {
- switch (type.ToLower())
+ if (PropertiesConfig.Rooms == null)
{
- case "digital":
- {
- var uo = Eisc.BooleanOutput[join].UserObject as Action;
- if (uo != null)
+ this.LogDebug("No rooms linked to this bridge.");
+ return;
+ }
+
+ foreach (var room in PropertiesConfig.Rooms)
+ {
+ if (!(DeviceManager.GetDeviceForKey(room.RoomKey) is IBridgeAdvanced rm))
+ {
+ this.LogDebug("Room {roomKey} does not implement IBridgeAdvanced. Skipping...", room.RoomKey);
+ continue;
+ }
+
+ rm.LinkToApi(Eisc, room.JoinStart, room.JoinMapKey, this);
+ }
+ }
+
+ ///
+ /// Adds a join map
+ ///
+ /// The key of the device to add the join map for
+ /// The join map to add
+ public void AddJoinMap(string deviceKey, JoinMapBaseAdvanced joinMap)
+ {
+ if (!JoinMaps.ContainsKey(deviceKey))
+ {
+ JoinMaps.Add(deviceKey, joinMap);
+ }
+ else
+ {
+ this.LogWarning("Unable to add join map with key '{deviceKey}'. Key already exists in JoinMaps dictionary", deviceKey);
+ }
+ }
+
+ ///
+ /// PrintJoinMaps method
+ ///
+ public virtual void PrintJoinMaps()
+ {
+ CrestronConsole.ConsoleCommandResponse("Join Maps for EISC IPID: {0}\r\n", Eisc.ID.ToString("X"));
+
+ foreach (var joinMap in JoinMaps)
+ {
+ CrestronConsole.ConsoleCommandResponse("Join map for device '{0}':", joinMap.Key);
+ joinMap.Value.PrintJoinMapInfo();
+ }
+ }
+
+ ///
+ /// MarkdownForBridge method
+ ///
+ public virtual void MarkdownForBridge(string bridgeKey)
+ {
+ this.LogInformation("Writing Joinmaps to files for EISC IPID: {eiscId}", Eisc.ID.ToString("X"));
+
+ foreach (var joinMap in JoinMaps)
+ {
+ this.LogInformation("Generating markdown for device '{deviceKey}':", joinMap.Key);
+ joinMap.Value.MarkdownJoinMapInfo(joinMap.Key, bridgeKey);
+ }
+ }
+
+ ///
+ /// Prints the join map for a device by key
+ ///
+ /// The key of the device to print the join map for
+ public void PrintJoinMapForDevice(string deviceKey)
+ {
+ var joinMap = JoinMaps[deviceKey];
+
+ if (joinMap == null)
+ {
+ this.LogInformation("Unable to find joinMap for device with key: '{deviceKey}'", deviceKey);
+ return;
+ }
+
+ this.LogInformation("Join map for device '{deviceKey}' on EISC '{eiscKey}':", deviceKey, Key);
+ joinMap.PrintJoinMapInfo();
+ }
+ ///
+ /// Prints the join map for a device by key in Markdown format
+ ///
+ /// The key of the device to print the join map for
+ /// The key of the bridge to use for the Markdown output
+ public void MarkdownJoinMapForDevice(string deviceKey, string bridgeKey)
+ {
+ var joinMap = JoinMaps[deviceKey];
+
+ if (joinMap == null)
+ {
+ this.LogInformation("Unable to find joinMap for device with key: '{deviceKey}'", deviceKey);
+ return;
+ }
+
+ this.LogInformation("Join map for device '{deviceKey}' on EISC '{eiscKey}':", deviceKey, Key);
+ joinMap.MarkdownJoinMapInfo(deviceKey, bridgeKey);
+ }
+
+ ///
+ /// Used for debugging to trigger an action based on a join number and type
+ ///
+ /// The join number to execute the action for
+ /// The type of join (digital, analog, serial)
+ /// The state to pass to the action
+ public void ExecuteJoinAction(uint join, string type, object state)
+ {
+ try
+ {
+ switch (type.ToLower())
+ {
+ case "digital":
{
- Debug.LogMessage(LogEventLevel.Verbose, this, "Executing Action: {0}", uo.ToString());
- uo(Convert.ToBoolean(state));
+ if (Eisc.BooleanOutput[join].UserObject is Action userObject)
+ {
+ this.LogVerbose("Executing Boolean Action");
+ userObject(Convert.ToBoolean(state));
+ }
+ else
+ this.LogVerbose("User Object is null. Nothing to Execute");
+ break;
}
- else
- Debug.LogMessage(LogEventLevel.Verbose, this, "User Action is null. Nothing to Execute");
+ case "analog":
+ {
+ if (Eisc.UShortOutput[join].UserObject is Action userObject)
+ {
+ this.LogVerbose("Executing Analog Action");
+ userObject(Convert.ToUInt16(state));
+ }
+ else
+ this.LogVerbose("User Object is null. Nothing to Execute");
+ break;
+ }
+ case "serial":
+ {
+ if (Eisc.StringOutput[join].UserObject is Action userObject)
+ {
+ this.LogVerbose("Executing Serial Action");
+ userObject(Convert.ToString(state));
+ }
+ else
+ this.LogVerbose("User Object is null. Nothing to Execute");
+ break;
+ }
+ default:
+ {
+ this.LogVerbose("Unknown join type. Use digital/serial/analog");
+ break;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ this.LogError("ExecuteJoinAction error: {message}", e.Message);
+ this.LogDebug(e, "Stack Trace: ");
+ }
+
+ }
+
+ ///
+ /// Handle incoming sig changes
+ ///
+ /// BasicTriList device that triggered the event
+ /// Event arguments containing the signal information
+ protected void Eisc_SigChange(object currentDevice, SigEventArgs args)
+ {
+ try
+ {
+ this.LogVerbose("EiscApiAdvanced change: {type} {number}={value}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue);
+ var userObject = args.Sig.UserObject;
+
+ if (userObject == null) return;
+
+
+ if (userObject is Action)
+ {
+ this.LogDebug("Executing Boolean Action");
+ (userObject as Action)(args.Sig.BoolValue);
+ }
+ else if (userObject is Action)
+ {
+ this.LogDebug("Executing Analog Action");
+ (userObject as Action)(args.Sig.UShortValue);
+ }
+ else if (userObject is Action)
+ {
+ this.LogDebug("Executing Serial Action");
+ (userObject as Action)(args.Sig.StringValue);
+ }
+ }
+ catch (Exception e)
+ {
+ this.LogError("Eisc_SigChange handler error: {message}", e.Message);
+ this.LogDebug(e, "Stack Trace: ");
+ }
+ }
+
+ #region Implementation of ICommunicationMonitor
+
+ ///
+ /// Gets or sets the CommunicationMonitor
+ ///
+ public StatusMonitorBase CommunicationMonitor { get; private set; }
+
+ #endregion
+ }
+
+ ///
+ /// Represents a EiscApiPropertiesConfig
+ ///
+ public class EiscApiPropertiesConfig
+ {
+ ///
+ /// Gets or sets the Control
+ ///
+ [JsonProperty("control")]
+ public EssentialsControlPropertiesConfig Control { get; set; }
+
+ ///
+ /// Gets or sets the Devices
+ ///
+ [JsonProperty("devices")]
+ public List Devices { get; set; }
+
+ ///
+ /// Gets or sets the Rooms
+ ///
+ [JsonProperty("rooms")]
+ public List Rooms { get; set; }
+
+
+ ///
+ /// Represents a ApiDevicePropertiesConfig
+ ///
+ public class ApiDevicePropertiesConfig
+ {
+ ///
+ /// Gets or sets the DeviceKey
+ ///
+ [JsonProperty("deviceKey")]
+ public string DeviceKey { get; set; }
+
+ ///
+ /// Gets or sets the JoinStart
+ ///
+ [JsonProperty("joinStart")]
+ public uint JoinStart { get; set; }
+
+ ///
+ /// Gets or sets the JoinMapKey
+ ///
+ [JsonProperty("joinMapKey")]
+ public string JoinMapKey { get; set; }
+ }
+
+ ///
+ /// Represents a ApiRoomPropertiesConfig
+ ///
+ public class ApiRoomPropertiesConfig
+ {
+ ///
+ /// Gets or sets the RoomKey
+ ///
+ [JsonProperty("roomKey")]
+ public string RoomKey { get; set; }
+
+ ///
+ /// Gets or sets the JoinStart
+ ///
+ [JsonProperty("joinStart")]
+ public uint JoinStart { get; set; }
+
+ ///
+ /// Gets or sets the JoinMapKey
+ ///
+ [JsonProperty("joinMapKey")]
+ public string JoinMapKey { get; set; }
+ }
+
+ }
+
+ ///
+ /// Factory class for EiscApiAdvanced devices
+ ///
+ ///
+ /// Supported types:
+ /// eiscapiadv - Create a standard EISC client over TCP/IP
+ /// eiscapiadvanced - Create a standard EISC client over TCP/IP
+ /// eiscapiadvancedserver - Create an EISC server
+ /// eiscapiadvancedclient - Create an EISC client
+ /// vceiscapiadv - Create a VC-4 EISC client
+ /// vceiscapiadvanced - Create a VC-4 EISC client
+ /// eiscapiadvudp - Create a standard EISC client over UDP
+ /// eiscapiadvancedudp - Create a standard EISC client over UDP
+ ///
+ public class EiscApiAdvancedFactory : EssentialsDeviceFactory
+ {
+ ///
+ /// Constructor
+ ///
+ public EiscApiAdvancedFactory()
+ {
+ TypeNames = new List { "eiscapiadv", "eiscapiadvanced", "eiscapiadvancedserver", "eiscapiadvancedclient", "vceiscapiadv", "vceiscapiadvanced", "eiscapiadvudp", "eiscapiadvancedudp" };
+ }
+
+ ///
+ public override EssentialsDevice BuildDevice(DeviceConfig dc)
+ {
+ Debug.LogDebug("Attempting to create new EiscApiAdvanced Device");
+
+ var controlProperties = CommFactory.GetControlPropertiesConfig(dc);
+
+ BasicTriList eisc;
+
+ switch (dc.Type.ToLower())
+ {
+ case "eiscapiadvudp":
+ case "eiscapiadvancedudp":
+ {
+ eisc = new EthernetIntersystemCommunications(controlProperties.IpIdInt,
+ controlProperties.TcpSshProperties.Address, Global.ControlSystem);
break;
}
- case "analog":
+ case "eiscapiadv":
+ case "eiscapiadvanced":
{
- var uo = Eisc.BooleanOutput[join].UserObject as Action;
- if (uo != null)
- {
- Debug.LogMessage(LogEventLevel.Verbose, this, "Executing Action: {0}", uo.ToString());
- uo(Convert.ToUInt16(state));
- }
- else
- Debug.LogMessage(LogEventLevel.Verbose, this, "User Action is null. Nothing to Execute"); break;
+ eisc = new ThreeSeriesTcpIpEthernetIntersystemCommunications(controlProperties.IpIdInt,
+ controlProperties.TcpSshProperties.Address, Global.ControlSystem);
+ break;
}
- case "serial":
+ case "eiscapiadvancedserver":
{
- var uo = Eisc.BooleanOutput[join].UserObject as Action;
- if (uo != null)
+ eisc = new EISCServer(controlProperties.IpIdInt, Global.ControlSystem);
+ break;
+ }
+ case "eiscapiadvancedclient":
+ {
+ eisc = new EISCClient(controlProperties.IpIdInt, controlProperties.TcpSshProperties.Address, Global.ControlSystem);
+ break;
+ }
+ case "vceiscapiadv":
+ case "vceiscapiadvanced":
+ {
+ if (string.IsNullOrEmpty(controlProperties.RoomId))
{
- Debug.LogMessage(LogEventLevel.Verbose, this, "Executing Action: {0}", uo.ToString());
- uo(Convert.ToString(state));
+ Debug.LogInformation("Unable to build VC-4 EISC Client for device {deviceKey}. Room ID is missing or empty", dc.Key);
+ eisc = null;
+ break;
}
- else
- Debug.LogMessage(LogEventLevel.Verbose, this, "User Action is null. Nothing to Execute");
+ eisc = new VirtualControlEISCClient(controlProperties.IpIdInt, controlProperties.RoomId,
+ Global.ControlSystem);
break;
}
default:
- {
- Debug.LogMessage(LogEventLevel.Verbose, "Unknown join type. Use digital/serial/analog");
- break;
- }
+ eisc = null;
+ break;
}
- }
- catch (Exception e)
- {
- Debug.LogMessage(LogEventLevel.Debug, this, "Error: {0}", e);
- }
- }
+ if (eisc == null)
+ {
+ return null;
+ }
- ///
- /// Handles incoming sig changes
- ///
- ///
- ///
- protected void Eisc_SigChange(object currentDevice, SigEventArgs args)
- {
- try
- {
- Debug.LogMessage(LogEventLevel.Verbose, this, "EiscApiAdvanced change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue);
- var uo = args.Sig.UserObject;
-
- if (uo == null) return;
-
- Debug.LogMessage(LogEventLevel.Debug, this, "Executing Action: {0}", uo.ToString());
- if (uo is Action)
- (uo as Action)(args.Sig.BoolValue);
- else if (uo is Action)
- (uo as Action)(args.Sig.UShortValue);
- else if (uo is Action)
- (uo as Action)(args.Sig.StringValue);
- }
- catch (Exception e)
- {
- Debug.LogMessage(LogEventLevel.Verbose, this, "Error in Eisc_SigChange handler: {0}", e);
+ return new EiscApiAdvanced(dc, eisc);
}
}
- #region Implementation of ICommunicationMonitor
-
- public StatusMonitorBase CommunicationMonitor { get; private set; }
-
- #endregion
-}
-
-public class EiscApiPropertiesConfig
-{
- [JsonProperty("control")]
- public EssentialsControlPropertiesConfig Control { get; set; }
-
- [JsonProperty("devices")]
- public List Devices { get; set; }
-
- [JsonProperty("rooms")]
- public List Rooms { get; set; }
-
-
- public class ApiDevicePropertiesConfig
- {
- [JsonProperty("deviceKey")]
- public string DeviceKey { get; set; }
-
- [JsonProperty("joinStart")]
- public uint JoinStart { get; set; }
-
- [JsonProperty("joinMapKey")]
- public string JoinMapKey { get; set; }
- }
-
- public class ApiRoomPropertiesConfig
- {
- [JsonProperty("roomKey")]
- public string RoomKey { get; set; }
-
- [JsonProperty("joinStart")]
- public uint JoinStart { get; set; }
-
- [JsonProperty("joinMapKey")]
- public string JoinMapKey { get; set; }
- }
-
-}
-
-public class EiscApiAdvancedFactory : EssentialsDeviceFactory
-{
- public EiscApiAdvancedFactory()
- {
- TypeNames = new List { "eiscapiadv", "eiscapiadvanced", "eiscapiadvancedserver", "eiscapiadvancedclient", "vceiscapiadv", "vceiscapiadvanced" };
- }
-
- public override EssentialsDevice BuildDevice(DeviceConfig dc)
- {
- Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new EiscApiAdvanced Device");
-
- var controlProperties = CommFactory.GetControlPropertiesConfig(dc);
-
- BasicTriList eisc;
-
- switch (dc.Type.ToLower())
- {
- case "eiscapiadv":
- case "eiscapiadvanced":
- {
- eisc = new ThreeSeriesTcpIpEthernetIntersystemCommunications(controlProperties.IpIdInt,
- controlProperties.TcpSshProperties.Address, Global.ControlSystem);
- break;
- }
- case "eiscapiadvancedserver":
- {
- eisc = new EISCServer(controlProperties.IpIdInt, Global.ControlSystem);
- break;
- }
- case "eiscapiadvancedclient":
- {
- eisc = new EISCClient(controlProperties.IpIdInt, controlProperties.TcpSshProperties.Address, Global.ControlSystem);
- break;
- }
- case "vceiscapiadv":
- case "vceiscapiadvanced":
- {
- if (string.IsNullOrEmpty(controlProperties.RoomId))
- {
- Debug.LogMessage(LogEventLevel.Information, "Unable to build VC-4 EISC Client for device {0}. Room ID is missing or empty", dc.Key);
- eisc = null;
- break;
- }
- eisc = new VirtualControlEISCClient(controlProperties.IpIdInt, controlProperties.RoomId,
- Global.ControlSystem);
- break;
- }
- default:
- eisc = null;
- break;
- }
-
- if (eisc == null)
- {
- return null;
- }
-
- return new EiscApiAdvanced(dc, eisc);
- }
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/BridgeHelper.cs b/src/PepperDash.Essentials.Core/Bridges/BridgeHelper.cs
index 3a3ceff2..8d183b77 100644
--- a/src/PepperDash.Essentials.Core/Bridges/BridgeHelper.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/BridgeHelper.cs
@@ -3,62 +3,71 @@ using Serilog.Events;
//using PepperDash.Essentials.Devices.Common.Cameras;
-namespace PepperDash.Essentials.Core.Bridges;
-
-///
-/// Helper methods for bridges
-///
-public static class BridgeHelper
+namespace PepperDash.Essentials.Core.Bridges
{
- public static void PrintJoinMap(string command)
+ ///
+ /// Helper methods for bridges
+ ///
+ public static class BridgeHelper
{
- var targets = command.Split(' ');
-
- var bridgeKey = targets[0].Trim();
-
- if (!(DeviceManager.GetDeviceForKey(bridgeKey) is EiscApiAdvanced bridge))
+ ///
+ /// PrintJoinMp method
+ ///
+ /// target bridgekey to print join map for
+ public static void PrintJoinMap(string command)
{
- Debug.LogMessage(LogEventLevel.Information, "Unable to find advanced bridge with key: '{0}'", bridgeKey);
- return;
+ 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();
+ }
}
-
- if (targets.Length > 1)
+ ///
+ /// JoinmapMarkdown method
+ ///
+ public static void JoinmapMarkdown(string command)
{
- var deviceKey = targets[1].Trim();
+ var targets = command.Split(' ');
- if (string.IsNullOrEmpty(deviceKey)) return;
- bridge.PrintJoinMapForDevice(deviceKey);
- }
- else
- {
- bridge.PrintJoinMaps();
+ 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);
+
+ }
}
}
- 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);
-
- }
- }
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/IBridge.cs b/src/PepperDash.Essentials.Core/Bridges/IBridge.cs
index 87b4bd35..34c11171 100644
--- a/src/PepperDash.Essentials.Core/Bridges/IBridge.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/IBridge.cs
@@ -1,11 +1,19 @@
using Crestron.SimplSharpPro.DeviceSupport;
-namespace PepperDash.Essentials.Core.Bridges;
-
-///
-/// Defines a device that uses JoinMapBaseAdvanced for its join map
-///
-public interface IBridgeAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge);
+ ///
+ /// Defines the contract for IBridgeAdvanced
+ ///
+ public interface IBridgeAdvanced
+ {
+ ///
+ /// Links the bridge to the API using the provided trilist, join start, join map key, and bridge.
+ ///
+ /// The trilist to link to.
+ /// The starting join number.
+ /// The key for the join map.
+ /// The EISC API bridge.
+ void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge);
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/AirMediaControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/AirMediaControllerJoinMap.cs
index 33fca20e..2bd218f6 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/AirMediaControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/AirMediaControllerJoinMap.cs
@@ -1,70 +1,110 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class AirMediaControllerJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("IsOnline")]
- public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Air Media Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("IsInSession")]
- public JoinDataComplete IsInSession = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Air Media In Sharing Session", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("HdmiVideoSync")]
- public JoinDataComplete HdmiVideoSync = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Air Media Has HDMI Video Sync", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("AutomaticInputRoutingEnabled")]
- public JoinDataComplete AutomaticInputRoutingEnabled = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "Air Media Automatic Input Routing Enable(d)", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("VideoOut")]
- public JoinDataComplete VideoOut = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Air Media Video Route Select / Feedback", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("ErrorFB")]
- public JoinDataComplete ErrorFB = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Air Media Error Status", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("NumberOfUsersConnectedFB")]
- public JoinDataComplete NumberOfUsersConnectedFB = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Air Media Number of Users Connected", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("LoginCode")]
- public JoinDataComplete LoginCode = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "Air Media Login Code Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("Name")]
- public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Air Media Device Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("ConnectionAddressFB")]
- public JoinDataComplete ConnectionAddressFB = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Air Media IP Address", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("HostnameFB")]
- public JoinDataComplete HostnameFB = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Air Media Hostname", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("SerialNumberFeedback")]
- public JoinDataComplete SerialNumberFeedback = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "Air Media Serial Number", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a AirMediaControllerJoinMap
///
- /// Join this join map will start at
- public AirMediaControllerJoinMap(uint joinStart)
- : this(joinStart, typeof(AirMediaControllerJoinMap))
+ public class AirMediaControllerJoinMap : JoinMapBaseAdvanced
{
- }
+ ///
+ /// Air Media Online status
+ ///
+ [JoinName("IsOnline")]
+ public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Air Media Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected AirMediaControllerJoinMap(uint joinStart, Type type) : base(joinStart, type){}
+ ///
+ /// Air Media In Sharing Session status
+ ///
+ [JoinName("IsInSession")]
+ public JoinDataComplete IsInSession = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Air Media In Sharing Session", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Air Media Has HDMI Video Sync status
+ ///
+ [JoinName("HdmiVideoSync")]
+ public JoinDataComplete HdmiVideoSync = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Air Media Has HDMI Video Sync", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Air Media Automatic Input Routing Enable(d)
+ ///
+ [JoinName("AutomaticInputRoutingEnabled")]
+ public JoinDataComplete AutomaticInputRoutingEnabled = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "Air Media Automatic Input Routing Enable(d)", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Air Media Video Route Select / Feedback
+ ///
+ [JoinName("VideoOut")]
+ public JoinDataComplete VideoOut = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Air Media Video Route Select / Feedback", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Air Media Error Status Feedback
+ ///
+ [JoinName("ErrorFB")]
+ public JoinDataComplete ErrorFB = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Air Media Error Status", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Air Media Number of Users Connected Feedback
+ ///
+ [JoinName("NumberOfUsersConnectedFB")]
+ public JoinDataComplete NumberOfUsersConnectedFB = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Air Media Number of Users Connected", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Air Media Login Code Set / Get
+ ///
+ [JoinName("LoginCode")]
+ public JoinDataComplete LoginCode = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "Air Media Login Code Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Air Media Device Name
+ ///
+ [JoinName("Name")]
+ public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Air Media Device Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Air Media IP Address Feedback
+ ///
+ [JoinName("ConnectionAddressFB")]
+ public JoinDataComplete ConnectionAddressFB = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Air Media IP Address", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Air Media Hostname Feedback
+ ///
+ [JoinName("HostnameFB")]
+ public JoinDataComplete HostnameFB = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Air Media Hostname", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Air Media Serial Number Feedback
+ ///
+ [JoinName("SerialNumberFeedback")]
+ public JoinDataComplete SerialNumberFeedback = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "Air Media Serial Number", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public AirMediaControllerJoinMap(uint joinStart)
+ : this(joinStart, typeof(AirMediaControllerJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected AirMediaControllerJoinMap(uint joinStart, Type type) : base(joinStart, type){}
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/AppleTvJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/AppleTvJoinMap.cs
index 3b3eaa06..c337f9cf 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/AppleTvJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/AppleTvJoinMap.cs
@@ -1,52 +1,77 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class AppleTvJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("UpArrow")]
- public JoinDataComplete UpArrow = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "AppleTv Nav Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DnArrow")]
- public JoinDataComplete DnArrow = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "AppleTv Nav Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("LeftArrow")]
- public JoinDataComplete LeftArrow = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "AppleTv Nav Left", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("RightArrow")]
- public JoinDataComplete RightArrow = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "AppleTv Nav Right", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Menu")]
- public JoinDataComplete Menu = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "AppleTv Menu", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Select")]
- public JoinDataComplete Select = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
- new JoinMetadata { Description = "AppleTv Select", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("PlayPause")]
- public JoinDataComplete PlayPause = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
- new JoinMetadata { Description = "AppleTv Play/Pause", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a AppleTvJoinMap
///
- /// Join this join map will start at
- public AppleTvJoinMap(uint joinStart)
- : base(joinStart, typeof(AppleTvJoinMap))
+ public class AppleTvJoinMap : JoinMapBaseAdvanced
{
- }
+ ///
+ /// AppleTv Nav Up
+ ///
+ [JoinName("UpArrow")]
+ public JoinDataComplete UpArrow = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "AppleTv Nav Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- public AppleTvJoinMap(uint joinStart, Type type) : base(joinStart, type)
- {
+ ///
+ /// AppleTv Nav Down
+ ///
+ [JoinName("DnArrow")]
+ public JoinDataComplete DnArrow = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "AppleTv Nav Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// AppleTv Nav Left
+ ///
+ [JoinName("LeftArrow")]
+ public JoinDataComplete LeftArrow = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "AppleTv Nav Left", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// AppleTv Nav Right
+ ///
+ [JoinName("RightArrow")]
+ public JoinDataComplete RightArrow = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "AppleTv Nav Right", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// AppleTv Menu
+ ///
+ [JoinName("Menu")]
+ public JoinDataComplete Menu = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "AppleTv Menu", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// AppleTv Select
+ ///
+ [JoinName("Select")]
+ public JoinDataComplete Select = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
+ new JoinMetadata { Description = "AppleTv Select", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// AppleTv Play/Pause
+ ///
+ [JoinName("PlayPause")]
+ public JoinDataComplete PlayPause = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
+ new JoinMetadata { Description = "AppleTv Play/Pause", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public AppleTvJoinMap(uint joinStart)
+ : base(joinStart, typeof(AppleTvJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ public AppleTvJoinMap(uint joinStart, Type type) : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/C2nRthsControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/C2nRthsControllerJoinMap.cs
index 17f8f4db..bb9da22f 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/C2nRthsControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/C2nRthsControllerJoinMap.cs
@@ -1,44 +1,63 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class C2nRthsControllerJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("IsOnline")]
- public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Temp Sensor Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("TemperatureFormat")]
- public JoinDataComplete TemperatureFormat = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Temp Sensor Unit Format", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Temperature")]
- public JoinDataComplete Temperature = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Temp Sensor Temperature Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("Humidity")]
- public JoinDataComplete Humidity = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Temp Sensor Humidity Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("Name")]
- public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Temp Sensor Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a C2nRthsControllerJoinMap
///
- /// Join this join map will start at
- public C2nRthsControllerJoinMap(uint joinStart)
- : this(joinStart, typeof(C2nRthsControllerJoinMap))
+ public class C2nRthsControllerJoinMap : JoinMapBaseAdvanced
{
- }
+ ///
+ /// C2nRthsController Online status
+ ///
+ [JoinName("IsOnline")]
+ public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Temp Sensor Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected C2nRthsControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)
- {
+ ///
+ /// Temperature Format (C/F)
+ ///
+ [JoinName("TemperatureFormat")]
+ public JoinDataComplete TemperatureFormat = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Temp Sensor Unit Format", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Temperature Sensor Feedbacks
+ ///
+ [JoinName("Temperature")]
+ public JoinDataComplete Temperature = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Temp Sensor Temperature Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Humidity Sensor Feedbacks
+ ///
+ [JoinName("Humidity")]
+ public JoinDataComplete Humidity = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Temp Sensor Humidity Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Temp Sensor Name
+ ///
+ [JoinName("Name")]
+ public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Temp Sensor Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public C2nRthsControllerJoinMap(uint joinStart)
+ : this(joinStart, typeof(C2nRthsControllerJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected C2nRthsControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/CameraControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/CameraControllerJoinMap.cs
index 0cb011fa..aad43513 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/CameraControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/CameraControllerJoinMap.cs
@@ -1,68 +1,140 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-///
-/// Join map for CameraBase devices
-///
-public class CameraControllerJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("TiltUp")]
- public JoinDataComplete TiltUp = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, new JoinMetadata { Description = "Tilt Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
- [JoinName("TiltDown")]
- public JoinDataComplete TiltDown = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 }, new JoinMetadata { Description = "Tilt Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
- [JoinName("PanLeft")]
- public JoinDataComplete PanLeft = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 }, new JoinMetadata { Description = "Pan Left", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
- [JoinName("PanRight")]
- public JoinDataComplete PanRight = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 }, new JoinMetadata { Description = "Pan Right", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
- [JoinName("ZoomIn")]
- public JoinDataComplete ZoomIn = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 }, new JoinMetadata { Description = "Zoom In", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
- [JoinName("ZoomOut")]
- public JoinDataComplete ZoomOut = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 }, new JoinMetadata { Description = "Zoom Out", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("IsOnline")]
- public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 }, new JoinMetadata { Description = "Is Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
- [JoinName("PowerOn")]
- public JoinDataComplete PowerOn = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 }, new JoinMetadata { Description = "Power On", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
- [JoinName("PowerOff")]
- public JoinDataComplete PowerOff = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 }, new JoinMetadata { Description = "Power Off", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("NumberOfPresets")]
- public JoinDataComplete NumberOfPresets = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 }, new JoinMetadata { Description = "Tells Essentials the number of defined presets", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });
- [JoinName("PresetRecallStart")]
- public JoinDataComplete PresetRecallStart = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 20 }, new JoinMetadata { Description = "Preset Recall Start", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
- [JoinName("PresetLabelStart")]
- public JoinDataComplete PresetLabelStart = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 20 }, new JoinMetadata { Description = "Preset Label Start", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });
- [JoinName("PresetSaveStart")]
- public JoinDataComplete PresetSaveStart = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 20 }, new JoinMetadata { Description = "Preset Save Start", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("CameraModeAuto")]
- public JoinDataComplete CameraModeAuto = new JoinDataComplete(new JoinData { JoinNumber = 51, JoinSpan = 1 }, new JoinMetadata { Description = "Camera Mode Auto", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
- [JoinName("CameraModeManual")]
- public JoinDataComplete CameraModeManual = new JoinDataComplete(new JoinData { JoinNumber = 52, JoinSpan = 1 }, new JoinMetadata { Description = "Camera Mode Manual", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
- [JoinName("CameraModeOff")]
- public JoinDataComplete CameraModeOff = new JoinDataComplete(new JoinData { JoinNumber = 53, JoinSpan = 1 }, new JoinMetadata { Description = "Camera Mode Off", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("SupportsCameraModeAuto")]
- public JoinDataComplete SupportsCameraModeAuto = new JoinDataComplete(new JoinData { JoinNumber = 55, JoinSpan = 1 }, new JoinMetadata { Description = "Supports Camera Mode Auto", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
- [JoinName("SupportsCameraModeOff")]
- public JoinDataComplete SupportsCameraModeOff = new JoinDataComplete(new JoinData { JoinNumber = 56, JoinSpan = 1 }, new JoinMetadata { Description = "Supports Camera Mode Off", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
- [JoinName("SupportsPresets")]
- public JoinDataComplete SupportsPresets = new JoinDataComplete(new JoinData { JoinNumber = 57, JoinSpan = 1 }, new JoinMetadata { Description = "Supports Presets", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a CameraControllerJoinMap
///
- /// Join this join map will start at
- public CameraControllerJoinMap(uint joinStart)
- : this(joinStart, typeof(CameraControllerJoinMap))
+ public class CameraControllerJoinMap : JoinMapBaseAdvanced
{
- }
+ ///
+ /// Tilt Up
+ ///
+ [JoinName("TiltUp")]
+ public JoinDataComplete TiltUp = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, new JoinMetadata { Description = "Tilt Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Tilt Down
+ ///
+ [JoinName("TiltDown")]
+ public JoinDataComplete TiltDown = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 }, new JoinMetadata { Description = "Tilt Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Pan Left
+ ///
+ [JoinName("PanLeft")]
+ public JoinDataComplete PanLeft = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 }, new JoinMetadata { Description = "Pan Left", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Pan Right
+ ///
+ [JoinName("PanRight")]
+ public JoinDataComplete PanRight = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 }, new JoinMetadata { Description = "Pan Right", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Zoom In
+ ///
+ [JoinName("ZoomIn")]
+ public JoinDataComplete ZoomIn = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 }, new JoinMetadata { Description = "Zoom In", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Zoom Out
+ ///
+ [JoinName("ZoomOut")]
+ public JoinDataComplete ZoomOut = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 }, new JoinMetadata { Description = "Zoom Out", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Is Online
+ ///
+ [JoinName("IsOnline")]
+ public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 }, new JoinMetadata { Description = "Is Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Power On
+ ///
+ [JoinName("PowerOn")]
+ public JoinDataComplete PowerOn = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 }, new JoinMetadata { Description = "Power On", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Power Off
+ ///
+ [JoinName("PowerOff")]
+ public JoinDataComplete PowerOff = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 }, new JoinMetadata { Description = "Power Off", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Number Of Presets
+ ///
+ [JoinName("NumberOfPresets")]
+ public JoinDataComplete NumberOfPresets = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 }, new JoinMetadata { Description = "Tells Essentials the number of defined presets", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Preset Recall Start
+ ///
+ [JoinName("PresetRecallStart")]
+ public JoinDataComplete PresetRecallStart = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 20 }, new JoinMetadata { Description = "Preset Recall Start", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Preset Label Start
+ ///
+ [JoinName("PresetLabelStart")]
+ public JoinDataComplete PresetLabelStart = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 20 }, new JoinMetadata { Description = "Preset Label Start", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Preset Save Start
+ ///
+ [JoinName("PresetSaveStart")]
+ public JoinDataComplete PresetSaveStart = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 20 }, new JoinMetadata { Description = "Preset Save Start", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Camera Mode Auto
+ ///
+ [JoinName("CameraModeAuto")]
+ public JoinDataComplete CameraModeAuto = new JoinDataComplete(new JoinData { JoinNumber = 51, JoinSpan = 1 }, new JoinMetadata { Description = "Camera Mode Auto", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Camera Mode Manual
+ ///
+ [JoinName("CameraModeManual")]
+ public JoinDataComplete CameraModeManual = new JoinDataComplete(new JoinData { JoinNumber = 52, JoinSpan = 1 }, new JoinMetadata { Description = "Camera Mode Manual", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Camera Mode Off
+ ///
+ [JoinName("CameraModeOff")]
+ public JoinDataComplete CameraModeOff = new JoinDataComplete(new JoinData { JoinNumber = 53, JoinSpan = 1 }, new JoinMetadata { Description = "Camera Mode Off", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Supports Camera Mode Manual
+ ///
+ [JoinName("SupportsCameraModeAuto")]
+ public JoinDataComplete SupportsCameraModeAuto = new JoinDataComplete(new JoinData { JoinNumber = 55, JoinSpan = 1 }, new JoinMetadata { Description = "Supports Camera Mode Auto", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Supports Camera Mode Off
+ ///
+ [JoinName("SupportsCameraModeOff")]
+ public JoinDataComplete SupportsCameraModeOff = new JoinDataComplete(new JoinData { JoinNumber = 56, JoinSpan = 1 }, new JoinMetadata { Description = "Supports Camera Mode Off", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Supports Presets
+ ///
+ [JoinName("SupportsPresets")]
+ public JoinDataComplete SupportsPresets = new JoinDataComplete(new JoinData { JoinNumber = 57, JoinSpan = 1 }, new JoinMetadata { Description = "Supports Presets", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected CameraControllerJoinMap(uint joinStart, Type type) : base(joinStart, type){}
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public CameraControllerJoinMap(uint joinStart)
+ : this(joinStart, typeof(CameraControllerJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected CameraControllerJoinMap(uint joinStart, Type type) : base(joinStart, type){}
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/CenOdtOccupancySensorBaseJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/CenOdtOccupancySensorBaseJoinMap.cs
index b585916b..b52c5309 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/CenOdtOccupancySensorBaseJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/CenOdtOccupancySensorBaseJoinMap.cs
@@ -1,196 +1,320 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class CenOdtOccupancySensorBaseJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- #region Digitals
-
- [JoinName("Online")]
- public JoinDataComplete Online = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("ForceOccupied")]
- public JoinDataComplete ForceOccupied = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Force Occupied", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("ForceVacant")]
- public JoinDataComplete ForceVacant = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Force Vacant", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("EnableRawStates")]
- public JoinDataComplete EnableRawStates = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "Enable Raw States", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("RoomOccupiedFeedback")]
- public JoinDataComplete RoomOccupiedFeedback = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Room Occupied Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("GraceOccupancyDetectedFeedback")]
- public JoinDataComplete GraceOccupancyDetectedFeedback = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Grace Occupancy Detected Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("RoomVacantFeedback")]
- public JoinDataComplete RoomVacantFeedback = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "Room Vacant Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("RawOccupancyFeedback")]
- public JoinDataComplete RawOccupancyFeedback = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "Raw Occupancy Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("RawOccupancyPirFeedback")]
- public JoinDataComplete RawOccupancyPirFeedback = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
- new JoinMetadata { Description = "Raw Occupancy Pir Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("RawOccupancyUsFeedback")]
- public JoinDataComplete RawOccupancyUsFeedback = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
- new JoinMetadata { Description = "Raw Occupancy Us Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("IdentityModeOn")]
- public JoinDataComplete IdentityMode = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
- new JoinMetadata { Description = "Enable Identity Mode", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("IdentityModeFeedback")]
- public JoinDataComplete IdentityModeFeedback = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
- new JoinMetadata { Description = "Identity Mode Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("EnableLedFlash")]
- public JoinDataComplete EnableLedFlash = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
- new JoinMetadata { Description = "Enable Led Flash", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DisableLedFlash")]
- public JoinDataComplete DisableLedFlash = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
- new JoinMetadata { Description = "Disable Led Flash", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("EnableShortTimeout")]
- public JoinDataComplete EnableShortTimeout = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
- new JoinMetadata { Description = "Enable Short Timeout", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DisableShortTimeout")]
- public JoinDataComplete DisableShortTimeout = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
- new JoinMetadata { Description = "Disable Short Timeout", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("OrWhenVacated")]
- public JoinDataComplete OrWhenVacated = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
- new JoinMetadata { Description = "Or When Vacated", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("AndWhenVacated")]
- public JoinDataComplete AndWhenVacated = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 1 },
- new JoinMetadata { Description = "AndWhenVacated", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("EnableUsA")]
- public JoinDataComplete EnableUsA = new JoinDataComplete(new JoinData { JoinNumber = 17, JoinSpan = 1 },
- new JoinMetadata { Description = "Enable Us A", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DisableUsA")]
- public JoinDataComplete DisableUsA = new JoinDataComplete(new JoinData { JoinNumber = 18, JoinSpan = 1 },
- new JoinMetadata { Description = "Disable Us A", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("EnableUsB")]
- public JoinDataComplete EnableUsB = new JoinDataComplete(new JoinData { JoinNumber = 19, JoinSpan = 1 },
- new JoinMetadata { Description = "Enable Us B", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DisableUsB")]
- public JoinDataComplete DisableUsB = new JoinDataComplete(new JoinData { JoinNumber = 20, JoinSpan = 1 },
- new JoinMetadata { Description = "Disable Us B", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("EnablePir")]
- public JoinDataComplete EnablePir = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },
- new JoinMetadata { Description = "Enable Pir", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DisablePir")]
- public JoinDataComplete DisablePir = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },
- new JoinMetadata { Description = "Disable Pir", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("IncrementUsInOccupiedState")]
- public JoinDataComplete IncrementUsInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },
- new JoinMetadata { Description = "Increment Us In OccupiedState", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DecrementUsInOccupiedState")]
- public JoinDataComplete DecrementUsInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },
- new JoinMetadata { Description = "Dencrement Us In Occupied State", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("IncrementUsInVacantState")]
- public JoinDataComplete IncrementUsInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },
- new JoinMetadata { Description = "Increment Us In VacantState", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DecrementUsInVacantState")]
- public JoinDataComplete DecrementUsInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 26, JoinSpan = 1 },
- new JoinMetadata { Description = "Decrement Us In VacantState", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("IncrementPirInOccupiedState")]
- public JoinDataComplete IncrementPirInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 27, JoinSpan = 1 },
- new JoinMetadata { Description = "Increment Pir In Occupied State", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DecrementPirInOccupiedState")]
- public JoinDataComplete DecrementPirInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 28, JoinSpan = 1 },
- new JoinMetadata { Description = "Decrement Pir In OccupiedState", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("IncrementPirInVacantState")]
- public JoinDataComplete IncrementPirInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 29, JoinSpan = 1 },
- new JoinMetadata { Description = "Increment Pir In Vacant State", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DecrementPirInVacantState")]
- public JoinDataComplete DecrementPirInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },
- new JoinMetadata { Description = "Decrement Pir In Vacant State", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- #endregion
-
- #region Analog
-
- [JoinName("Timeout")]
- public JoinDataComplete Timeout = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Timeout", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("TimeoutLocalFeedback")]
- public JoinDataComplete TimeoutLocalFeedback = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Timeout Local Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("InternalPhotoSensorValue")]
- public JoinDataComplete InternalPhotoSensorValue = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Internal PhotoSensor Value", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("UsSensitivityInOccupiedState")]
- public JoinDataComplete UsSensitivityInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "Us Sensitivity In Occupied State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("UsSensitivityInVacantState")]
- public JoinDataComplete UsSensitivityInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
- new JoinMetadata { Description = "Us Sensitivity In Vacant State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("PirSensitivityInOccupiedState")]
- public JoinDataComplete PirSensitivityInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
- new JoinMetadata { Description = "Pir Sensitivity In Occupied State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("PirSensitivityInVacantState")]
- public JoinDataComplete PirSensitivityInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
- new JoinMetadata { Description = "Pir Sensitivity In Vacant State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- #endregion
-
- #region Serial
-
- [JoinName("Name")]
- public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- #endregion
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a CenOdtOccupancySensorBaseJoinMap
///
- /// Join this join map will start at
- public CenOdtOccupancySensorBaseJoinMap(uint joinStart)
- : this(joinStart, typeof(CenOdtOccupancySensorBaseJoinMap))
+ public class CenOdtOccupancySensorBaseJoinMap : JoinMapBaseAdvanced
{
+ #region Digitals
+
+ ///
+ /// Online
+ ///
+ [JoinName("Online")]
+ public JoinDataComplete Online = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Force Occupied
+ ///
+ [JoinName("ForceOccupied")]
+ public JoinDataComplete ForceOccupied = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Force Occupied", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Force Vacant
+ ///
+ [JoinName("ForceVacant")]
+ public JoinDataComplete ForceVacant = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Force Vacant", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Enable Raw States
+ ///
+ [JoinName("EnableRawStates")]
+ public JoinDataComplete EnableRawStates = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "Enable Raw States", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Disable Raw States
+ ///
+ [JoinName("RoomOccupiedFeedback")]
+ public JoinDataComplete RoomOccupiedFeedback = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Room Occupied Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Grace Occupancy Detected Feedback
+ ///
+ [JoinName("GraceOccupancyDetectedFeedback")]
+ public JoinDataComplete GraceOccupancyDetectedFeedback = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Grace Occupancy Detected Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Room Vacant Feedback
+ ///
+ [JoinName("RoomVacantFeedback")]
+ public JoinDataComplete RoomVacantFeedback = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "Room Vacant Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Raw Occupancy Feedback
+ ///
+ [JoinName("RawOccupancyFeedback")]
+ public JoinDataComplete RawOccupancyFeedback = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "Raw Occupancy Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Raw Occupancy Pir Feedback
+ ///
+ [JoinName("RawOccupancyPirFeedback")]
+ public JoinDataComplete RawOccupancyPirFeedback = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
+ new JoinMetadata { Description = "Raw Occupancy Pir Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Raw Occupancy Us Feedback
+ ///
+ [JoinName("RawOccupancyUsFeedback")]
+ public JoinDataComplete RawOccupancyUsFeedback = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
+ new JoinMetadata { Description = "Raw Occupancy Us Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Identity Mode On
+ ///
+ [JoinName("IdentityModeOn")]
+ public JoinDataComplete IdentityMode = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
+ new JoinMetadata { Description = "Enable Identity Mode", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Identity Mode Off
+ ///
+ [JoinName("IdentityModeFeedback")]
+ public JoinDataComplete IdentityModeFeedback = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
+ new JoinMetadata { Description = "Identity Mode Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Enable Led Flash
+ ///
+ [JoinName("EnableLedFlash")]
+ public JoinDataComplete EnableLedFlash = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
+ new JoinMetadata { Description = "Enable Led Flash", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Disable Led Flash
+ ///
+ [JoinName("DisableLedFlash")]
+ public JoinDataComplete DisableLedFlash = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
+ new JoinMetadata { Description = "Disable Led Flash", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Enable Short Timeout
+ ///
+ [JoinName("EnableShortTimeout")]
+ public JoinDataComplete EnableShortTimeout = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
+ new JoinMetadata { Description = "Enable Short Timeout", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Disable Short Timeout
+ ///
+ [JoinName("DisableShortTimeout")]
+ public JoinDataComplete DisableShortTimeout = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
+ new JoinMetadata { Description = "Disable Short Timeout", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Or When Vacated
+ ///
+ [JoinName("OrWhenVacated")]
+ public JoinDataComplete OrWhenVacated = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
+ new JoinMetadata { Description = "Or When Vacated", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// And When Vacated
+ ///
+ [JoinName("AndWhenVacated")]
+ public JoinDataComplete AndWhenVacated = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 1 },
+ new JoinMetadata { Description = "AndWhenVacated", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Enable Us A
+ ///
+ [JoinName("EnableUsA")]
+ public JoinDataComplete EnableUsA = new JoinDataComplete(new JoinData { JoinNumber = 17, JoinSpan = 1 },
+ new JoinMetadata { Description = "Enable Us A", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Disable Us A
+ ///
+ [JoinName("DisableUsA")]
+ public JoinDataComplete DisableUsA = new JoinDataComplete(new JoinData { JoinNumber = 18, JoinSpan = 1 },
+ new JoinMetadata { Description = "Disable Us A", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Enable Us B
+ ///
+ [JoinName("EnableUsB")]
+ public JoinDataComplete EnableUsB = new JoinDataComplete(new JoinData { JoinNumber = 19, JoinSpan = 1 },
+ new JoinMetadata { Description = "Enable Us B", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Disable Us B
+ ///
+ [JoinName("DisableUsB")]
+ public JoinDataComplete DisableUsB = new JoinDataComplete(new JoinData { JoinNumber = 20, JoinSpan = 1 },
+ new JoinMetadata { Description = "Disable Us B", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Enable Pir
+ ///
+ [JoinName("EnablePir")]
+ public JoinDataComplete EnablePir = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },
+ new JoinMetadata { Description = "Enable Pir", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Disable Pir
+ ///
+ [JoinName("DisablePir")]
+ public JoinDataComplete DisablePir = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },
+ new JoinMetadata { Description = "Disable Pir", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Increment Us In Occupied State
+ ///
+ [JoinName("IncrementUsInOccupiedState")]
+ public JoinDataComplete IncrementUsInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },
+ new JoinMetadata { Description = "Increment Us In OccupiedState", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Decrement Us In Occupied State
+ ///
+ [JoinName("DecrementUsInOccupiedState")]
+ public JoinDataComplete DecrementUsInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },
+ new JoinMetadata { Description = "Dencrement Us In Occupied State", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Increment Us In Vacant State
+ ///
+ [JoinName("IncrementUsInVacantState")]
+ public JoinDataComplete IncrementUsInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },
+ new JoinMetadata { Description = "Increment Us In VacantState", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Decrement Us In Vacant State
+ ///
+ [JoinName("DecrementUsInVacantState")]
+ public JoinDataComplete DecrementUsInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 26, JoinSpan = 1 },
+ new JoinMetadata { Description = "Decrement Us In VacantState", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Increment Pir In Occupied State
+ ///
+ [JoinName("IncrementPirInOccupiedState")]
+ public JoinDataComplete IncrementPirInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 27, JoinSpan = 1 },
+ new JoinMetadata { Description = "Increment Pir In Occupied State", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Decrement Pir In Occupied State
+ ///
+ [JoinName("DecrementPirInOccupiedState")]
+ public JoinDataComplete DecrementPirInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 28, JoinSpan = 1 },
+ new JoinMetadata { Description = "Decrement Pir In OccupiedState", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Increment Pir In Vacant State
+ ///
+ [JoinName("IncrementPirInVacantState")]
+ public JoinDataComplete IncrementPirInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 29, JoinSpan = 1 },
+ new JoinMetadata { Description = "Increment Pir In Vacant State", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Decrement Pir In Vacant State
+ ///
+ [JoinName("DecrementPirInVacantState")]
+ public JoinDataComplete DecrementPirInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },
+ new JoinMetadata { Description = "Decrement Pir In Vacant State", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ #endregion
+
+ #region Analog
+ ///
+ /// Timeout
+ ///
+ [JoinName("Timeout")]
+ public JoinDataComplete Timeout = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Timeout", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Timeout Local Feedback
+ ///
+ [JoinName("TimeoutLocalFeedback")]
+ public JoinDataComplete TimeoutLocalFeedback = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Timeout Local Feedback", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Internal PhotoSensor Value
+ ///
+ [JoinName("InternalPhotoSensorValue")]
+ public JoinDataComplete InternalPhotoSensorValue = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Internal PhotoSensor Value", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// External PhotoSensor Value
+ ///
+ [JoinName("UsSensitivityInOccupiedState")]
+ public JoinDataComplete UsSensitivityInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "Us Sensitivity In Occupied State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Us Sensitivity In Vacant State
+ ///
+ [JoinName("UsSensitivityInVacantState")]
+ public JoinDataComplete UsSensitivityInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
+ new JoinMetadata { Description = "Us Sensitivity In Vacant State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Pir Sensitivity In Occupied State
+ ///
+ [JoinName("PirSensitivityInOccupiedState")]
+ public JoinDataComplete PirSensitivityInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
+ new JoinMetadata { Description = "Pir Sensitivity In Occupied State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Pir Sensitivity In Vacant State
+ ///
+ [JoinName("PirSensitivityInVacantState")]
+ public JoinDataComplete PirSensitivityInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
+ new JoinMetadata { Description = "Pir Sensitivity In Vacant State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ #endregion
+
+ #region Serial
+
+ ///
+ /// Name
+ ///
+ [JoinName("Name")]
+ public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ #endregion
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public CenOdtOccupancySensorBaseJoinMap(uint joinStart)
+ : this(joinStart, typeof(CenOdtOccupancySensorBaseJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected CenOdtOccupancySensorBaseJoinMap(uint joinStart, Type type) : base(joinStart, type)
+ {
+ }
}
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected CenOdtOccupancySensorBaseJoinMap(uint joinStart, Type type) : base(joinStart, type)
- {
- }
}
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DisplayControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DisplayControllerJoinMap.cs
index 2ab95b8e..3741f951 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DisplayControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DisplayControllerJoinMap.cs
@@ -1,84 +1,133 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class DisplayControllerJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("Name")]
- public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("PowerOff")]
- public JoinDataComplete PowerOff = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Power Off", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("PowerOn")]
- public JoinDataComplete PowerOn = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Power On", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("IsTwoWayDisplay")]
- public JoinDataComplete IsTwoWayDisplay = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Is Two Way Display", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("VolumeUp")]
- public JoinDataComplete VolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "Volume Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("VolumeLevel")]
- public JoinDataComplete VolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "Volume Level", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("VolumeDown")]
- public JoinDataComplete VolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
- new JoinMetadata { Description = "Volume Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("VolumeMute")]
- public JoinDataComplete VolumeMute = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
- new JoinMetadata { Description = "Volume Mute", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("VolumeMuteOn")]
- public JoinDataComplete VolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
- new JoinMetadata { Description = "Volume Mute On", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("VolumeMuteOff")]
- public JoinDataComplete VolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 },
- new JoinMetadata { Description = "Volume Mute Off", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("InputSelectOffset")]
- public JoinDataComplete InputSelectOffset = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 10 },
- new JoinMetadata { Description = "Input Select", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("InputNamesOffset")]
- public JoinDataComplete InputNamesOffset = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 10 },
- new JoinMetadata { Description = "Input Names Offset", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("InputSelect")]
- public JoinDataComplete InputSelect = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
- new JoinMetadata { Description = "Input Select", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("ButtonVisibilityOffset")]
- public JoinDataComplete ButtonVisibilityOffset = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 10 },
- new JoinMetadata { Description = "Button Visibility Offset", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.DigitalSerial });
-
- [JoinName("IsOnline")]
- public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1 },
- new JoinMetadata { Description = "Is Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a DisplayControllerJoinMap
///
- /// Join this join map will start at
- public DisplayControllerJoinMap(uint joinStart)
- : this(joinStart, typeof(DisplayControllerJoinMap))
+ public class DisplayControllerJoinMap : JoinMapBaseAdvanced
{
- }
+ ///
+ /// Name
+ ///
+ [JoinName("Name")]
+ public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Power Off
+ ///
+ [JoinName("PowerOff")]
+ public JoinDataComplete PowerOff = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Power Off", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Power On
+ ///
+ [JoinName("PowerOn")]
+ public JoinDataComplete PowerOn = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Power On", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Is Two Way Display
+ ///
+ [JoinName("IsTwoWayDisplay")]
+ public JoinDataComplete IsTwoWayDisplay = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Is Two Way Display", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Volume Up
+ ///
+ [JoinName("VolumeUp")]
+ public JoinDataComplete VolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "Volume Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Volume Level
+ ///
+ [JoinName("VolumeLevel")]
+ public JoinDataComplete VolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "Volume Level", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Volume Down
+ ///
+ [JoinName("VolumeDown")]
+ public JoinDataComplete VolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
+ new JoinMetadata { Description = "Volume Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Volume Mute
+ ///
+ [JoinName("VolumeMute")]
+ public JoinDataComplete VolumeMute = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
+ new JoinMetadata { Description = "Volume Mute", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Volume Mute On
+ ///
+ [JoinName("VolumeMuteOn")]
+ public JoinDataComplete VolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
+ new JoinMetadata { Description = "Volume Mute On", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Volume Mute Off
+ ///
+ [JoinName("VolumeMuteOff")]
+ public JoinDataComplete VolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 },
+ new JoinMetadata { Description = "Volume Mute Off", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Input Select Offset
+ ///
+ [JoinName("InputSelectOffset")]
+ public JoinDataComplete InputSelectOffset = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 10 },
+ new JoinMetadata { Description = "Input Select", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Input Names Offset
+ ///
+ [JoinName("InputNamesOffset")]
+ public JoinDataComplete InputNamesOffset = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 10 },
+ new JoinMetadata { Description = "Input Names Offset", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Input Select
+ ///
+ [JoinName("InputSelect")]
+ public JoinDataComplete InputSelect = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
+ new JoinMetadata { Description = "Input Select", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Button Visibility Offset
+ ///
+ [JoinName("ButtonVisibilityOffset")]
+ public JoinDataComplete ButtonVisibilityOffset = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 10 },
+ new JoinMetadata { Description = "Button Visibility Offset", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.DigitalSerial });
+
+ ///
+ /// Is Online
+ ///
+ [JoinName("IsOnline")]
+ public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1 },
+ new JoinMetadata { Description = "Is Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected DisplayControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)
- {
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public DisplayControllerJoinMap(uint joinStart)
+ : this(joinStart, typeof(DisplayControllerJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected DisplayControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmBladeChassisControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmBladeChassisControllerJoinMap.cs
index 3edd1222..c6488bba 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmBladeChassisControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmBladeChassisControllerJoinMap.cs
@@ -1,73 +1,113 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-public class DmBladeChassisControllerJoinMap : JoinMapBaseAdvanced {
-
- [JoinName("IsOnline")]
- public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
- new JoinMetadata { Description = "DM Blade Chassis Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("VideoSyncStatus")]
- public JoinDataComplete VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 128 },
- new JoinMetadata { Description = "DM Blade Input Video Sync", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("InputEndpointOnline")]
- public JoinDataComplete InputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 128 },
- new JoinMetadata { Description = "DM Blade Chassis Input Endpoint Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("OutputEndpointOnline")]
- public JoinDataComplete OutputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 128 },
- new JoinMetadata { Description = "DM Blade Chassis Output Endpoint Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("TxAdvancedIsPresent")]
- public JoinDataComplete TxAdvancedIsPresent = new JoinDataComplete(new JoinData { JoinNumber = 1001, JoinSpan = 128 },
- new JoinMetadata { Description = "DM Blade Chassis Tx Advanced Is Present", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("OutputVideo")]
- public JoinDataComplete OutputVideo = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 128 },
- new JoinMetadata { Description = "DM Blade Chassis Output Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("HdcpSupportState")]
- public JoinDataComplete HdcpSupportState = new JoinDataComplete(new JoinData { JoinNumber = 1001, JoinSpan = 128 },
- new JoinMetadata { Description = "DM Blade Chassis Input HDCP Support State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("HdcpSupportCapability")]
- public JoinDataComplete HdcpSupportCapability = new JoinDataComplete(new JoinData { JoinNumber = 1201, JoinSpan = 128 },
- new JoinMetadata { Description = "DM Blade Chassis Input HDCP Support Capability", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("InputNames")]
- public JoinDataComplete InputNames = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 128 },
- new JoinMetadata { Description = "DM Blade Chassis Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("OutputNames")]
- public JoinDataComplete OutputNames = new JoinDataComplete(new JoinData { JoinNumber = 301, JoinSpan = 128 },
- new JoinMetadata { Description = "DM Blade Chassis Output Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("OutputCurrentVideoInputNames")]
- public JoinDataComplete OutputCurrentVideoInputNames = new JoinDataComplete(new JoinData { JoinNumber = 2001, JoinSpan = 128 },
- new JoinMetadata { Description = "DM Blade Chassis Video Output Currently Routed Video Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("InputCurrentResolution")]
- public JoinDataComplete InputCurrentResolution = new JoinDataComplete(new JoinData { JoinNumber = 2401, JoinSpan = 128 },
- new JoinMetadata { Description = "DM Blade Chassis Input Current Resolution", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
-
+namespace PepperDash.Essentials.Core.Bridges {
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a DmBladeChassisControllerJoinMap
///
- /// Join this join map will start at
- public DmBladeChassisControllerJoinMap(uint joinStart)
- : this(joinStart, typeof(DmBladeChassisControllerJoinMap))
- {
- }
+ public class DmBladeChassisControllerJoinMap : JoinMapBaseAdvanced {
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected DmBladeChassisControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)
- {
- }
+ ///
+ /// DM Blade Chassis Online status
+ ///
+ [JoinName("IsOnline")]
+ public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM Blade Chassis Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM Blade Input Video Sync
+ ///
+ [JoinName("VideoSyncStatus")]
+ public JoinDataComplete VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 128 },
+ new JoinMetadata { Description = "DM Blade Input Video Sync", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM Blade Chassis Input Endpoint Online
+ ///
+ [JoinName("InputEndpointOnline")]
+ public JoinDataComplete InputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 128 },
+ new JoinMetadata { Description = "DM Blade Chassis Input Endpoint Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM Blade Chassis Output Endpoint Online
+ ///
+ [JoinName("OutputEndpointOnline")]
+ public JoinDataComplete OutputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 128 },
+ new JoinMetadata { Description = "DM Blade Chassis Output Endpoint Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM Blade Chassis Tx Advanced Is Present
+ ///
+ [JoinName("TxAdvancedIsPresent")]
+ public JoinDataComplete TxAdvancedIsPresent = new JoinDataComplete(new JoinData { JoinNumber = 1001, JoinSpan = 128 },
+ new JoinMetadata { Description = "DM Blade Chassis Tx Advanced Is Present", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM Blade Chassis Rx Advanced Is Present
+ ///
+ [JoinName("OutputVideo")]
+ public JoinDataComplete OutputVideo = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 128 },
+ new JoinMetadata { Description = "DM Blade Chassis Output Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM Blade Chassis Input HDCP Support State
+ ///
+ [JoinName("HdcpSupportState")]
+ public JoinDataComplete HdcpSupportState = new JoinDataComplete(new JoinData { JoinNumber = 1001, JoinSpan = 128 },
+ new JoinMetadata { Description = "DM Blade Chassis Input HDCP Support State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM Blade Chassis Input HDCP Support Capability
+ ///
+ [JoinName("HdcpSupportCapability")]
+ public JoinDataComplete HdcpSupportCapability = new JoinDataComplete(new JoinData { JoinNumber = 1201, JoinSpan = 128 },
+ new JoinMetadata { Description = "DM Blade Chassis Input HDCP Support Capability", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM Blade Chassis Input Names
+ ///
+ [JoinName("InputNames")]
+ public JoinDataComplete InputNames = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 128 },
+ new JoinMetadata { Description = "DM Blade Chassis Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM Blade Chassis Output Names
+ ///
+ [JoinName("OutputNames")]
+ public JoinDataComplete OutputNames = new JoinDataComplete(new JoinData { JoinNumber = 301, JoinSpan = 128 },
+ new JoinMetadata { Description = "DM Blade Chassis Output Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM Blade Chassis Video Output Currently Routed Video Input Name
+ ///
+ [JoinName("OutputCurrentVideoInputNames")]
+ public JoinDataComplete OutputCurrentVideoInputNames = new JoinDataComplete(new JoinData { JoinNumber = 2001, JoinSpan = 128 },
+ new JoinMetadata { Description = "DM Blade Chassis Video Output Currently Routed Video Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM Blade Chassis Input Current Resolution
+ ///
+ [JoinName("InputCurrentResolution")]
+ public JoinDataComplete InputCurrentResolution = new JoinDataComplete(new JoinData { JoinNumber = 2401, JoinSpan = 128 },
+ new JoinMetadata { Description = "DM Blade Chassis Input Current Resolution", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public DmBladeChassisControllerJoinMap(uint joinStart)
+ : this(joinStart, typeof(DmBladeChassisControllerJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected DmBladeChassisControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)
+ {
+ }
+
+ }
}
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmChassisControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmChassisControllerJoinMap.cs
index db3701b3..9d2217cc 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmChassisControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmChassisControllerJoinMap.cs
@@ -1,169 +1,259 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class DmChassisControllerJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("EnableAudioBreakaway")]
- public JoinDataComplete EnableAudioBreakaway = new JoinDataComplete(
- new JoinData {JoinNumber = 4, JoinSpan = 1},
- new JoinMetadata
- {
- Description = "DM Chassis enable audio breakaway routing",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
-
- [JoinName("EnableUsbBreakaway")]
- public JoinDataComplete EnableUsbBreakaway = new JoinDataComplete(
- new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata
- {
- Description = "DM Chassis enable USB breakaway routing",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
-
- [JoinName("Name")]
- public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "DM Chassis Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("SystemId")]
- public JoinDataComplete SystemId = new JoinDataComplete(new JoinData { JoinNumber = 10, JoinSpan = 1 },
- new JoinMetadata { Description = "DM Chassis SystemId Get/Set/Trigger/", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.DigitalAnalog });
-
- [JoinName("IsOnline")]
- public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
- new JoinMetadata { Description = "DM Chassis Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("VideoSyncStatus")]
- public JoinDataComplete VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Input Video Sync", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("InputEndpointOnline")]
- public JoinDataComplete InputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Input Endpoint Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("OutputEndpointOnline")]
- public JoinDataComplete OutputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Output Endpoint Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("TxAdvancedIsPresent")]
- public JoinDataComplete TxAdvancedIsPresent = new JoinDataComplete(new JoinData { JoinNumber = 1001, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Tx Advanced Is Present", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("OutputDisabledByHdcp")]
- public JoinDataComplete OutputDisabledByHdcp = new JoinDataComplete(new JoinData { JoinNumber = 1201, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Output Disabled by HDCP", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("OutputVideo")]
- public JoinDataComplete OutputVideo = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Output Video Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("OutputAudio")]
- public JoinDataComplete OutputAudio = new JoinDataComplete(new JoinData { JoinNumber = 301, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Output Audio Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("OutputUsb")]
- public JoinDataComplete OutputUsb = new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Output USB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("InputUsb")]
- public JoinDataComplete InputUsb = new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Input Usb Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("HdcpSupportState")]
- public JoinDataComplete HdcpSupportState = new JoinDataComplete(new JoinData { JoinNumber = 1001, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Input HDCP Support State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("HdcpSupportCapability")]
- public JoinDataComplete HdcpSupportCapability = new JoinDataComplete(new JoinData { JoinNumber = 1201, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Input HDCP Support Capability", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("InputStreamCardState")]
- public JoinDataComplete InputStreamCardState = new JoinDataComplete(new JoinData { JoinNumber = 1501, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Stream Input Start (1), Stop (2), Pause (3) with Feedback", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("OutputStreamCardState")]
- public JoinDataComplete OutputStreamCardState = new JoinDataComplete(new JoinData { JoinNumber = 1601, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Stream Output Start (1), Stop (2), Pause (3) with Feedback", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("NoRouteName")]
- public JoinDataComplete NoRouteName = new JoinDataComplete(new JoinData { JoinNumber = 100, JoinSpan = 1 },
- new JoinMetadata { Description = "DM Chassis Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("InputNames")]
- public JoinDataComplete InputNames = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("OutputNames")]
- public JoinDataComplete OutputNames = new JoinDataComplete(new JoinData { JoinNumber = 301, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Output Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("InputVideoNames")] public JoinDataComplete InputVideoNames =
- new JoinDataComplete(new JoinData {JoinNumber = 501, JoinSpan = 200},
+ ///
+ /// Represents a DmChassisControllerJoinMap
+ ///
+ public class DmChassisControllerJoinMap : JoinMapBaseAdvanced
+ {
+ ///
+ /// DM Chassis enable audio breakaway routing
+ ///
+ [JoinName("EnableAudioBreakaway")]
+ public JoinDataComplete EnableAudioBreakaway = new JoinDataComplete(
+ new JoinData {JoinNumber = 4, JoinSpan = 1},
new JoinMetadata
{
- Description = "DM Chassis Video Input Names",
+ Description = "DM Chassis enable audio breakaway routing",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ ///
+ /// DM Chassis enable USB breakaway routing
+ ///
+ [JoinName("EnableUsbBreakaway")]
+ public JoinDataComplete EnableUsbBreakaway = new JoinDataComplete(
+ new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata
+ {
+ Description = "DM Chassis enable USB breakaway routing",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ ///
+ /// DM Chassis Name
+ ///
+ [JoinName("Name")]
+ public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM Chassis Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM Chassis SystemId Get/Set/Trigger
+ ///
+ [JoinName("SystemId")]
+ public JoinDataComplete SystemId = new JoinDataComplete(new JoinData { JoinNumber = 10, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM Chassis SystemId Get/Set/Trigger/", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.DigitalAnalog });
+
+ ///
+ /// DM Chassis Online status
+ ///
+ [JoinName("IsOnline")]
+ public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM Chassis Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM Input Video Sync
+ ///
+ [JoinName("VideoSyncStatus")]
+ public JoinDataComplete VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Input Video Sync", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM Chassis Input Endpoint Online
+ ///
+ [JoinName("InputEndpointOnline")]
+ public JoinDataComplete InputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Input Endpoint Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM Chassis Output Endpoint Online
+ ///
+ [JoinName("OutputEndpointOnline")]
+ public JoinDataComplete OutputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Output Endpoint Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM Chassis Tx Advanced Is Present
+ ///
+ [JoinName("TxAdvancedIsPresent")]
+ public JoinDataComplete TxAdvancedIsPresent = new JoinDataComplete(new JoinData { JoinNumber = 1001, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Tx Advanced Is Present", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM Chassis Rx Advanced Is Present
+ ///
+ [JoinName("OutputDisabledByHdcp")]
+ public JoinDataComplete OutputDisabledByHdcp = new JoinDataComplete(new JoinData { JoinNumber = 1201, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Output Disabled by HDCP", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM Chassis Output Set / Get
+ ///
+ [JoinName("OutputVideo")]
+ public JoinDataComplete OutputVideo = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Output Video Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM Chassis Output Audio Set / Get
+ ///
+ [JoinName("OutputAudio")]
+ public JoinDataComplete OutputAudio = new JoinDataComplete(new JoinData { JoinNumber = 301, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Output Audio Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM Chassis Input Set / Get
+ ///
+ [JoinName("OutputUsb")]
+ public JoinDataComplete OutputUsb = new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Output USB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM Chassis Input Set / Get
+ ///
+ [JoinName("InputUsb")]
+ public JoinDataComplete InputUsb = new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Input Usb Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM Chassis Input HDCP Support State
+ ///
+ [JoinName("HdcpSupportState")]
+ public JoinDataComplete HdcpSupportState = new JoinDataComplete(new JoinData { JoinNumber = 1001, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Input HDCP Support State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM Chassis Input HDCP Support Capability
+ ///
+ [JoinName("HdcpSupportCapability")]
+ public JoinDataComplete HdcpSupportCapability = new JoinDataComplete(new JoinData { JoinNumber = 1201, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Input HDCP Support Capability", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM Chassis Stream Input Start (1), Stop (2), Pause (3) with Feedback
+ ///
+ [JoinName("InputStreamCardState")]
+ public JoinDataComplete InputStreamCardState = new JoinDataComplete(new JoinData { JoinNumber = 1501, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Stream Input Start (1), Stop (2), Pause (3) with Feedback", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM Chassis Stream Output Start (1), Stop (2), Pause (3) with Feedback
+ ///
+ [JoinName("OutputStreamCardState")]
+ public JoinDataComplete OutputStreamCardState = new JoinDataComplete(new JoinData { JoinNumber = 1601, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Stream Output Start (1), Stop (2), Pause (3) with Feedback", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM Chassis No Route Name
+ ///
+ [JoinName("NoRouteName")]
+ public JoinDataComplete NoRouteName = new JoinDataComplete(new JoinData { JoinNumber = 100, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM Chassis Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM Chassis Input Names
+ ///
+ [JoinName("InputNames")]
+ public JoinDataComplete InputNames = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM Chassis Output Names
+ ///
+ [JoinName("OutputNames")]
+ public JoinDataComplete OutputNames = new JoinDataComplete(new JoinData { JoinNumber = 301, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Output Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM Chassis Video Input Names
+ ///
+ [JoinName("InputVideoNames")] public JoinDataComplete InputVideoNames =
+ new JoinDataComplete(new JoinData {JoinNumber = 501, JoinSpan = 200},
+ new JoinMetadata
+ {
+ Description = "DM Chassis Video Input Names",
+ JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ ///
+ /// DM Chassis Audio Input Names
+ ///
+ [JoinName("InputAudioNames")]
+ public JoinDataComplete InputAudioNames =
+ new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 200 },
+ new JoinMetadata
+ {
+ Description = "DM Chassis Audio Input Names",
JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
JoinType = eJoinType.Serial
});
+
+ ///
+ /// DM Chassis Video Output Names
+ ///
+ [JoinName("OutputVideoNames")]
+ public JoinDataComplete OutputVideoNames =
+ new JoinDataComplete(new JoinData { JoinNumber = 901, JoinSpan = 200 },
+ new JoinMetadata
+ {
+ Description = "DM Chassis Video Output Names",
+ JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ ///
+ /// DM Chassis Audio Output Names
+ ///
+ [JoinName("OutputAudioNames")]
+ public JoinDataComplete OutputAudioNames =
+ new JoinDataComplete(new JoinData { JoinNumber = 1101, JoinSpan = 200 },
+ new JoinMetadata
+ {
+ Description = "DM Chassis Audio Output Names",
+ JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ ///
+ /// DM Chassis Video Output Currently Routed Video Input Name
+ ///
+ [JoinName("OutputCurrentVideoInputNames")]
+ public JoinDataComplete OutputCurrentVideoInputNames = new JoinDataComplete(new JoinData { JoinNumber = 2001, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Video Output Currently Routed Video Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM Chassis Audio Output Currently Routed Audio Input Name
+ ///
+ [JoinName("OutputCurrentAudioInputNames")]
+ public JoinDataComplete OutputCurrentAudioInputNames = new JoinDataComplete(new JoinData { JoinNumber = 2201, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Audio Output Currently Routed Video Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM Chassis Input Current Resolution
+ ///
+ [JoinName("InputCurrentResolution")]
+ public JoinDataComplete InputCurrentResolution = new JoinDataComplete(new JoinData { JoinNumber = 2401, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Input Current Resolution", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("InputAudioNames")]
- public JoinDataComplete InputAudioNames =
- new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 200 },
- new JoinMetadata
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public DmChassisControllerJoinMap(uint joinStart)
+ : this(joinStart, typeof(DmChassisControllerJoinMap))
{
- Description = "DM Chassis Audio Input Names",
- JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
- JoinType = eJoinType.Serial
- });
- [JoinName("OutputVideoNames")]
- public JoinDataComplete OutputVideoNames =
- new JoinDataComplete(new JoinData { JoinNumber = 901, JoinSpan = 200 },
- new JoinMetadata
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected DmChassisControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)
{
- Description = "DM Chassis Video Output Names",
- JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
- JoinType = eJoinType.Serial
- });
- [JoinName("OutputAudioNames")]
- public JoinDataComplete OutputAudioNames =
- new JoinDataComplete(new JoinData { JoinNumber = 1101, JoinSpan = 200 },
- new JoinMetadata
- {
- Description = "DM Chassis Audio Output Names",
- JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
- JoinType = eJoinType.Serial
- });
-
- [JoinName("OutputCurrentVideoInputNames")]
- public JoinDataComplete OutputCurrentVideoInputNames = new JoinDataComplete(new JoinData { JoinNumber = 2001, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Video Output Currently Routed Video Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("OutputCurrentAudioInputNames")]
- public JoinDataComplete OutputCurrentAudioInputNames = new JoinDataComplete(new JoinData { JoinNumber = 2201, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Audio Output Currently Routed Video Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("InputCurrentResolution")]
- public JoinDataComplete InputCurrentResolution = new JoinDataComplete(new JoinData { JoinNumber = 2401, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Input Current Resolution", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- ///
- /// Constructor to use when instantiating this Join Map without inheriting from it
- ///
- /// Join this join map will start at
- public DmChassisControllerJoinMap(uint joinStart)
- : this(joinStart, typeof(DmChassisControllerJoinMap))
- {
- }
-
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected DmChassisControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)
- {
+ }
}
}
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmRmcControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmRmcControllerJoinMap.cs
index 4ddb9ff4..6f0df102 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmRmcControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmRmcControllerJoinMap.cs
@@ -1,91 +1,143 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class DmRmcControllerJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("IsOnline")]
- public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "DM RMC Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("VideoMuteOn")]
- public JoinDataComplete VideoMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "DM RMC Mute Video", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("VideoMuteOff")]
- public JoinDataComplete VideoMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "DM RMC UnMute Video", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("VideoMuteToggle")]
- public JoinDataComplete VideoMuteToggle = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "DM RMC Mute Video Toggle", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("CurrentOutputResolution")]
- public JoinDataComplete CurrentOutputResolution = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "DM RMC Current Output Resolution", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("EdidManufacturer")]
- public JoinDataComplete EdidManufacturer = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "DM RMC EDID Manufacturer", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("EdidName")]
- public JoinDataComplete EdidName = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "DM RMC EDID Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("EdidPrefferedTiming")]
- public JoinDataComplete EdidPrefferedTiming = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "DM RMC EDID Preferred Timing", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("EdidSerialNumber")]
- public JoinDataComplete EdidSerialNumber = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "DM RMC EDID Serial Number", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("Name")]
- public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
- new JoinMetadata { Description = "DM RMC Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("AudioVideoSource")]
- public JoinDataComplete AudioVideoSource = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "DM RMC Audio Video Source Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("HdcpSupportCapability")]
- public JoinDataComplete HdcpSupportCapability = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "DM RMC HDCP Support Capability", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("Port1HdcpState")]
- public JoinDataComplete Port1HdcpState = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "DM RMC Port 1 (DM) HDCP State Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("Port2HdcpState")]
- public JoinDataComplete Port2HdcpState = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "DM TX Port 2 (HDMI) HDCP State Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("HdmiInputSync")]
- public JoinDataComplete HdmiInputSync = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "DM RMC HDMI Input Sync", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("HdcpInputPortCount")]
- public JoinDataComplete HdcpInputPortCount = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "Number of Input Ports that support HDCP", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
-
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a DmRmcControllerJoinMap
///
- /// Join this join map will start at
- public DmRmcControllerJoinMap(uint joinStart)
- : this(joinStart, typeof(DmRmcControllerJoinMap))
+ public class DmRmcControllerJoinMap : JoinMapBaseAdvanced
{
- }
+ ///
+ /// DM RMC Online
+ ///
+ [JoinName("IsOnline")]
+ public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM RMC Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM RMC Mute Video
+ ///
+ [JoinName("VideoMuteOn")]
+ public JoinDataComplete VideoMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM RMC Mute Video", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM RMC UnMute Video
+ ///
+ [JoinName("VideoMuteOff")]
+ public JoinDataComplete VideoMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM RMC UnMute Video", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM RMC Mute Video Toggle
+ ///
+ [JoinName("VideoMuteToggle")]
+ public JoinDataComplete VideoMuteToggle = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM RMC Mute Video Toggle", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM RMC Current Output Resolution
+ ///
+ [JoinName("CurrentOutputResolution")]
+ public JoinDataComplete CurrentOutputResolution = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM RMC Current Output Resolution", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM RMC EDID Manufacturer
+ ///
+ [JoinName("EdidManufacturer")]
+ public JoinDataComplete EdidManufacturer = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM RMC EDID Manufacturer", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM RMC EDID Name
+ ///
+ [JoinName("EdidName")]
+ public JoinDataComplete EdidName = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM RMC EDID Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM RMC EDID Preferred Timing
+ ///
+ [JoinName("EdidPrefferedTiming")]
+ public JoinDataComplete EdidPrefferedTiming = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM RMC EDID Preferred Timing", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM RMC EDID Serial Number
+ ///
+ [JoinName("EdidSerialNumber")]
+ public JoinDataComplete EdidSerialNumber = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM RMC EDID Serial Number", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM RMC Name
+ ///
+ [JoinName("Name")]
+ public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM RMC Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM RMC Audio Video Source Set / Get
+ ///
+ [JoinName("AudioVideoSource")]
+ public JoinDataComplete AudioVideoSource = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM RMC Audio Video Source Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM RMC HDCP Support Capability
+ ///
+ [JoinName("HdcpSupportCapability")]
+ public JoinDataComplete HdcpSupportCapability = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM RMC HDCP Support Capability", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM RMC Port 1 (DM) HDCP State Set / Get
+ ///
+ [JoinName("Port1HdcpState")]
+ public JoinDataComplete Port1HdcpState = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM RMC Port 1 (DM) HDCP State Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM RMC Port 2 (HDMI) HDCP State Set / Get
+ ///
+ [JoinName("Port2HdcpState")]
+ public JoinDataComplete Port2HdcpState = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM RMC Port 2 (HDMI) HDCP State Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM RMC HDMI Input Sync
+ ///
+ [JoinName("HdmiInputSync")]
+ public JoinDataComplete HdmiInputSync = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM RMC HDMI Input Sync", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM RMC Number of Input Ports that support HDCP
+ ///
+ [JoinName("HdcpInputPortCount")]
+ public JoinDataComplete HdcpInputPortCount = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "Number of Input Ports that support HDCP", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected DmRmcControllerJoinMap(uint joinStart, Type type)
- : base(joinStart, type)
- {
+
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public DmRmcControllerJoinMap(uint joinStart)
+ : this(joinStart, typeof(DmRmcControllerJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected DmRmcControllerJoinMap(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmTxControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmTxControllerJoinMap.cs
index 8fb99f33..ab1299d1 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmTxControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmTxControllerJoinMap.cs
@@ -1,95 +1,150 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class DmTxControllerJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("IsOnline")]
- public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "DM TX Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("VideoSyncStatus")]
- public JoinDataComplete VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "DM TX Video Sync", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("FreeRunEnabled")]
- public JoinDataComplete FreeRunEnabled = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "DM TX Enable Free Run Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Input1VideoSyncStatus")]
- public JoinDataComplete Input1VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "Input 1 Video Sync Status", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Input2VideoSyncStatus")]
- public JoinDataComplete Input2VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "Input 2 Video Sync Status", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Input3VideoSyncStatus")]
- public JoinDataComplete Input3VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
- new JoinMetadata { Description = "Input 3 Video Sync Status", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("CurrentInputResolution")]
- public JoinDataComplete CurrentInputResolution = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "DM TX Current Input Resolution", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("Name")]
- public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "DM TX Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("VideoInput")]
- public JoinDataComplete VideoInput = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "DM TX Video Input Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("AudioInput")]
- public JoinDataComplete AudioInput = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "DM TX Audio Input Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("HdcpSupportCapability")]
- public JoinDataComplete HdcpSupportCapability = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "DM TX HDCP Support Capability", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("Port1HdcpState")]
- public JoinDataComplete Port1HdcpState = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "DM TX Port 1 HDCP State Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("Port2HdcpState")]
- public JoinDataComplete Port2HdcpState = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "DM TX Port 2 HDCP State Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("VgaBrightness")]
- public JoinDataComplete VgaBrightness = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
- new JoinMetadata { Description = "DM TX VGA Brightness", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("VgaContrast")]
- public JoinDataComplete VgaContrast = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
- new JoinMetadata { Description = "DM TX Online", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("Port3HdcpState")]
- public JoinDataComplete Port3HdcpState = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
- new JoinMetadata { Description = "DM TX Port 3 HDCP State Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("HdcpInputPortCount")]
- public JoinDataComplete HdcpInputPortCount = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 },
- new JoinMetadata { Description = "Number of Input Ports that support HDCP", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
-
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a DmTxControllerJoinMap
///
- /// Join this join map will start at
- public DmTxControllerJoinMap(uint joinStart)
- : this(joinStart, typeof(DmTxControllerJoinMap))
+ public class DmTxControllerJoinMap : JoinMapBaseAdvanced
{
- }
+ ///
+ /// DM TX Online
+ ///
+ [JoinName("IsOnline")]
+ public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM TX Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM TX Video Sync
+ ///
+ [JoinName("VideoSyncStatus")]
+ public JoinDataComplete VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM TX Video Sync", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM TX Enable Free Run Set / Get
+ ///
+ [JoinName("FreeRunEnabled")]
+ public JoinDataComplete FreeRunEnabled = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM TX Enable Free Run Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Input 1 Video Sync Status
+ ///
+ [JoinName("Input1VideoSyncStatus")]
+ public JoinDataComplete Input1VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "Input 1 Video Sync Status", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Input 2 Video Sync Status
+ ///
+ [JoinName("Input2VideoSyncStatus")]
+ public JoinDataComplete Input2VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "Input 2 Video Sync Status", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Input 3 Video Sync Status
+ ///
+ [JoinName("Input3VideoSyncStatus")]
+ public JoinDataComplete Input3VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
+ new JoinMetadata { Description = "Input 3 Video Sync Status", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM TX Current Input Resolution
+ ///
+ [JoinName("CurrentInputResolution")]
+ public JoinDataComplete CurrentInputResolution = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM TX Current Input Resolution", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM TX Name
+ ///
+ [JoinName("Name")]
+ public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM TX Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM TX Video Input Set / Get
+ ///
+ [JoinName("VideoInput")]
+ public JoinDataComplete VideoInput = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM TX Video Input Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM TX Audio Input Set / Get
+ ///
+ [JoinName("AudioInput")]
+ public JoinDataComplete AudioInput = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM TX Audio Input Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM TX HDCP Support Capability
+ ///
+ [JoinName("HdcpSupportCapability")]
+ public JoinDataComplete HdcpSupportCapability = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM TX HDCP Support Capability", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM TX Port 1 HDCP State Set / Get
+ ///
+ [JoinName("Port1HdcpState")]
+ public JoinDataComplete Port1HdcpState = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM TX Port 1 HDCP State Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM TX Port 2 HDCP State Set / Get
+ ///
+ [JoinName("Port2HdcpState")]
+ public JoinDataComplete Port2HdcpState = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM TX Port 2 HDCP State Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM TX VGA Brightness
+ ///
+ [JoinName("VgaBrightness")]
+ public JoinDataComplete VgaBrightness = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM TX VGA Brightness", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM TX VGA Contrast
+ ///
+ [JoinName("VgaContrast")]
+ public JoinDataComplete VgaContrast = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM TX Online", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM TX Port 3 HDCP State Set / Get
+ ///
+ [JoinName("Port3HdcpState")]
+ public JoinDataComplete Port3HdcpState = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
+ new JoinMetadata { Description = "DM TX Port 3 HDCP State Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM TX Number of Input Ports that support HDCP
+ ///
+ [JoinName("HdcpInputPortCount")]
+ public JoinDataComplete HdcpInputPortCount = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 },
+ new JoinMetadata { Description = "Number of Input Ports that support HDCP", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected DmTxControllerJoinMap(uint joinStart, Type type)
- : base(joinStart, type)
- {
+
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public DmTxControllerJoinMap(uint joinStart)
+ : this(joinStart, typeof(DmTxControllerJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected DmTxControllerJoinMap(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmpsAudioOutputControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmpsAudioOutputControllerJoinMap.cs
index 9ad04691..ca1d60b4 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmpsAudioOutputControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmpsAudioOutputControllerJoinMap.cs
@@ -1,173 +1,288 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class DmpsAudioOutputControllerJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
-
- [JoinName("MasterVolumeLevel")]
- public JoinDataComplete MasterVolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Master Volume Signed dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("MasterVolumeLevelScaled")]
- public JoinDataComplete MasterVolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Master Volume 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("MixerPresetRecall")]
- public JoinDataComplete MixerPresetRecall = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Mixer Preset Recall Set", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("MixerEqPresetRecall")]
- public JoinDataComplete MixerEqPresetRecall = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "Mixer Eq Preset Recall Set", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("MasterVolumeMuteOn")]
- public JoinDataComplete MasterVolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Master Volume Mute On Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("MasterVolumeMuteOff")]
- public JoinDataComplete MasterVolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Master Volume Mute Off Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("MasterVolumeUp")]
- public JoinDataComplete MasterVolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Master Volume Level Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("MasterVolumeDown")]
- public JoinDataComplete MasterVolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "Master Volume Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("MasterVolumeLevelScaledSend")]
- public JoinDataComplete MasterVolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "Master Volume Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("SourceVolumeLevel")]
- public JoinDataComplete SourceVolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
- new JoinMetadata { Description = "Source Volume Signed dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("SourceVolumeLevelScaled")]
- public JoinDataComplete SourceVolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
- new JoinMetadata { Description = "Source Volume 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("SourceVolumeMuteOn")]
- public JoinDataComplete SourceVolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
- new JoinMetadata { Description = "Source Volume Mute On Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("SourceVolumeMuteOff")]
- public JoinDataComplete SourceVolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
- new JoinMetadata { Description = "Source Volume Mute Off Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("SourceVolumeUp")]
- public JoinDataComplete SourceVolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
- new JoinMetadata { Description = "Source Volume Level Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("SourceVolumeDown")]
- public JoinDataComplete SourceVolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
- new JoinMetadata { Description = "Source Volume Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("SourceVolumeLevelScaledSend")]
- public JoinDataComplete SourceVolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
- new JoinMetadata { Description = "Source Volume Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Codec1VolumeLevel")]
- public JoinDataComplete Codec1VolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },
- new JoinMetadata { Description = "Codec1 Volume Signed dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("Codec1VolumeLevelScaled")]
- public JoinDataComplete Codec1VolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },
- new JoinMetadata { Description = "Codec1 Volume 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("Codec1VolumeMuteOn")]
- public JoinDataComplete Codec1VolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },
- new JoinMetadata { Description = "Codec1 Volume Mute On Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Codec1VolumeMuteOff")]
- public JoinDataComplete Codec1VolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },
- new JoinMetadata { Description = "Codec1 Volume Mute Off Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Codec1VolumeUp")]
- public JoinDataComplete Codec1VolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },
- new JoinMetadata { Description = "Codec1 Volume Level Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Codec1VolumeDown")]
- public JoinDataComplete Codec1VolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },
- new JoinMetadata { Description = "Codec1 Volume Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Codec1VolumeLevelScaledSend")]
- public JoinDataComplete Codec1VolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },
- new JoinMetadata { Description = "Codec1 Volume Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Codec2VolumeLevel")]
- public JoinDataComplete Codec2VolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 1 },
- new JoinMetadata { Description = "Codec2 Volume Signed dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("Codec2VolumeLevelScaled")]
- public JoinDataComplete Codec2VolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 32, JoinSpan = 1 },
- new JoinMetadata { Description = "Codec2 Volume 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("Codec2VolumeMuteOn")]
- public JoinDataComplete Codec2VolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 1 },
- new JoinMetadata { Description = "Codec2 Volume Mute On Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Codec2VolumeMuteOff")]
- public JoinDataComplete Codec2VolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 32, JoinSpan = 1 },
- new JoinMetadata { Description = "Codec2 Volume Mute Off Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Codec2VolumeUp")]
- public JoinDataComplete Codec2VolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 33, JoinSpan = 1 },
- new JoinMetadata { Description = "Codec2 Volume Level Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Codec2VolumeDown")]
- public JoinDataComplete Codec2VolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 34, JoinSpan = 1 },
- new JoinMetadata { Description = "Codec2 Volume Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Codec2VolumeLevelScaledSend")]
- public JoinDataComplete Codec2VolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 35, JoinSpan = 1 },
- new JoinMetadata { Description = "Codec2 Volume Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("MicsMasterVolumeLevel")]
- public JoinDataComplete MicsMasterVolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 },
- new JoinMetadata { Description = "MicsMaster Volume Signed dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("MicsMasterVolumeLevelScaled")]
- public JoinDataComplete MicsMasterVolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },
- new JoinMetadata { Description = "MicsMaster Volume 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("MicsMasterVolumeMuteOn")]
- public JoinDataComplete MicsMasterVolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 },
- new JoinMetadata { Description = "MicsMaster Volume Mute On Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("MicsMasterVolumeMuteOff")]
- public JoinDataComplete MicsMasterVolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },
- new JoinMetadata { Description = "MicsMaster Volume Mute Off Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("MicsMasterVolumeUp")]
- public JoinDataComplete MicsMasterVolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },
- new JoinMetadata { Description = "MicsMaster Volume Level Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("MicsMasterVolumeDown")]
- public JoinDataComplete MicsMasterVolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 44, JoinSpan = 1 },
- new JoinMetadata { Description = "MicsMaster Volume Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("MicsMasterVolumeLevelScaledSend")]
- public JoinDataComplete MicsMasterVolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 45, JoinSpan = 1 },
- new JoinMetadata { Description = "Mics Master Volume Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a DmpsAudioOutputControllerJoinMap
///
- /// Join this join map will start at
- public DmpsAudioOutputControllerJoinMap(uint joinStart)
- : this(joinStart, typeof(DmpsAudioOutputControllerJoinMap))
+ public class DmpsAudioOutputControllerJoinMap : JoinMapBaseAdvanced
{
- }
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected DmpsAudioOutputControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)
- {
+ ///
+ /// Master Volume Level Signed dB Set / Get
+ ///
+ [JoinName("MasterVolumeLevel")]
+ public JoinDataComplete MasterVolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Master Volume Signed dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Master Volume 16bit Scaled Set / Get
+ ///
+ [JoinName("MasterVolumeLevelScaled")]
+ public JoinDataComplete MasterVolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Master Volume 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Mixer Preset Recall Set
+ ///
+ [JoinName("MixerPresetRecall")]
+ public JoinDataComplete MixerPresetRecall = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Mixer Preset Recall Set", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Mixer Eq Preset Recall Set
+ ///
+ [JoinName("MixerEqPresetRecall")]
+ public JoinDataComplete MixerEqPresetRecall = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "Mixer Eq Preset Recall Set", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Master Volume Mute On Set / Get
+ ///
+ [JoinName("MasterVolumeMuteOn")]
+ public JoinDataComplete MasterVolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Master Volume Mute On Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Master Volume Mute Off Set / Get
+ ///
+ [JoinName("MasterVolumeMuteOff")]
+ public JoinDataComplete MasterVolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Master Volume Mute Off Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Master Volume Level Up
+ ///
+ [JoinName("MasterVolumeUp")]
+ public JoinDataComplete MasterVolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Master Volume Level Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Master Volume Level Down
+ ///
+ [JoinName("MasterVolumeDown")]
+ public JoinDataComplete MasterVolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "Master Volume Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Master Volume Scaled Send Enable/Disable
+ ///
+ [JoinName("MasterVolumeLevelScaledSend")]
+ public JoinDataComplete MasterVolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "Master Volume Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Source Volume Signed dB Set / Get
+ ///
+ [JoinName("SourceVolumeLevel")]
+ public JoinDataComplete SourceVolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
+ new JoinMetadata { Description = "Source Volume Signed dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Source Volume 16bit Scaled Set / Get
+ ///
+ [JoinName("SourceVolumeLevelScaled")]
+ public JoinDataComplete SourceVolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
+ new JoinMetadata { Description = "Source Volume 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Source Volume Mute On Set / Get
+ ///
+ [JoinName("SourceVolumeMuteOn")]
+ public JoinDataComplete SourceVolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
+ new JoinMetadata { Description = "Source Volume Mute On Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Source Volume Mute Off Set / Get
+ ///
+ [JoinName("SourceVolumeMuteOff")]
+ public JoinDataComplete SourceVolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
+ new JoinMetadata { Description = "Source Volume Mute Off Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Source Volume Level Up
+ ///
+ [JoinName("SourceVolumeUp")]
+ public JoinDataComplete SourceVolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
+ new JoinMetadata { Description = "Source Volume Level Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Source Volume Level Down
+ ///
+ [JoinName("SourceVolumeDown")]
+ public JoinDataComplete SourceVolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
+ new JoinMetadata { Description = "Source Volume Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Source Volume Scaled Send Enable/Disable
+ ///
+ [JoinName("SourceVolumeLevelScaledSend")]
+ public JoinDataComplete SourceVolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
+ new JoinMetadata { Description = "Source Volume Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Codec1 Volume Signed dB Set / Get
+ ///
+ [JoinName("Codec1VolumeLevel")]
+ public JoinDataComplete Codec1VolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },
+ new JoinMetadata { Description = "Codec1 Volume Signed dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Codec1 Volume 16bit Scaled Set / Get
+ ///
+ [JoinName("Codec1VolumeLevelScaled")]
+ public JoinDataComplete Codec1VolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },
+ new JoinMetadata { Description = "Codec1 Volume 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Codec1 Volume Mute On Set / Get
+ ///
+ [JoinName("Codec1VolumeMuteOn")]
+ public JoinDataComplete Codec1VolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },
+ new JoinMetadata { Description = "Codec1 Volume Mute On Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Codec1 Volume Mute Off Set / Get
+ ///
+ [JoinName("Codec1VolumeMuteOff")]
+ public JoinDataComplete Codec1VolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },
+ new JoinMetadata { Description = "Codec1 Volume Mute Off Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Codec1 Volume Level Up
+ ///
+ [JoinName("Codec1VolumeUp")]
+ public JoinDataComplete Codec1VolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },
+ new JoinMetadata { Description = "Codec1 Volume Level Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Codec1 Volume Level Down
+ ///
+ [JoinName("Codec1VolumeDown")]
+ public JoinDataComplete Codec1VolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },
+ new JoinMetadata { Description = "Codec1 Volume Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Codec1 Volume Scaled Send Enable/Disable
+ ///
+ [JoinName("Codec1VolumeLevelScaledSend")]
+ public JoinDataComplete Codec1VolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },
+ new JoinMetadata { Description = "Codec1 Volume Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Codec2 Volume Signed dB Set / Get
+ ///
+ [JoinName("Codec2VolumeLevel")]
+ public JoinDataComplete Codec2VolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 1 },
+ new JoinMetadata { Description = "Codec2 Volume Signed dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Codec2 Volume 16bit Scaled Set / Get
+ ///
+ [JoinName("Codec2VolumeLevelScaled")]
+ public JoinDataComplete Codec2VolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 32, JoinSpan = 1 },
+ new JoinMetadata { Description = "Codec2 Volume 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Codec2 Volume Mute On Set / Get
+ ///
+ [JoinName("Codec2VolumeMuteOn")]
+ public JoinDataComplete Codec2VolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 1 },
+ new JoinMetadata { Description = "Codec2 Volume Mute On Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Codec2 Volume Mute Off Set / Get
+ ///
+ [JoinName("Codec2VolumeMuteOff")]
+ public JoinDataComplete Codec2VolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 32, JoinSpan = 1 },
+ new JoinMetadata { Description = "Codec2 Volume Mute Off Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Codec2 Volume Level Up
+ ///
+ [JoinName("Codec2VolumeUp")]
+ public JoinDataComplete Codec2VolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 33, JoinSpan = 1 },
+ new JoinMetadata { Description = "Codec2 Volume Level Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Codec2 Volume Level Down
+ ///
+ [JoinName("Codec2VolumeDown")]
+ public JoinDataComplete Codec2VolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 34, JoinSpan = 1 },
+ new JoinMetadata { Description = "Codec2 Volume Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Codec2 Volume Scaled Send Enable/Disable
+ ///
+ [JoinName("Codec2VolumeLevelScaledSend")]
+ public JoinDataComplete Codec2VolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 35, JoinSpan = 1 },
+ new JoinMetadata { Description = "Codec2 Volume Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// MicsMaster Volume Signed dB Set / Get
+ ///
+ [JoinName("MicsMasterVolumeLevel")]
+ public JoinDataComplete MicsMasterVolumeLevel = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 },
+ new JoinMetadata { Description = "MicsMaster Volume Signed dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// MicsMaster Volume 16bit Scaled Set / Get
+ ///
+ [JoinName("MicsMasterVolumeLevelScaled")]
+ public JoinDataComplete MicsMasterVolumeLevelScaled = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },
+ new JoinMetadata { Description = "MicsMaster Volume 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// MicsMaster Volume Mute On Set / Get
+ ///
+ [JoinName("MicsMasterVolumeMuteOn")]
+ public JoinDataComplete MicsMasterVolumeMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 },
+ new JoinMetadata { Description = "MicsMaster Volume Mute On Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// MicsMaster Volume Mute Off Set / Get
+ ///
+ [JoinName("MicsMasterVolumeMuteOff")]
+ public JoinDataComplete MicsMasterVolumeMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },
+ new JoinMetadata { Description = "MicsMaster Volume Mute Off Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// MicsMaster Volume Level Up
+ ///
+ [JoinName("MicsMasterVolumeUp")]
+ public JoinDataComplete MicsMasterVolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },
+ new JoinMetadata { Description = "MicsMaster Volume Level Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// MicsMaster Volume Level Down
+ ///
+ [JoinName("MicsMasterVolumeDown")]
+ public JoinDataComplete MicsMasterVolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 44, JoinSpan = 1 },
+ new JoinMetadata { Description = "MicsMaster Volume Level Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// MicsMaster Volume Scaled Send Enable/Disable
+ ///
+ [JoinName("MicsMasterVolumeLevelScaledSend")]
+ public JoinDataComplete MicsMasterVolumeLevelScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 45, JoinSpan = 1 },
+ new JoinMetadata { Description = "Mics Master Volume Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public DmpsAudioOutputControllerJoinMap(uint joinStart)
+ : this(joinStart, typeof(DmpsAudioOutputControllerJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected DmpsAudioOutputControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmpsMicrophoneControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmpsMicrophoneControllerJoinMap.cs
index 90efc964..3fba12e5 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmpsMicrophoneControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmpsMicrophoneControllerJoinMap.cs
@@ -1,49 +1,72 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class DmpsMicrophoneControllerJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("MicGain")]
- public JoinDataComplete MicGain = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Mic Gain dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("MicGainScaled")]
- public JoinDataComplete MicGainScaled = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Mic Gain 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("MicMuteOn")]
- public JoinDataComplete MicMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Mic Mute On Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("MicMuteOff")]
- public JoinDataComplete MicMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Mic Mute Off Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("MicGainScaledSend")]
- public JoinDataComplete MicGainScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Mic Gain Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("MicName")]
- public JoinDataComplete MicName = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Mic Name Get", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a DmpsMicrophoneControllerJoinMap
///
- /// Join this join map will start at
- public DmpsMicrophoneControllerJoinMap(uint joinStart)
- : this(joinStart, typeof(DmpsMicrophoneControllerJoinMap))
+ public class DmpsMicrophoneControllerJoinMap : JoinMapBaseAdvanced
{
- }
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected DmpsMicrophoneControllerJoinMap(uint joinStart, Type type)
- : base(joinStart, type)
- {
+ ///
+ /// Mic Gain dB Set / Get
+ ///
+ [JoinName("MicGain")]
+ public JoinDataComplete MicGain = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Mic Gain dB Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Mic Gain 16bit Scaled Set / Get
+ ///
+ [JoinName("MicGainScaled")]
+ public JoinDataComplete MicGainScaled = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Mic Gain 16bit Scaled Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Mic Mute On Set / Get
+ ///
+ [JoinName("MicMuteOn")]
+ public JoinDataComplete MicMuteOn = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Mic Mute On Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Mic Mute Off Set / Get
+ ///
+ [JoinName("MicMuteOff")]
+ public JoinDataComplete MicMuteOff = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Mic Mute Off Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Mic Gain Scaled Send Enable/Disable
+ ///
+ [JoinName("MicGainScaledSend")]
+ public JoinDataComplete MicGainScaledSend = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Mic Gain Scaled Send Enable/Disable", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Mic Name Get
+ ///
+ [JoinName("MicName")]
+ public JoinDataComplete MicName = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Mic Name Get", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public DmpsMicrophoneControllerJoinMap(uint joinStart)
+ : this(joinStart, typeof(DmpsMicrophoneControllerJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected DmpsMicrophoneControllerJoinMap(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs
index e39e38c6..8e9a3e97 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/DmpsRoutingControllerJoinMap.cs
@@ -1,122 +1,185 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class DmpsRoutingControllerJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("EnableRouting")]
- public JoinDataComplete EnableRouting = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "DMPS Enable Audio and Video Routing", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("SystemPowerOn")]
- public JoinDataComplete SystemPowerOn = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
- new JoinMetadata { Description = "DMPS System Power On Get/Set", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("SystemPowerOff")]
- public JoinDataComplete SystemPowerOff = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
- new JoinMetadata { Description = "DMPS System Power Off Get/Set", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("FrontPanelLockOn")]
- public JoinDataComplete FrontPanelLockOn = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
- new JoinMetadata { Description = "DMPS Front Panel Lock On Get/Set", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("FrontPanelLockOff")]
- public JoinDataComplete FrontPanelLockOff = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
- new JoinMetadata { Description = "DMPS Front Panel Lock Off Get/Set", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("VideoSyncStatus")]
- public JoinDataComplete VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Input Video Sync", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("InputEndpointOnline")]
- public JoinDataComplete InputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Input Endpoint Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("OutputEndpointOnline")]
- public JoinDataComplete OutputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Output Endpoint Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("OutputVideo")]
- public JoinDataComplete OutputVideo = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Output Video Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("OutputAudio")]
- public JoinDataComplete OutputAudio = new JoinDataComplete(new JoinData { JoinNumber = 301, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Output Audio Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("InputNames")]
- public JoinDataComplete InputNames = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("OutputNames")]
- public JoinDataComplete OutputNames = new JoinDataComplete(new JoinData { JoinNumber = 301, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Output Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("InputVideoNames")]
- public JoinDataComplete InputVideoNames =
- new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 32 },
- new JoinMetadata
- {
- Description = "Video Input Name",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
-
- [JoinName("InputAudioNames")]
- public JoinDataComplete InputAudioNames =
- new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 32 },
- new JoinMetadata
- {
- Description = "Audio Input Name",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
- [JoinName("OutputVideoNames")]
- public JoinDataComplete OutputVideoNames =
- new JoinDataComplete(new JoinData { JoinNumber = 901, JoinSpan = 32 },
- new JoinMetadata
- {
- Description = "Video Output Name",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
- [JoinName("OutputAudioNames")]
- public JoinDataComplete OutputAudioNames =
- new JoinDataComplete(new JoinData { JoinNumber = 1101, JoinSpan = 32 },
- new JoinMetadata
- {
- Description = "Audio Output Name",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
-
- [JoinName("OutputCurrentVideoInputNames")]
- public JoinDataComplete OutputCurrentVideoInputNames = new JoinDataComplete(new JoinData { JoinNumber = 2001, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Video Output Currently Routed Video Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("OutputCurrentAudioInputNames")]
- public JoinDataComplete OutputCurrentAudioInputNames = new JoinDataComplete(new JoinData { JoinNumber = 2201, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Audio Output Currently Routed Video Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("InputCurrentResolution")]
- public JoinDataComplete InputCurrentResolution = new JoinDataComplete(new JoinData { JoinNumber = 2401, JoinSpan = 32 },
- new JoinMetadata { Description = "DM Chassis Input Current Resolution", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a DmpsRoutingControllerJoinMap
///
- /// Join this join map will start at
- public DmpsRoutingControllerJoinMap(uint joinStart)
- : this(joinStart, typeof(DmpsRoutingControllerJoinMap))
+ public class DmpsRoutingControllerJoinMap : JoinMapBaseAdvanced
{
- }
+ ///
+ /// DMPS Enable Audio and Video Routing
+ ///
+ [JoinName("EnableRouting")]
+ public JoinDataComplete EnableRouting = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "DMPS Enable Audio and Video Routing", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DMPS Disable Audio and Video Routing
+ ///
+ [JoinName("SystemPowerOn")]
+ public JoinDataComplete SystemPowerOn = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
+ new JoinMetadata { Description = "DMPS System Power On Get/Set", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DMPS Disable Audio and Video Routing
+ ///
+ [JoinName("SystemPowerOff")]
+ public JoinDataComplete SystemPowerOff = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
+ new JoinMetadata { Description = "DMPS System Power Off Get/Set", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DMPS Front Panel Lock On Get/Set
+ ///
+ [JoinName("FrontPanelLockOn")]
+ public JoinDataComplete FrontPanelLockOn = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
+ new JoinMetadata { Description = "DMPS Front Panel Lock On Get/Set", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DMPS Front Panel Lock Off Get/Set
+ ///
+ [JoinName("FrontPanelLockOff")]
+ public JoinDataComplete FrontPanelLockOff = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
+ new JoinMetadata { Description = "DMPS Front Panel Lock Off Get/Set", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM Input Video Sync
+ ///
+ [JoinName("VideoSyncStatus")]
+ public JoinDataComplete VideoSyncStatus = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Input Video Sync", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM Chassis Input Endpoint Online
+ ///
+ [JoinName("InputEndpointOnline")]
+ public JoinDataComplete InputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Input Endpoint Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM Chassis Output Endpoint Online
+ ///
+ [JoinName("OutputEndpointOnline")]
+ public JoinDataComplete OutputEndpointOnline = new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Output Endpoint Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DM Chassis Input Video Set / Get
+ ///
+ [JoinName("OutputVideo")]
+ public JoinDataComplete OutputVideo = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Output Video Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM Chassis Input Audio Set / Get
+ ///
+ [JoinName("OutputAudio")]
+ public JoinDataComplete OutputAudio = new JoinDataComplete(new JoinData { JoinNumber = 301, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Output Audio Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// DM Chassis Input Name
+ ///
+ [JoinName("InputNames")]
+ public JoinDataComplete InputNames = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM Chassis Output Name
+ ///
+ [JoinName("OutputNames")]
+ public JoinDataComplete OutputNames = new JoinDataComplete(new JoinData { JoinNumber = 301, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Output Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM Chassis Video Input Name
+ ///
+ [JoinName("InputVideoNames")]
+ public JoinDataComplete InputVideoNames =
+ new JoinDataComplete(new JoinData { JoinNumber = 501, JoinSpan = 32 },
+ new JoinMetadata
+ {
+ Description = "DM Chassis Video Input Name",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ ///
+ /// DM Chassis Audio Input Name
+ ///
+ [JoinName("InputAudioNames")]
+ public JoinDataComplete InputAudioNames =
+ new JoinDataComplete(new JoinData { JoinNumber = 701, JoinSpan = 32 },
+ new JoinMetadata
+ {
+ Description = "Audio Input Name",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ ///
+ /// DM Chassis Video Output Name
+ ///
+ [JoinName("OutputVideoNames")]
+ public JoinDataComplete OutputVideoNames =
+ new JoinDataComplete(new JoinData { JoinNumber = 901, JoinSpan = 32 },
+ new JoinMetadata
+ {
+ Description = "Video Output Name",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ ///
+ /// DM Chassis Audio Output Name
+ ///
+ [JoinName("OutputAudioNames")]
+ public JoinDataComplete OutputAudioNames =
+ new JoinDataComplete(new JoinData { JoinNumber = 1101, JoinSpan = 32 },
+ new JoinMetadata
+ {
+ Description = "Audio Output Name",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ ///
+ /// DM Chassis Video Output Currently Routed Video Input Name
+ ///
+ [JoinName("OutputCurrentVideoInputNames")]
+ public JoinDataComplete OutputCurrentVideoInputNames = new JoinDataComplete(new JoinData { JoinNumber = 2001, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Video Output Currently Routed Video Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM Chassis Audio Output Currently Routed Video Input Name
+ ///
+ [JoinName("OutputCurrentAudioInputNames")]
+ public JoinDataComplete OutputCurrentAudioInputNames = new JoinDataComplete(new JoinData { JoinNumber = 2201, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Audio Output Currently Routed Video Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// DM Chassis Input Current Resolution
+ ///
+ [JoinName("InputCurrentResolution")]
+ public JoinDataComplete InputCurrentResolution = new JoinDataComplete(new JoinData { JoinNumber = 2401, JoinSpan = 32 },
+ new JoinMetadata { Description = "DM Chassis Input Current Resolution", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected DmpsRoutingControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)
- {
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public DmpsRoutingControllerJoinMap(uint joinStart)
+ : this(joinStart, typeof(DmpsRoutingControllerJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected DmpsRoutingControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericIrControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericIrControllerJoinMap.cs
index 00087187..57977737 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericIrControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericIrControllerJoinMap.cs
@@ -1,7 +1,10 @@
using PepperDash.Essentials.Core;
-namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
-
+namespace PepperDash.Essentials.Core.Bridges.JoinMaps
+{
+ ///
+ /// Represents a GenericIrControllerJoinMap
+ ///
public sealed class GenericIrControllerJoinMap : JoinMapBaseAdvanced
{
///
@@ -992,4 +995,5 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
: base(joinStart, typeof(GenericIrControllerJoinMap))
{
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericLightingJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericLightingJoinMap.cs
index 179b4124..2b684d9f 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericLightingJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericLightingJoinMap.cs
@@ -1,48 +1,67 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class GenericLightingJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
-
- [JoinName("IsOnline")]
- public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Lighting Controller Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("SelectScene")]
- public JoinDataComplete SelectScene = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Lighting Controller Select Scene By Index", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("SelectSceneDirect")]
- public JoinDataComplete SelectSceneDirect = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 10 },
- new JoinMetadata { Description = "Lighting Controller Select Scene", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.DigitalSerial });
-
- [JoinName("ButtonVisibility")]
- public JoinDataComplete ButtonVisibility = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 10 },
- new JoinMetadata { Description = "Lighting Controller Button Visibility", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("IntegrationIdSet")]
- public JoinDataComplete IntegrationIdSet = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Lighting Controller Set Integration Id", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });
-
-
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a GenericLightingJoinMap
///
- /// Join this join map will start at
- public GenericLightingJoinMap(uint joinStart)
- : this(joinStart, typeof(GenericLightingJoinMap))
+ public class GenericLightingJoinMap : JoinMapBaseAdvanced
{
- }
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected GenericLightingJoinMap(uint joinStart, Type type) : base(joinStart, type)
- {
+ ///
+ /// Lighting Controller Online
+ ///
+ [JoinName("IsOnline")]
+ public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Lighting Controller Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Select Scene By Index
+ ///
+ [JoinName("SelectScene")]
+ public JoinDataComplete SelectScene = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Lighting Controller Select Scene By Index", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Select Scene Direct
+ ///
+ [JoinName("SelectSceneDirect")]
+ public JoinDataComplete SelectSceneDirect = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 10 },
+ new JoinMetadata { Description = "Lighting Controller Select Scene", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.DigitalSerial });
+
+ ///
+ /// Button Visibility
+ ///
+ [JoinName("ButtonVisibility")]
+ public JoinDataComplete ButtonVisibility = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 10 },
+ new JoinMetadata { Description = "Lighting Controller Button Visibility", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Set Integration Id
+ ///
+ [JoinName("IntegrationIdSet")]
+ public JoinDataComplete IntegrationIdSet = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Lighting Controller Set Integration Id", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });
+
+
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public GenericLightingJoinMap(uint joinStart)
+ : this(joinStart, typeof(GenericLightingJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected GenericLightingJoinMap(uint joinStart, Type type) : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericRelayControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericRelayControllerJoinMap.cs
index 03aaf041..470dc4e1 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericRelayControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GenericRelayControllerJoinMap.cs
@@ -1,30 +1,37 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class GenericRelayControllerJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
-
- [JoinName("Relay")]
- public JoinDataComplete Relay = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Device Relay State Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a GenericRelayControllerJoinMap
///
- /// Join this join map will start at
- public GenericRelayControllerJoinMap(uint joinStart)
- : this(joinStart, typeof(GenericRelayControllerJoinMap))
+ public class GenericRelayControllerJoinMap : JoinMapBaseAdvanced
{
- }
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected GenericRelayControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)
- {
-
+ ///
+ /// Device Relay State Set / Get
+ ///
+ [JoinName("Relay")]
+ public JoinDataComplete Relay = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Device Relay State Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public GenericRelayControllerJoinMap(uint joinStart)
+ : this(joinStart, typeof(GenericRelayControllerJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected GenericRelayControllerJoinMap(uint joinStart, Type type) : base(joinStart, type)
+ {
+
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GlsOccupancySensorBaseJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GlsOccupancySensorBaseJoinMap.cs
index 228cb4f0..23341e7e 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GlsOccupancySensorBaseJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GlsOccupancySensorBaseJoinMap.cs
@@ -1,184 +1,304 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class GlsOccupancySensorBaseJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("IsOnline")]
- public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Is Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("ForceOccupied")]
- public JoinDataComplete ForceOccupied = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Set to Occupied", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("ForceVacant")]
- public JoinDataComplete ForceVacant = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Set to Vacant", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("EnableRawStates")]
- public JoinDataComplete EnableRawStates = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Enable Raw", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("RoomOccupiedFeedback")]
- public JoinDataComplete RoomOccupiedFeedback = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Room Is Occupied", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("GraceOccupancyDetectedFeedback")]
- public JoinDataComplete GraceOccupancyDetectedFeedback = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Grace Occupancy Detected", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("RoomVacantFeedback")]
- public JoinDataComplete RoomVacantFeedback = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Room Is Vacant", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("RawOccupancyFeedback")]
- public JoinDataComplete RawOccupancyFeedback = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Raw Occupancy Detected", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("RawOccupancyPirFeedback")]
- public JoinDataComplete RawOccupancyPirFeedback = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Raw PIR Occupancy Detected", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("RawOccupancyUsFeedback")]
- public JoinDataComplete RawOccupancyUsFeedback = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Raw US Occupancy Detected", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("EnableLedFlash")]
- public JoinDataComplete EnableLedFlash = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Enable LED Flash", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DisableLedFlash")]
- public JoinDataComplete DisableLedFlash = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Disable LED Flash", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("EnableShortTimeout")]
- public JoinDataComplete EnableShortTimeout = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Enable Short Timeout", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DisableShortTimeout")]
- public JoinDataComplete DisableShortTimeout = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Disable Short Timeout", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("OrWhenVacated")]
- public JoinDataComplete OrWhenVacated = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Set To Vacant when Either Sensor is Vacant", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("AndWhenVacated")]
- public JoinDataComplete AndWhenVacated = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Set To Vacant when Both Sensors are Vacant", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("EnableUsA")]
- public JoinDataComplete EnableUsA = new JoinDataComplete(new JoinData { JoinNumber = 17, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Enable Ultrasonic Sensor A", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DisableUsA")]
- public JoinDataComplete DisableUsA = new JoinDataComplete(new JoinData { JoinNumber = 18, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Disable Ultrasonic Sensor A", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("EnableUsB")]
- public JoinDataComplete EnableUsB = new JoinDataComplete(new JoinData { JoinNumber = 19, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Enable Ultrasonic Sensor B", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DisableUsB")]
- public JoinDataComplete DisableUsB = new JoinDataComplete(new JoinData { JoinNumber = 20, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Disable Ultrasonic Sensor B", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("EnablePir")]
- public JoinDataComplete EnablePir = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Enable IR Sensor", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DisablePir")]
- public JoinDataComplete DisablePir = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Disable IR Sensor", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("IncrementUsInOccupiedState")]
- public JoinDataComplete IncrementUsInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Increment US Occupied State Sensitivity", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DecrementUsInOccupiedState")]
- public JoinDataComplete DecrementUsInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Decrement US Occupied State Sensitivity", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("IncrementUsInVacantState")]
- public JoinDataComplete IncrementUsInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Increment US Vacant State Sensitivity", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DecrementUsInVacantState")]
- public JoinDataComplete DecrementUsInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 26, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Decrement US Vacant State Sensitivity", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("IncrementPirInOccupiedState")]
- public JoinDataComplete IncrementPirInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 27, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Increment IR Occupied State Sensitivity", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DecrementPirInOccupiedState")]
- public JoinDataComplete DecrementPirInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 28, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Decrement IR Occupied State Sensitivity", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("IncrementPirInVacantState")]
- public JoinDataComplete IncrementPirInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 29, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Increment IR Vacant State Sensitivity", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DecrementPirInVacantState")]
- public JoinDataComplete DecrementPirInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Decrement IR Vacant State Sensitivity", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Timeout")]
- public JoinDataComplete Timeout = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Timeout Value", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("TimeoutLocalFeedback")]
- public JoinDataComplete TimeoutLocalFeedback = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Local Timeout Value", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("InternalPhotoSensorValue")]
- public JoinDataComplete InternalPhotoSensorValue = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Internal PhotoSensor Value", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("ExternalPhotoSensorValue")]
- public JoinDataComplete ExternalPhotoSensorValue = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor External PhotoSensor Value", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("UsSensitivityInOccupiedState")]
- public JoinDataComplete UsSensitivityInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Ultrasonic Sensitivity in Occupied State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("UsSensitivityInVacantState")]
- public JoinDataComplete UsSensitivityInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Ultrasonic Sensitivity in Vacant State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("PirSensitivityInOccupiedState")]
- public JoinDataComplete PirSensitivityInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor PIR Sensitivity in Occupied State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("PirSensitivityInVacantState")]
- public JoinDataComplete PirSensitivityInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor PIR Sensitivity in Vacant State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("Name")]
- public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Occ Sensor Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a GlsOccupancySensorBaseJoinMap
///
- /// Join this join map will start at
- public GlsOccupancySensorBaseJoinMap(uint joinStart)
- : this(joinStart, typeof(GlsOccupancySensorBaseJoinMap))
+ public class GlsOccupancySensorBaseJoinMap : JoinMapBaseAdvanced
{
+ ///
+ /// Occ Sensor Is Online
+ ///
+ [JoinName("IsOnline")]
+ public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Is Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Set to Occupied
+ ///
+ [JoinName("ForceOccupied")]
+ public JoinDataComplete ForceOccupied = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Set to Occupied", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Set to Vacant
+ ///
+ [JoinName("ForceVacant")]
+ public JoinDataComplete ForceVacant = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Set to Vacant", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Enable Raw
+ ///
+ [JoinName("EnableRawStates")]
+ public JoinDataComplete EnableRawStates = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Enable Raw", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Disable Raw
+ ///
+ [JoinName("RoomOccupiedFeedback")]
+ public JoinDataComplete RoomOccupiedFeedback = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Room Is Occupied", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Grace Occupancy Detected
+ ///
+ [JoinName("GraceOccupancyDetectedFeedback")]
+ public JoinDataComplete GraceOccupancyDetectedFeedback = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Grace Occupancy Detected", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Room Is Vacant
+ ///
+ [JoinName("RoomVacantFeedback")]
+ public JoinDataComplete RoomVacantFeedback = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Room Is Vacant", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Raw Occupancy Detected
+ ///
+ [JoinName("RawOccupancyFeedback")]
+ public JoinDataComplete RawOccupancyFeedback = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Raw Occupancy Detected", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Raw PIR Occupancy Detected
+ ///
+ [JoinName("RawOccupancyPirFeedback")]
+ public JoinDataComplete RawOccupancyPirFeedback = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Raw PIR Occupancy Detected", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Raw US Occupancy Detected
+ ///
+ [JoinName("RawOccupancyUsFeedback")]
+ public JoinDataComplete RawOccupancyUsFeedback = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Raw US Occupancy Detected", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Enable LED Flash
+ ///
+ [JoinName("EnableLedFlash")]
+ public JoinDataComplete EnableLedFlash = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Enable LED Flash", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Disable LED Flash
+ ///
+ [JoinName("DisableLedFlash")]
+ public JoinDataComplete DisableLedFlash = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Disable LED Flash", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Enable Short Timeout
+ ///
+ [JoinName("EnableShortTimeout")]
+ public JoinDataComplete EnableShortTimeout = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Enable Short Timeout", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Disable Short Timeout
+ ///
+ [JoinName("DisableShortTimeout")]
+ public JoinDataComplete DisableShortTimeout = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Disable Short Timeout", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Set To Vacant when Either Sensor is Vacant
+ ///
+ [JoinName("OrWhenVacated")]
+ public JoinDataComplete OrWhenVacated = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Set To Vacant when Either Sensor is Vacant", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Set To Vacant when Both Sensors are Vacant
+ ///
+ [JoinName("AndWhenVacated")]
+ public JoinDataComplete AndWhenVacated = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Set To Vacant when Both Sensors are Vacant", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Enable Ultrasonic Sensor A
+ ///
+ [JoinName("EnableUsA")]
+ public JoinDataComplete EnableUsA = new JoinDataComplete(new JoinData { JoinNumber = 17, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Enable Ultrasonic Sensor A", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Disable Ultrasonic Sensor A
+ ///
+ [JoinName("DisableUsA")]
+ public JoinDataComplete DisableUsA = new JoinDataComplete(new JoinData { JoinNumber = 18, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Disable Ultrasonic Sensor A", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Enable Ultrasonic Sensor B
+ ///
+ [JoinName("EnableUsB")]
+ public JoinDataComplete EnableUsB = new JoinDataComplete(new JoinData { JoinNumber = 19, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Enable Ultrasonic Sensor B", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Disable Ultrasonic Sensor B
+ ///
+ [JoinName("DisableUsB")]
+ public JoinDataComplete DisableUsB = new JoinDataComplete(new JoinData { JoinNumber = 20, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Disable Ultrasonic Sensor B", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Enable IR Sensor
+ ///
+ [JoinName("EnablePir")]
+ public JoinDataComplete EnablePir = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Enable IR Sensor", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Disable IR Sensor
+ ///
+ [JoinName("DisablePir")]
+ public JoinDataComplete DisablePir = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Disable IR Sensor", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Increment US Occupied State Sensitivity
+ ///
+ [JoinName("IncrementUsInOccupiedState")]
+ public JoinDataComplete IncrementUsInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Increment US Occupied State Sensitivity", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Decrement US Occupied State Sensitivity
+ ///
+ [JoinName("DecrementUsInOccupiedState")]
+ public JoinDataComplete DecrementUsInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Decrement US Occupied State Sensitivity", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Increment US Vacant State Sensitivity
+ ///
+ [JoinName("IncrementUsInVacantState")]
+ public JoinDataComplete IncrementUsInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Increment US Vacant State Sensitivity", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Decrement US Vacant State Sensitivity
+ ///
+ [JoinName("DecrementUsInVacantState")]
+ public JoinDataComplete DecrementUsInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 26, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Decrement US Vacant State Sensitivity", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Increment IR Occupied State Sensitivity
+ ///
+ [JoinName("IncrementPirInOccupiedState")]
+ public JoinDataComplete IncrementPirInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 27, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Increment IR Occupied State Sensitivity", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Decrement IR Occupied State Sensitivity
+ ///
+ [JoinName("DecrementPirInOccupiedState")]
+ public JoinDataComplete DecrementPirInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 28, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Decrement IR Occupied State Sensitivity", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Increment IR Vacant State Sensitivity
+ ///
+ [JoinName("IncrementPirInVacantState")]
+ public JoinDataComplete IncrementPirInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 29, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Increment IR Vacant State Sensitivity", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Decrement IR Vacant State Sensitivity
+ ///
+ [JoinName("DecrementPirInVacantState")]
+ public JoinDataComplete DecrementPirInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Decrement IR Vacant State Sensitivity", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Occ Sensor Timeout Value
+ ///
+ [JoinName("Timeout")]
+ public JoinDataComplete Timeout = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Timeout Value", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Occ Sensor Local Timeout Value
+ ///
+ [JoinName("TimeoutLocalFeedback")]
+ public JoinDataComplete TimeoutLocalFeedback = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Local Timeout Value", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Occ Sensor Internal PhotoSensor Value
+ ///
+ [JoinName("InternalPhotoSensorValue")]
+ public JoinDataComplete InternalPhotoSensorValue = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Internal PhotoSensor Value", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Occ Sensor External PhotoSensor Value
+ ///
+ [JoinName("ExternalPhotoSensorValue")]
+ public JoinDataComplete ExternalPhotoSensorValue = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor External PhotoSensor Value", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Occ Sensor Ultrasonic Sensitivity in Occupied State
+ ///
+ [JoinName("UsSensitivityInOccupiedState")]
+ public JoinDataComplete UsSensitivityInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Ultrasonic Sensitivity in Occupied State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Occ Sensor Ultrasonic Sensitivity in Vacant State
+ ///
+ [JoinName("UsSensitivityInVacantState")]
+ public JoinDataComplete UsSensitivityInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Ultrasonic Sensitivity in Vacant State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Occ Sensor PIR Sensitivity in Occupied State
+ ///
+ [JoinName("PirSensitivityInOccupiedState")]
+ public JoinDataComplete PirSensitivityInOccupiedState = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor PIR Sensitivity in Occupied State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Occ Sensor PIR Sensitivity in Vacant State
+ ///
+ [JoinName("PirSensitivityInVacantState")]
+ public JoinDataComplete PirSensitivityInVacantState = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor PIR Sensitivity in Vacant State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Occ Sensor Name
+ ///
+ [JoinName("Name")]
+ public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Occ Sensor Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public GlsOccupancySensorBaseJoinMap(uint joinStart)
+ : this(joinStart, typeof(GlsOccupancySensorBaseJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected GlsOccupancySensorBaseJoinMap(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+ }
+
}
-
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected GlsOccupancySensorBaseJoinMap(uint joinStart, Type type)
- : base(joinStart, type)
- {
- }
-
-}
-
+}
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GlsPartitionSensorJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GlsPartitionSensorJoinMap.cs
index 7515c8c2..98c491dd 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GlsPartitionSensorJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/GlsPartitionSensorJoinMap.cs
@@ -1,156 +1,183 @@
using System;
using PepperDash.Essentials.Core;
-namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
-
-public class GlsPartitionSensorJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges.JoinMaps
{
-
- #region Digital
-
- [JoinName("IsOnline")]
- public JoinDataComplete IsOnline = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 1,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Sensor Is Online",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Digital
- });
-
-
- [JoinName("Enable")]
- public JoinDataComplete Enable = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 2,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Sensor Enable",
- JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
- JoinType = eJoinType.Digital
- });
-
- [JoinName("PartitionSensed")]
- public JoinDataComplete PartitionSensed = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 3,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Sensor Partition Sensed",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Digital
- });
-
- [JoinName("PartitionNotSensed")]
- public JoinDataComplete PartitionNotSensed = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 4,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Sensor Partition Not Sensed",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Digital
- });
-
- [JoinName("IncreaseSensitivity")]
- public JoinDataComplete IncreaseSensitivity = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 6,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Sensor Increase Sensitivity",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
-
- [JoinName("DecreaseSensitivity")]
- public JoinDataComplete DecreaseSensitivity = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 7,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Sensor Decrease Sensitivity",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
-
- #endregion
-
- #region Analog
-
- [JoinName("Sensitivity")]
- public JoinDataComplete Sensitivity = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 2,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Sensor Sensitivity",
- JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
- JoinType = eJoinType.Analog
- });
-
- #endregion
-
-
- #region Serial
-
- [JoinName("Name")]
- public JoinDataComplete Name = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 1,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Sensor Name",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
-
- #endregion
-
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a GlsPartitionSensorJoinMap
///
- /// Join this join map will start at
- public GlsPartitionSensorJoinMap(uint joinStart)
- : this(joinStart, typeof(GlsPartitionSensorJoinMap))
+ public class GlsPartitionSensorJoinMap : JoinMapBaseAdvanced
{
- }
+ #region Digital
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected GlsPartitionSensorJoinMap(uint joinStart, Type type)
- : base(joinStart, type)
- {
+ ///
+ /// Sensor Is Online
+ ///
+ [JoinName("IsOnline")]
+ public JoinDataComplete IsOnline = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 1,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Sensor Is Online",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Digital
+ });
+ ///
+ /// Sensor Enable
+ ///
+ [JoinName("Enable")]
+ public JoinDataComplete Enable = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 2,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Sensor Enable",
+ JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ ///
+ /// Sensor Partition Sensed
+ ///
+ [JoinName("PartitionSensed")]
+ public JoinDataComplete PartitionSensed = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 3,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Sensor Partition Sensed",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ ///
+ /// Sensor Partition Not Sensed
+ ///
+ [JoinName("PartitionNotSensed")]
+ public JoinDataComplete PartitionNotSensed = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 4,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Sensor Partition Not Sensed",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ ///
+ /// Sensor Increase Sensitivity
+ ///
+ [JoinName("IncreaseSensitivity")]
+ public JoinDataComplete IncreaseSensitivity = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 6,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Sensor Increase Sensitivity",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ ///
+ /// Sensor Decrease Sensitivity
+ ///
+ [JoinName("DecreaseSensitivity")]
+ public JoinDataComplete DecreaseSensitivity = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 7,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Sensor Decrease Sensitivity",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
+
+ #endregion
+
+ #region Analog
+
+ ///
+ /// Sensor Sensitivity
+ ///
+ [JoinName("Sensitivity")]
+ public JoinDataComplete Sensitivity = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 2,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Sensor Sensitivity",
+ JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinType = eJoinType.Analog
+ });
+
+ #endregion
+
+
+ #region Serial
+
+ ///
+ /// Sensor Name
+ ///
+ [JoinName("Name")]
+ public JoinDataComplete Name = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 1,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Sensor Name",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
+
+ #endregion
+
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public GlsPartitionSensorJoinMap(uint joinStart)
+ : this(joinStart, typeof(GlsPartitionSensorJoinMap))
+ {
+
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected GlsPartitionSensorJoinMap(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+
+ }
}
}
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdMdNxM4kEControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdMdNxM4kEControllerJoinMap.cs
index 43ed25c9..950e3f1e 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdMdNxM4kEControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdMdNxM4kEControllerJoinMap.cs
@@ -1,65 +1,99 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class HdMdNxM4kEControllerJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("Name")]
- public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Device Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("EnableAutoRoute")]
- public JoinDataComplete EnableAutoRoute = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Enable Automatic Routing on 4x1 Switchers", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("InputName")]
- public JoinDataComplete InputName = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 8 },
- new JoinMetadata { Description = "Device Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("InputSync")]
- public JoinDataComplete InputSync = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 8 },
- new JoinMetadata { Description = "Device Input Sync", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("OutputName")]
- public JoinDataComplete OutputName = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 2 },
- new JoinMetadata { Description = "Device Output Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("OutputRoute")]
- public JoinDataComplete OutputRoute = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 2 },
- new JoinMetadata { Description = "Device Output Route Set/Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("OutputRoutedName")]
- public JoinDataComplete OutputRoutedName = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 2 },
- new JoinMetadata { Description = "Device Output Route Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("EnableInputHdcp")]
- public JoinDataComplete EnableInputHdcp = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 8 },
- new JoinMetadata { Description = "Device Enable Input Hdcp", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DisableInputHdcp")]
- public JoinDataComplete DisableInputHdcp = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 8 },
- new JoinMetadata { Description = "Device Disnable Input Hdcp", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("IsOnline")]
- public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },
- new JoinMetadata { Description = "Device Onlne", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a HdMdNxM4kEControllerJoinMap
///
- /// Join this join map will start at
- public HdMdNxM4kEControllerJoinMap(uint joinStart)
- : this(joinStart, typeof(HdMdNxM4kEControllerJoinMap))
+ public class HdMdNxM4kEControllerJoinMap : JoinMapBaseAdvanced
{
- }
+ ///
+ /// Device Name
+ ///
+ [JoinName("Name")]
+ public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Device Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected HdMdNxM4kEControllerJoinMap(uint joinStart, Type type)
- : base(joinStart, type)
- {
+ ///
+ /// Enable Automatic Routing on 4x1 Switchers
+ ///
+ [JoinName("EnableAutoRoute")]
+ public JoinDataComplete EnableAutoRoute = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Enable Automatic Routing on 4x1 Switchers", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Device Input Name
+ ///
+ [JoinName("InputName")]
+ public JoinDataComplete InputName = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 8 },
+ new JoinMetadata { Description = "Device Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Device Input Sync
+ ///
+ [JoinName("InputSync")]
+ public JoinDataComplete InputSync = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 8 },
+ new JoinMetadata { Description = "Device Input Sync", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Device Output Name
+ ///
+ [JoinName("OutputName")]
+ public JoinDataComplete OutputName = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 2 },
+ new JoinMetadata { Description = "Device Output Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Device Output Route Set/Get
+ ///
+ [JoinName("OutputRoute")]
+ public JoinDataComplete OutputRoute = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 2 },
+ new JoinMetadata { Description = "Device Output Route Set/Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Device Output Route Name
+ ///
+ [JoinName("OutputRoutedName")]
+ public JoinDataComplete OutputRoutedName = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 2 },
+ new JoinMetadata { Description = "Device Output Route Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Device Enable Input Hdcp
+ ///
+ [JoinName("EnableInputHdcp")]
+ public JoinDataComplete EnableInputHdcp = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 8 },
+ new JoinMetadata { Description = "Device Enable Input Hdcp", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Device Disable Input Hdcp
+ ///
+ [JoinName("DisableInputHdcp")]
+ public JoinDataComplete DisableInputHdcp = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 8 },
+ new JoinMetadata { Description = "Device Disnable Input Hdcp", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Device Online Status
+ ///
+ [JoinName("IsOnline")]
+ public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },
+ new JoinMetadata { Description = "Device Onlne", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public HdMdNxM4kEControllerJoinMap(uint joinStart)
+ : this(joinStart, typeof(HdMdNxM4kEControllerJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected HdMdNxM4kEControllerJoinMap(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdMdxxxCEControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdMdxxxCEControllerJoinMap.cs
index 32bc5651..2730a0ea 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdMdxxxCEControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdMdxxxCEControllerJoinMap.cs
@@ -1,75 +1,114 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class HdMdxxxCEControllerJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
-
- [JoinName("IsOnline")]
- public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Device Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("RemoteEndDetected")]
- public JoinDataComplete RemoteEndDetected = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Device Remote End Detected", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("AutoRouteOn")]
- public JoinDataComplete AutoRouteOn = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Device Auto Route On", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("AutoRouteOff")]
- public JoinDataComplete AutoRouteOff = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "Device Auto Route Off", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("PriorityRoutingOn")]
- public JoinDataComplete PriorityRoutingOn = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "Device Priority Routing On", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("PriorityRoutingOff")]
- public JoinDataComplete PriorityRoutingOff = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
- new JoinMetadata { Description = "Device Priority Routing Off", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("InputOnScreenDisplayEnabled")]
- public JoinDataComplete InputOnScreenDisplayEnabled = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
- new JoinMetadata { Description = "Device Input OSD Enabled", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("InputOnScreenDisplayDisabled")]
- public JoinDataComplete InputOnScreenDisplayDisabled = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
- new JoinMetadata { Description = "Device Input OSD Disabled", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("SyncDetected")]
- public JoinDataComplete SyncDetected = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 5 },
- new JoinMetadata { Description = "Device Sync Detected", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("VideoSource")]
- public JoinDataComplete VideoSource = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 5 },
- new JoinMetadata { Description = "Device Video Source Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("SourceCount")]
- public JoinDataComplete SourceCount = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 5 },
- new JoinMetadata { Description = "Device Video Source Count", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("SourceNames")]
- public JoinDataComplete SourceNames = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 5 },
- new JoinMetadata { Description = "Device Video Source Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a HdMdxxxCEControllerJoinMap
///
- /// Join this join map will start at
- public HdMdxxxCEControllerJoinMap(uint joinStart)
- : this(joinStart, typeof(HdMdxxxCEControllerJoinMap))
+ public class HdMdxxxCEControllerJoinMap : JoinMapBaseAdvanced
{
- }
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected HdMdxxxCEControllerJoinMap(uint joinStart, Type type)
- : base(joinStart, type)
- {
+ ///
+ /// Device Online
+ ///
+ [JoinName("IsOnline")]
+ public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Device Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Remote End Detected
+ ///
+ [JoinName("RemoteEndDetected")]
+ public JoinDataComplete RemoteEndDetected = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Device Remote End Detected", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Auto Route On
+ ///
+ [JoinName("AutoRouteOn")]
+ public JoinDataComplete AutoRouteOn = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Device Auto Route On", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Auto Route Off
+ ///
+ [JoinName("AutoRouteOff")]
+ public JoinDataComplete AutoRouteOff = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "Device Auto Route Off", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Priority Routing On
+ ///
+ [JoinName("PriorityRoutingOn")]
+ public JoinDataComplete PriorityRoutingOn = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "Device Priority Routing On", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Priority Routing Off
+ ///
+ [JoinName("PriorityRoutingOff")]
+ public JoinDataComplete PriorityRoutingOff = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
+ new JoinMetadata { Description = "Device Priority Routing Off", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Input On Screen Display Enabled
+ ///
+ [JoinName("InputOnScreenDisplayEnabled")]
+ public JoinDataComplete InputOnScreenDisplayEnabled = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
+ new JoinMetadata { Description = "Device Input OSD Enabled", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Input On Screen Display Disabled
+ ///
+ [JoinName("InputOnScreenDisplayDisabled")]
+ public JoinDataComplete InputOnScreenDisplayDisabled = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
+ new JoinMetadata { Description = "Device Input OSD Disabled", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Sync Detected
+ ///
+ [JoinName("SyncDetected")]
+ public JoinDataComplete SyncDetected = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 5 },
+ new JoinMetadata { Description = "Device Sync Detected", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Video Source
+ ///
+ [JoinName("VideoSource")]
+ public JoinDataComplete VideoSource = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 5 },
+ new JoinMetadata { Description = "Device Video Source Set / Get", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Source Count
+ ///
+ [JoinName("SourceCount")]
+ public JoinDataComplete SourceCount = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 5 },
+ new JoinMetadata { Description = "Device Video Source Count", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+ ///
+ /// Source Names
+ ///
+ [JoinName("SourceNames")]
+ public JoinDataComplete SourceNames = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 5 },
+ new JoinMetadata { Description = "Device Video Source Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public HdMdxxxCEControllerJoinMap(uint joinStart)
+ : this(joinStart, typeof(HdMdxxxCEControllerJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected HdMdxxxCEControllerJoinMap(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdPsXxxControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdPsXxxControllerJoinMap.cs
index e5225ac6..aee3c2f1 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdPsXxxControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/HdPsXxxControllerJoinMap.cs
@@ -1,8 +1,11 @@
using System;
using PepperDash.Essentials.Core;
-namespace PepperDash.Essentials.Core.Bridges;
-
+namespace PepperDash.Essentials.Core.Bridges
+{
+ ///
+ /// Represents a HdPsXxxControllerJoinMap
+ ///
public class HdPsXxxControllerJoinMap : JoinMapBaseAdvanced
{
@@ -209,4 +212,5 @@ namespace PepperDash.Essentials.Core.Bridges;
: base(joinStart, type)
{
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/Hrxxx0WirelessRemoteControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/Hrxxx0WirelessRemoteControllerJoinMap.cs
index ca9ef3c6..5c4c858e 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/Hrxxx0WirelessRemoteControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/Hrxxx0WirelessRemoteControllerJoinMap.cs
@@ -1,245 +1,414 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class Hrxxx0WirelessRemoteControllerJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("Power")]
- public JoinDataComplete Power = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Power", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Menu")]
- public JoinDataComplete Menu = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Menu", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Guide")]
- public JoinDataComplete Guide = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Guide", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Info")]
- public JoinDataComplete Info = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "Info", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("VolumeUp")]
- public JoinDataComplete VolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "VolumeUp", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("VolumeDown")]
- public JoinDataComplete VolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
- new JoinMetadata { Description = "VolumeDown", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DialPadUp")]
- public JoinDataComplete DialPadUp = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
- new JoinMetadata { Description = "DialPadUp", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DialPadDown")]
- public JoinDataComplete DialPadDown = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
- new JoinMetadata { Description = "DialPadDown", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DialPadLeft")]
- public JoinDataComplete DialPadLeft = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 },
- new JoinMetadata { Description = "DialPadLeft", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DialPadRight")]
- public JoinDataComplete DialPadRight = new JoinDataComplete(new JoinData { JoinNumber = 10, JoinSpan = 1 },
- new JoinMetadata { Description = "DialPadRight", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DialPadSelect")]
- public JoinDataComplete DialPadSelect = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
- new JoinMetadata { Description = "DialPadSelect", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("ChannelUp")]
- public JoinDataComplete ChannelUp = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
- new JoinMetadata { Description = "ChannelUp", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("ChannelDown")]
- public JoinDataComplete ChannelDown = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
- new JoinMetadata { Description = "ChannelDown", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Mute")]
- public JoinDataComplete Mute = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
- new JoinMetadata { Description = "Mute", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Exit")]
- public JoinDataComplete Exit = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
- new JoinMetadata { Description = "Exit", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Last")]
- public JoinDataComplete Last = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 1 },
- new JoinMetadata { Description = "Last", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Play")]
- public JoinDataComplete Play = new JoinDataComplete(new JoinData { JoinNumber = 17, JoinSpan = 1 },
- new JoinMetadata { Description = "Play", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Pause")]
- public JoinDataComplete Pause = new JoinDataComplete(new JoinData { JoinNumber = 18, JoinSpan = 1 },
- new JoinMetadata { Description = "Pause", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Rewind")]
- public JoinDataComplete Rewind = new JoinDataComplete(new JoinData { JoinNumber = 19, JoinSpan = 1 },
- new JoinMetadata { Description = "Rewind", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("FastForward")]
- public JoinDataComplete FastForward = new JoinDataComplete(new JoinData { JoinNumber = 20, JoinSpan = 1 },
- new JoinMetadata { Description = "FastForward", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("PreviousTrack")]
- public JoinDataComplete PreviousTrack = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },
- new JoinMetadata { Description = "PreviousTrack", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("NextTrack")]
- public JoinDataComplete NextTrack = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },
- new JoinMetadata { Description = "NextTrack", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Stop")]
- public JoinDataComplete Stop = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },
- new JoinMetadata { Description = "Stop", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Record")]
- public JoinDataComplete Record = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },
- new JoinMetadata { Description = "Record", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Dvr")]
- public JoinDataComplete Dvr = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },
- new JoinMetadata { Description = "Dvr", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Keypad1")]
- public JoinDataComplete Keypad1 = new JoinDataComplete(new JoinData { JoinNumber = 26, JoinSpan = 1 },
- new JoinMetadata { Description = "Keypad1", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Keypad2Abc")]
- public JoinDataComplete Keypad2 = new JoinDataComplete(new JoinData { JoinNumber = 27, JoinSpan = 1 },
- new JoinMetadata { Description = "Keypad2Abc", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Keypad3Def")]
- public JoinDataComplete Keypad3Def = new JoinDataComplete(new JoinData { JoinNumber = 28, JoinSpan = 1 },
- new JoinMetadata { Description = "Keypad3Def", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Keypad4Ghi")]
- public JoinDataComplete Keypad4Ghi = new JoinDataComplete(new JoinData { JoinNumber = 29, JoinSpan = 1 },
- new JoinMetadata { Description = "Keypad4Ghi", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Keypad5Jkl")]
- public JoinDataComplete Keypad5Jkl = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },
- new JoinMetadata { Description = "Keypad5Jkl", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Keypad6Mno")]
- public JoinDataComplete Keypad6Mno = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 1 },
- new JoinMetadata { Description = "Keypad6Mno", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Keypad7Pqrs")]
- public JoinDataComplete Keypad7Pqrs = new JoinDataComplete(new JoinData { JoinNumber = 32, JoinSpan = 1 },
- new JoinMetadata { Description = "Keypad7Pqrs", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Keypad8Tuv")]
- public JoinDataComplete Keypad8Tuv = new JoinDataComplete(new JoinData { JoinNumber = 33, JoinSpan = 1 },
- new JoinMetadata { Description = "Keypad8Tuv", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Keypad9Wxyz")]
- public JoinDataComplete Keypad9Wxyz = new JoinDataComplete(new JoinData { JoinNumber = 34, JoinSpan = 1 },
- new JoinMetadata { Description = "Keypad9Wxyz", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Keypad0")]
- public JoinDataComplete Keypad0 = new JoinDataComplete(new JoinData { JoinNumber = 35, JoinSpan = 1 },
- new JoinMetadata { Description = "Keypad0", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Clear")]
- public JoinDataComplete Clear = new JoinDataComplete(new JoinData { JoinNumber = 36, JoinSpan = 1 },
- new JoinMetadata { Description = "Clear", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Enter")]
- public JoinDataComplete Enter = new JoinDataComplete(new JoinData { JoinNumber = 37, JoinSpan = 1 },
- new JoinMetadata { Description = "Enter", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Red")]
- public JoinDataComplete Red = new JoinDataComplete(new JoinData { JoinNumber = 38, JoinSpan = 1 },
- new JoinMetadata { Description = "Red", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Green")]
- public JoinDataComplete Green = new JoinDataComplete(new JoinData { JoinNumber = 39, JoinSpan = 1 },
- new JoinMetadata { Description = "Green", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Yellow")]
- public JoinDataComplete Yellow = new JoinDataComplete(new JoinData { JoinNumber = 40, JoinSpan = 1 },
- new JoinMetadata { Description = "Yellow", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Blue")]
- public JoinDataComplete Blue = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 },
- new JoinMetadata { Description = "Blue", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Custom1")]
- public JoinDataComplete Custom1 = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },
- new JoinMetadata { Description = "Custom1", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Custom2")]
- public JoinDataComplete Custom2 = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },
- new JoinMetadata { Description = "Custom2", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Custom3")]
- public JoinDataComplete Custom3 = new JoinDataComplete(new JoinData { JoinNumber = 44, JoinSpan = 1 },
- new JoinMetadata { Description = "Custom3", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Custom4")]
- public JoinDataComplete Custom4 = new JoinDataComplete(new JoinData { JoinNumber = 45, JoinSpan = 1 },
- new JoinMetadata { Description = "Custom4", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Custom5")]
- public JoinDataComplete Custom5 = new JoinDataComplete(new JoinData { JoinNumber = 46, JoinSpan = 1 },
- new JoinMetadata { Description = "Custom5", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Custom6")]
- public JoinDataComplete Custom6 = new JoinDataComplete(new JoinData { JoinNumber = 47, JoinSpan = 1 },
- new JoinMetadata { Description = "Custom6", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Custom7")]
- public JoinDataComplete Custom7 = new JoinDataComplete(new JoinData { JoinNumber = 48, JoinSpan = 1 },
- new JoinMetadata { Description = "Custom7", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Custom8")]
- public JoinDataComplete Custom8 = new JoinDataComplete(new JoinData { JoinNumber = 49, JoinSpan = 1 },
- new JoinMetadata { Description = "Custom8", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Custom9")]
- public JoinDataComplete Custom9 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1 },
- new JoinMetadata { Description = "Custom9", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Fav")]
- public JoinDataComplete Fav = new JoinDataComplete(new JoinData { JoinNumber = 51, JoinSpan = 1 },
- new JoinMetadata { Description = "Fav", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Home")]
- public JoinDataComplete Home = new JoinDataComplete(new JoinData { JoinNumber = 52, JoinSpan = 1 },
- new JoinMetadata { Description = "Home", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("BatteryLow")]
- public JoinDataComplete BatteryLow = new JoinDataComplete(new JoinData { JoinNumber = 53, JoinSpan = 1 },
- new JoinMetadata { Description = "BatteryLow", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("BatteryCritical")]
- public JoinDataComplete BatteryCritical = new JoinDataComplete(new JoinData { JoinNumber = 54, JoinSpan = 1 },
- new JoinMetadata { Description = "BatteryCritical", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("BatteryVoltage")]
- public JoinDataComplete BatteryVoltage = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "BatteryVoltage", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a Hrxxx0WirelessRemoteControllerJoinMap
///
- /// Join this join map will start at
- public Hrxxx0WirelessRemoteControllerJoinMap(uint joinStart)
- : this(joinStart, typeof(Hrxxx0WirelessRemoteControllerJoinMap))
+ public class Hrxxx0WirelessRemoteControllerJoinMap : JoinMapBaseAdvanced
{
- }
+ ///
+ /// Power
+ ///
+ [JoinName("Power")]
+ public JoinDataComplete Power = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Power", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected Hrxxx0WirelessRemoteControllerJoinMap(uint joinStart, Type type)
- : base(joinStart, type)
- {
+ ///
+ /// Menu
+ ///
+ [JoinName("Menu")]
+ public JoinDataComplete Menu = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Menu", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Guide
+ ///
+ [JoinName("Guide")]
+ public JoinDataComplete Guide = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Guide", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Info
+ ///
+ [JoinName("Info")]
+ public JoinDataComplete Info = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "Info", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// VolumeUp
+ ///
+ [JoinName("VolumeUp")]
+ public JoinDataComplete VolumeUp = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "VolumeUp", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// VolumeDown
+ ///
+ [JoinName("VolumeDown")]
+ public JoinDataComplete VolumeDown = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
+ new JoinMetadata { Description = "VolumeDown", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DialPadUp
+ ///
+ [JoinName("DialPadUp")]
+ public JoinDataComplete DialPadUp = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
+ new JoinMetadata { Description = "DialPadUp", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DialPadDown
+ ///
+ [JoinName("DialPadDown")]
+ public JoinDataComplete DialPadDown = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
+ new JoinMetadata { Description = "DialPadDown", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DialPadLeft
+ ///
+ [JoinName("DialPadLeft")]
+ public JoinDataComplete DialPadLeft = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 },
+ new JoinMetadata { Description = "DialPadLeft", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DialPadRight
+ ///
+ [JoinName("DialPadRight")]
+ public JoinDataComplete DialPadRight = new JoinDataComplete(new JoinData { JoinNumber = 10, JoinSpan = 1 },
+ new JoinMetadata { Description = "DialPadRight", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// DialPadSelect
+ ///
+ [JoinName("DialPadSelect")]
+ public JoinDataComplete DialPadSelect = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
+ new JoinMetadata { Description = "DialPadSelect", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// ChannelUp
+ ///
+ [JoinName("ChannelUp")]
+ public JoinDataComplete ChannelUp = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
+ new JoinMetadata { Description = "ChannelUp", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// ChannelDown
+ ///
+ [JoinName("ChannelDown")]
+ public JoinDataComplete ChannelDown = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
+ new JoinMetadata { Description = "ChannelDown", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Mute
+ ///
+ [JoinName("Mute")]
+ public JoinDataComplete Mute = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
+ new JoinMetadata { Description = "Mute", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Exit
+ ///
+ [JoinName("Exit")]
+ public JoinDataComplete Exit = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
+ new JoinMetadata { Description = "Exit", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Last
+ ///
+ [JoinName("Last")]
+ public JoinDataComplete Last = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 1 },
+ new JoinMetadata { Description = "Last", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Play
+ ///
+ [JoinName("Play")]
+ public JoinDataComplete Play = new JoinDataComplete(new JoinData { JoinNumber = 17, JoinSpan = 1 },
+ new JoinMetadata { Description = "Play", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Pause
+ ///
+ [JoinName("Pause")]
+ public JoinDataComplete Pause = new JoinDataComplete(new JoinData { JoinNumber = 18, JoinSpan = 1 },
+ new JoinMetadata { Description = "Pause", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Rewind
+ ///
+ [JoinName("Rewind")]
+ public JoinDataComplete Rewind = new JoinDataComplete(new JoinData { JoinNumber = 19, JoinSpan = 1 },
+ new JoinMetadata { Description = "Rewind", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// FastForward
+ ///
+ [JoinName("FastForward")]
+ public JoinDataComplete FastForward = new JoinDataComplete(new JoinData { JoinNumber = 20, JoinSpan = 1 },
+ new JoinMetadata { Description = "FastForward", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// PreviousTrack
+ ///
+ [JoinName("PreviousTrack")]
+ public JoinDataComplete PreviousTrack = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },
+ new JoinMetadata { Description = "PreviousTrack", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// NextTrack
+ ///
+ [JoinName("NextTrack")]
+ public JoinDataComplete NextTrack = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },
+ new JoinMetadata { Description = "NextTrack", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Stop
+ ///
+ [JoinName("Stop")]
+ public JoinDataComplete Stop = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },
+ new JoinMetadata { Description = "Stop", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Record
+ ///
+ [JoinName("Record")]
+ public JoinDataComplete Record = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },
+ new JoinMetadata { Description = "Record", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Dvr
+ ///
+ [JoinName("Dvr")]
+ public JoinDataComplete Dvr = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },
+ new JoinMetadata { Description = "Dvr", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Keypad1
+ ///
+ [JoinName("Keypad1")]
+ public JoinDataComplete Keypad1 = new JoinDataComplete(new JoinData { JoinNumber = 26, JoinSpan = 1 },
+ new JoinMetadata { Description = "Keypad1", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Keypad2Abc
+ ///
+ [JoinName("Keypad2Abc")]
+ public JoinDataComplete Keypad2 = new JoinDataComplete(new JoinData { JoinNumber = 27, JoinSpan = 1 },
+ new JoinMetadata { Description = "Keypad2Abc", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Keypad3Def
+ ///
+ [JoinName("Keypad3Def")]
+ public JoinDataComplete Keypad3Def = new JoinDataComplete(new JoinData { JoinNumber = 28, JoinSpan = 1 },
+ new JoinMetadata { Description = "Keypad3Def", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Keypad4Ghi
+ ///
+ [JoinName("Keypad4Ghi")]
+ public JoinDataComplete Keypad4Ghi = new JoinDataComplete(new JoinData { JoinNumber = 29, JoinSpan = 1 },
+ new JoinMetadata { Description = "Keypad4Ghi", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Keypad5Jkl
+ ///
+ [JoinName("Keypad5Jkl")]
+ public JoinDataComplete Keypad5Jkl = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },
+ new JoinMetadata { Description = "Keypad5Jkl", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Keypad6Mno
+ ///
+ [JoinName("Keypad6Mno")]
+ public JoinDataComplete Keypad6Mno = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 1 },
+ new JoinMetadata { Description = "Keypad6Mno", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Keypad7Pqrs
+ ///
+ [JoinName("Keypad7Pqrs")]
+ public JoinDataComplete Keypad7Pqrs = new JoinDataComplete(new JoinData { JoinNumber = 32, JoinSpan = 1 },
+ new JoinMetadata { Description = "Keypad7Pqrs", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Keypad8Tuv
+ ///
+ [JoinName("Keypad8Tuv")]
+ public JoinDataComplete Keypad8Tuv = new JoinDataComplete(new JoinData { JoinNumber = 33, JoinSpan = 1 },
+ new JoinMetadata { Description = "Keypad8Tuv", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Keypad9Wxyz
+ ///
+ [JoinName("Keypad9Wxyz")]
+ public JoinDataComplete Keypad9Wxyz = new JoinDataComplete(new JoinData { JoinNumber = 34, JoinSpan = 1 },
+ new JoinMetadata { Description = "Keypad9Wxyz", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Keypad0
+ ///
+ [JoinName("Keypad0")]
+ public JoinDataComplete Keypad0 = new JoinDataComplete(new JoinData { JoinNumber = 35, JoinSpan = 1 },
+ new JoinMetadata { Description = "Keypad0", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Clear
+ ///
+ [JoinName("Clear")]
+ public JoinDataComplete Clear = new JoinDataComplete(new JoinData { JoinNumber = 36, JoinSpan = 1 },
+ new JoinMetadata { Description = "Clear", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Enter
+ ///
+ [JoinName("Enter")]
+ public JoinDataComplete Enter = new JoinDataComplete(new JoinData { JoinNumber = 37, JoinSpan = 1 },
+ new JoinMetadata { Description = "Enter", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Red
+ ///
+ [JoinName("Red")]
+ public JoinDataComplete Red = new JoinDataComplete(new JoinData { JoinNumber = 38, JoinSpan = 1 },
+ new JoinMetadata { Description = "Red", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Green
+ ///
+ [JoinName("Green")]
+ public JoinDataComplete Green = new JoinDataComplete(new JoinData { JoinNumber = 39, JoinSpan = 1 },
+ new JoinMetadata { Description = "Green", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Yellow
+ ///
+ [JoinName("Yellow")]
+ public JoinDataComplete Yellow = new JoinDataComplete(new JoinData { JoinNumber = 40, JoinSpan = 1 },
+ new JoinMetadata { Description = "Yellow", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Blue
+ ///
+ [JoinName("Blue")]
+ public JoinDataComplete Blue = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 },
+ new JoinMetadata { Description = "Blue", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Custom1
+ ///
+ [JoinName("Custom1")]
+ public JoinDataComplete Custom1 = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },
+ new JoinMetadata { Description = "Custom1", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Custom2
+ ///
+ [JoinName("Custom2")]
+ public JoinDataComplete Custom2 = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },
+ new JoinMetadata { Description = "Custom2", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Custom3
+ ///
+ [JoinName("Custom3")]
+ public JoinDataComplete Custom3 = new JoinDataComplete(new JoinData { JoinNumber = 44, JoinSpan = 1 },
+ new JoinMetadata { Description = "Custom3", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Custom4
+ ///
+ [JoinName("Custom4")]
+ public JoinDataComplete Custom4 = new JoinDataComplete(new JoinData { JoinNumber = 45, JoinSpan = 1 },
+ new JoinMetadata { Description = "Custom4", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Custom5
+ ///
+ [JoinName("Custom5")]
+ public JoinDataComplete Custom5 = new JoinDataComplete(new JoinData { JoinNumber = 46, JoinSpan = 1 },
+ new JoinMetadata { Description = "Custom5", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Custom6
+ ///
+ [JoinName("Custom6")]
+ public JoinDataComplete Custom6 = new JoinDataComplete(new JoinData { JoinNumber = 47, JoinSpan = 1 },
+ new JoinMetadata { Description = "Custom6", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Custom7
+ ///
+ [JoinName("Custom7")]
+ public JoinDataComplete Custom7 = new JoinDataComplete(new JoinData { JoinNumber = 48, JoinSpan = 1 },
+ new JoinMetadata { Description = "Custom7", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Custom8
+ ///
+ [JoinName("Custom8")]
+ public JoinDataComplete Custom8 = new JoinDataComplete(new JoinData { JoinNumber = 49, JoinSpan = 1 },
+ new JoinMetadata { Description = "Custom8", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Custom9
+ ///
+ [JoinName("Custom9")]
+ public JoinDataComplete Custom9 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1 },
+ new JoinMetadata { Description = "Custom9", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Fav
+ ///
+ [JoinName("Fav")]
+ public JoinDataComplete Fav = new JoinDataComplete(new JoinData { JoinNumber = 51, JoinSpan = 1 },
+ new JoinMetadata { Description = "Fav", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Home
+ ///
+ [JoinName("Home")]
+ public JoinDataComplete Home = new JoinDataComplete(new JoinData { JoinNumber = 52, JoinSpan = 1 },
+ new JoinMetadata { Description = "Home", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// BatteryLow
+ ///
+ [JoinName("BatteryLow")]
+ public JoinDataComplete BatteryLow = new JoinDataComplete(new JoinData { JoinNumber = 53, JoinSpan = 1 },
+ new JoinMetadata { Description = "BatteryLow", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// BatteryCritical
+ ///
+ [JoinName("BatteryCritical")]
+ public JoinDataComplete BatteryCritical = new JoinDataComplete(new JoinData { JoinNumber = 54, JoinSpan = 1 },
+ new JoinMetadata { Description = "BatteryCritical", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// BatteryVoltage
+ ///
+ [JoinName("BatteryVoltage")]
+ public JoinDataComplete BatteryVoltage = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "BatteryVoltage", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public Hrxxx0WirelessRemoteControllerJoinMap(uint joinStart)
+ : this(joinStart, typeof(Hrxxx0WirelessRemoteControllerJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected Hrxxx0WirelessRemoteControllerJoinMap(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IAnalogInputJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IAnalogInputJoinMap.cs
index 80509ecc..59f90347 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IAnalogInputJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IAnalogInputJoinMap.cs
@@ -1,33 +1,44 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class IAnalogInputJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
-
- [JoinName("InputValue")]
- public JoinDataComplete InputValue = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Input Value", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
- [JoinName("MinimumChange")]
- public JoinDataComplete MinimumChange = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Minimum voltage change required to reflect a change", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a IAnalogInputJoinMap
///
- /// Join this join map will start at
- public IAnalogInputJoinMap(uint joinStart)
- : this(joinStart, typeof(IAnalogInputJoinMap))
+ public class IAnalogInputJoinMap : JoinMapBaseAdvanced
{
- }
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected IAnalogInputJoinMap(uint joinStart, Type type)
- : base(joinStart, type)
- {
+ ///
+ /// Input Value
+ ///
+ [JoinName("InputValue")]
+ public JoinDataComplete InputValue = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Input Value", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Minimum Change
+ ///
+ [JoinName("MinimumChange")]
+ public JoinDataComplete MinimumChange = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Minimum voltage change required to reflect a change", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public IAnalogInputJoinMap(uint joinStart)
+ : this(joinStart, typeof(IAnalogInputJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected IAnalogInputJoinMap(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IBasicCommunicationJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IBasicCommunicationJoinMap.cs
index 1db5b1ff..b4c473cb 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IBasicCommunicationJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IBasicCommunicationJoinMap.cs
@@ -1,49 +1,71 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class IBasicCommunicationJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("TextReceived")]
- public JoinDataComplete TextReceived = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Text Received From Remote Device", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("SendText")]
- public JoinDataComplete SendText = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Text Sent To Remote Device", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("SetPortConfig")]
- public JoinDataComplete SetPortConfig = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Set Port Config", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("Connect")]
- public JoinDataComplete Connect = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Connect", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Connected")]
- public JoinDataComplete Connected = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Connected", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Status")]
- public JoinDataComplete Status = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Status", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a IBasicCommunicationJoinMap
///
- /// Join this join map will start at
- public IBasicCommunicationJoinMap(uint joinStart)
- : this(joinStart, typeof(IBasicCommunicationJoinMap))
+ public class IBasicCommunicationJoinMap : JoinMapBaseAdvanced
{
- }
+ ///
+ /// Text Received From Remote Device
+ ///
+ [JoinName("TextReceived")]
+ public JoinDataComplete TextReceived = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Text Received From Remote Device", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected IBasicCommunicationJoinMap(uint joinStart, Type type)
- : base(joinStart, type)
- {
+ ///
+ /// Text Sent To Remote Device
+ ///
+ [JoinName("SendText")]
+ public JoinDataComplete SendText = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Text Sent To Remote Device", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Set Port Config
+ ///
+ [JoinName("SetPortConfig")]
+ public JoinDataComplete SetPortConfig = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Set Port Config", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Connect
+ ///
+ [JoinName("Connect")]
+ public JoinDataComplete Connect = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Connect", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Disconnect
+ ///
+ [JoinName("Connected")]
+ public JoinDataComplete Connected = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Connected", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Status
+ ///
+ [JoinName("Status")]
+ public JoinDataComplete Status = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Status", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public IBasicCommunicationJoinMap(uint joinStart)
+ : this(joinStart, typeof(IBasicCommunicationJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected IBasicCommunicationJoinMap(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IDigitalInputJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IDigitalInputJoinMap.cs
index ceed325d..0f77fce3 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IDigitalInputJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IDigitalInputJoinMap.cs
@@ -1,30 +1,36 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class IDigitalInputJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
-
- [JoinName("InputState")]
- public JoinDataComplete InputState = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Input State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a IDigitalInputJoinMap
///
- /// Join this join map will start at
- public IDigitalInputJoinMap(uint joinStart)
- : this(joinStart, typeof(IDigitalInputJoinMap))
+ public class IDigitalInputJoinMap : JoinMapBaseAdvanced
{
- }
+ ///
+ /// Input State
+ ///
+ [JoinName("InputState")]
+ public JoinDataComplete InputState = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Input State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected IDigitalInputJoinMap(uint joinStart, Type type)
- : base(joinStart, type)
- {
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public IDigitalInputJoinMap(uint joinStart)
+ : this(joinStart, typeof(IDigitalInputJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected IDigitalInputJoinMap(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IDigitalOutputJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IDigitalOutputJoinMap.cs
index 8abb2b74..05895acb 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IDigitalOutputJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IDigitalOutputJoinMap.cs
@@ -1,30 +1,36 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class IDigitalOutputJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
-
- [JoinName("OutputState")]
- public JoinDataComplete OutputState = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Get / Set state of Digital Input", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a IDigitalOutputJoinMap
///
- /// Join this join map will start at
- public IDigitalOutputJoinMap(uint joinStart)
- : this(joinStart, typeof(IDigitalOutputJoinMap))
+ public class IDigitalOutputJoinMap : JoinMapBaseAdvanced
{
- }
+ ///
+ /// Output State
+ ///
+ [JoinName("OutputState")]
+ public JoinDataComplete OutputState = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Get / Set state of Digital Input", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected IDigitalOutputJoinMap(uint joinStart, Type type)
- : base(joinStart, type)
- {
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public IDigitalOutputJoinMap(uint joinStart)
+ : this(joinStart, typeof(IDigitalOutputJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected IDigitalOutputJoinMap(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IRBlurayBaseJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IRBlurayBaseJoinMap.cs
index d63a1f52..3e66eda6 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IRBlurayBaseJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/IRBlurayBaseJoinMap.cs
@@ -4,218 +4,359 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Essentials.Core.Bridges;
-
-///
-/// Join map for IRBlurayBase devices
-///
-public class IRBlurayBaseJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("PowerOn")]
- public JoinDataComplete PowerOn = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Power On", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("PowerOff")]
- public JoinDataComplete PowerOff = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Power Off", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("PowerToggle")]
- public JoinDataComplete PowerToggle = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Power Toggle", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Up")]
- public JoinDataComplete Up = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "Nav Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Down")]
- public JoinDataComplete Down = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "Nav Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Left")]
- public JoinDataComplete Left = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
- new JoinMetadata { Description = "Nav Left", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Right")]
- public JoinDataComplete Right = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
- new JoinMetadata { Description = "Nav Right", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Select")]
- public JoinDataComplete Select = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
- new JoinMetadata { Description = "Select", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Menu")]
- public JoinDataComplete Menu = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 },
- new JoinMetadata { Description = "Menu", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Exit")]
- public JoinDataComplete Exit = new JoinDataComplete(new JoinData { JoinNumber = 10, JoinSpan = 1 },
- new JoinMetadata { Description = "Exit", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit0")]
- public JoinDataComplete Digit0 = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
- new JoinMetadata { Description = "Digit 0", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit1")]
- public JoinDataComplete Digit1 = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
- new JoinMetadata { Description = "Digit 1", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit2")]
- public JoinDataComplete Digit2 = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
- new JoinMetadata { Description = "Digit 2", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit3")]
- public JoinDataComplete Digit3 = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
- new JoinMetadata { Description = "Digit 3", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit4")]
- public JoinDataComplete Digit4 = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
- new JoinMetadata { Description = "Digit 4", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit5")]
- public JoinDataComplete Digit5 = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 1 },
- new JoinMetadata { Description = "Digit 5", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit6")]
- public JoinDataComplete Digit6 = new JoinDataComplete(new JoinData { JoinNumber = 17, JoinSpan = 1 },
- new JoinMetadata { Description = "Digit 6", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit7")]
- public JoinDataComplete Digit7 = new JoinDataComplete(new JoinData { JoinNumber = 18, JoinSpan = 1 },
- new JoinMetadata { Description = "Digit 7", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit8")]
- public JoinDataComplete Digit8 = new JoinDataComplete(new JoinData { JoinNumber = 19, JoinSpan = 1 },
- new JoinMetadata { Description = "Digit 8", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit9")]
- public JoinDataComplete Digit9 = new JoinDataComplete(new JoinData { JoinNumber = 20, JoinSpan = 1 },
- new JoinMetadata { Description = "Digit 9", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("KeypadClear")]
- public JoinDataComplete KeypadClear = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },
- new JoinMetadata { Description = "Keypad Clear", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("KeypadEnter")]
- public JoinDataComplete KeypadEnter = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },
- new JoinMetadata { Description = "Keypad Enter", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("ChannelUp")]
- public JoinDataComplete ChannelUp = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Channel Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("ChannelDown")]
- public JoinDataComplete ChannelDown = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Channel Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("LastChannel")]
- public JoinDataComplete LastChannel = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },
- new JoinMetadata { Description = "Last Channel", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Guide")]
- public JoinDataComplete Guide = new JoinDataComplete(new JoinData { JoinNumber = 26, JoinSpan = 1 },
- new JoinMetadata { Description = "Guide", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Info")]
- public JoinDataComplete Info = new JoinDataComplete(new JoinData { JoinNumber = 27, JoinSpan = 1 },
- new JoinMetadata { Description = "Info", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Red")]
- public JoinDataComplete Red = new JoinDataComplete(new JoinData { JoinNumber = 28, JoinSpan = 1 },
- new JoinMetadata { Description = "Red", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Green")]
- public JoinDataComplete Green = new JoinDataComplete(new JoinData { JoinNumber = 29, JoinSpan = 1 },
- new JoinMetadata { Description = "Green", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Yellow")]
- public JoinDataComplete Yellow = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },
- new JoinMetadata { Description = "Yellow", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Blue")]
- public JoinDataComplete Blue = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 1 },
- new JoinMetadata { Description = "Blue", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
-
- [JoinName("Play")]
- public JoinDataComplete Play = new JoinDataComplete(new JoinData { JoinNumber = 33, JoinSpan = 1 },
- new JoinMetadata { Description = "Play", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Pause")]
- public JoinDataComplete Pause = new JoinDataComplete(new JoinData { JoinNumber = 34, JoinSpan = 1 },
- new JoinMetadata { Description = "Pause", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Stop")]
- public JoinDataComplete Stop = new JoinDataComplete(new JoinData { JoinNumber = 35, JoinSpan = 1 },
- new JoinMetadata { Description = "Stop", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("FFwd")]
- public JoinDataComplete FFwd = new JoinDataComplete(new JoinData { JoinNumber = 36, JoinSpan = 1 },
- new JoinMetadata { Description = "FFwd", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Rewind")]
- public JoinDataComplete Rewind = new JoinDataComplete(new JoinData { JoinNumber = 37, JoinSpan = 1 },
- new JoinMetadata { Description = "Rewind", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("ChapPlus")]
- public JoinDataComplete ChapPlus = new JoinDataComplete(new JoinData { JoinNumber = 38, JoinSpan = 1 },
- new JoinMetadata { Description = "Chapter Plus", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("ChapMinus")]
- public JoinDataComplete ChapMinus = new JoinDataComplete(new JoinData { JoinNumber = 39, JoinSpan = 1 },
- new JoinMetadata { Description = "Chapter Minus", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Replay")]
- public JoinDataComplete Replay = new JoinDataComplete(new JoinData { JoinNumber = 40, JoinSpan = 1 },
- new JoinMetadata { Description = "Replay", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Record")]
- public JoinDataComplete Record = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 },
- new JoinMetadata { Description = "Record", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("HasKeypadAccessoryButton1")]
- public JoinDataComplete HasKeypadAccessoryButton1 = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },
- new JoinMetadata { Description = "Has Keypad Accessory Button 1", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("HasKeypadAccessoryButton2")]
- public JoinDataComplete HasKeypadAccessoryButton2 = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },
- new JoinMetadata { Description = "Has Keypad Accessory Button 2", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("KeypadAccessoryButton1Press")]
- public JoinDataComplete KeypadAccessoryButton1Press = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 2 },
- new JoinMetadata { Description = "Keypad Accessory Button 1 Press", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("KeypadAccessoryButton2Press")]
- public JoinDataComplete KeypadAccessoryButton2Press = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 2 },
- new JoinMetadata { Description = "Keypad Accessory Button 2 Press", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Name")]
- public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("KeypadAccessoryButton1Label")]
- public JoinDataComplete KeypadAccessoryButton1Label = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },
- new JoinMetadata { Description = "Keypad Accessory Button 1 Label", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("KeypadAccessoryButton2Label")]
- public JoinDataComplete KeypadAccessoryButton2Label = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },
- new JoinMetadata { Description = "Keypad Accessory Button 1 Label", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- ///
- /// Constructor to use when instantiating this Join Map without inheriting from it
- ///
- /// Join this join map will start at
- public IRBlurayBaseJoinMap(uint joinStart)
- : this(joinStart, typeof(IRBlurayBaseJoinMap))
- {
- }
-
///
- /// Constructor to use when extending this Join map
+ /// Join map for IRBlurayBase devices
///
- /// Join this join map will start at
- /// Type of the child join map
- protected IRBlurayBaseJoinMap(uint joinStart, Type type)
- : base(joinStart, type)
+ public class IRBlurayBaseJoinMap : JoinMapBaseAdvanced
{
+ ///
+ /// Power On
+ ///
+ [JoinName("PowerOn")]
+ public JoinDataComplete PowerOn = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Power On", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Power Off
+ ///
+ [JoinName("PowerOff")]
+ public JoinDataComplete PowerOff = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Power Off", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Power Toggle
+ ///
+ [JoinName("PowerToggle")]
+ public JoinDataComplete PowerToggle = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Power Toggle", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Nav Up
+ ///
+ [JoinName("Up")]
+ public JoinDataComplete Up = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "Nav Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Nav Down
+ ///
+ [JoinName("Down")]
+ public JoinDataComplete Down = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "Nav Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Nav Left
+ ///
+ [JoinName("Left")]
+ public JoinDataComplete Left = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
+ new JoinMetadata { Description = "Nav Left", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Nav Right
+ ///
+ [JoinName("Right")]
+ public JoinDataComplete Right = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
+ new JoinMetadata { Description = "Nav Right", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Select
+ ///
+ [JoinName("Select")]
+ public JoinDataComplete Select = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
+ new JoinMetadata { Description = "Select", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Menu
+ ///
+ [JoinName("Menu")]
+ public JoinDataComplete Menu = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 },
+ new JoinMetadata { Description = "Menu", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Exit
+ ///
+ [JoinName("Exit")]
+ public JoinDataComplete Exit = new JoinDataComplete(new JoinData { JoinNumber = 10, JoinSpan = 1 },
+ new JoinMetadata { Description = "Exit", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Digit 0
+ ///
+ [JoinName("Digit0")]
+ public JoinDataComplete Digit0 = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
+ new JoinMetadata { Description = "Digit 0", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Digit 1
+ ///
+ [JoinName("Digit1")]
+ public JoinDataComplete Digit1 = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
+ new JoinMetadata { Description = "Digit 1", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Digit 2
+ ///
+ [JoinName("Digit2")]
+ public JoinDataComplete Digit2 = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
+ new JoinMetadata { Description = "Digit 2", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Digit 3
+ ///
+ [JoinName("Digit3")]
+ public JoinDataComplete Digit3 = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
+ new JoinMetadata { Description = "Digit 3", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Digit 4
+ ///
+ [JoinName("Digit4")]
+ public JoinDataComplete Digit4 = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
+ new JoinMetadata { Description = "Digit 4", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Digit 5
+ ///
+ [JoinName("Digit5")]
+ public JoinDataComplete Digit5 = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 1 },
+ new JoinMetadata { Description = "Digit 5", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Digit 6
+ ///
+ [JoinName("Digit6")]
+ public JoinDataComplete Digit6 = new JoinDataComplete(new JoinData { JoinNumber = 17, JoinSpan = 1 },
+ new JoinMetadata { Description = "Digit 6", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Digit 7
+ ///
+ [JoinName("Digit7")]
+ public JoinDataComplete Digit7 = new JoinDataComplete(new JoinData { JoinNumber = 18, JoinSpan = 1 },
+ new JoinMetadata { Description = "Digit 7", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Digit 8
+ ///
+ [JoinName("Digit8")]
+ public JoinDataComplete Digit8 = new JoinDataComplete(new JoinData { JoinNumber = 19, JoinSpan = 1 },
+ new JoinMetadata { Description = "Digit 8", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Digit 9
+ ///
+ [JoinName("Digit9")]
+ public JoinDataComplete Digit9 = new JoinDataComplete(new JoinData { JoinNumber = 20, JoinSpan = 1 },
+ new JoinMetadata { Description = "Digit 9", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Keypad Clear
+ ///
+ [JoinName("KeypadClear")]
+ public JoinDataComplete KeypadClear = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },
+ new JoinMetadata { Description = "Keypad Clear", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Keypad Enter
+ ///
+ [JoinName("KeypadEnter")]
+ public JoinDataComplete KeypadEnter = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },
+ new JoinMetadata { Description = "Keypad Enter", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Channel Up
+ ///
+ [JoinName("ChannelUp")]
+ public JoinDataComplete ChannelUp = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Channel Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Channel Down
+ ///
+ [JoinName("ChannelDown")]
+ public JoinDataComplete ChannelDown = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Channel Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Last Channel
+ ///
+ [JoinName("LastChannel")]
+ public JoinDataComplete LastChannel = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },
+ new JoinMetadata { Description = "Last Channel", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Guide
+ ///
+ [JoinName("Guide")]
+ public JoinDataComplete Guide = new JoinDataComplete(new JoinData { JoinNumber = 26, JoinSpan = 1 },
+ new JoinMetadata { Description = "Guide", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Info
+ ///
+ [JoinName("Info")]
+ public JoinDataComplete Info = new JoinDataComplete(new JoinData { JoinNumber = 27, JoinSpan = 1 },
+ new JoinMetadata { Description = "Info", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Red
+ ///
+ [JoinName("Red")]
+ public JoinDataComplete Red = new JoinDataComplete(new JoinData { JoinNumber = 28, JoinSpan = 1 },
+ new JoinMetadata { Description = "Red", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Green
+ ///
+ [JoinName("Green")]
+ public JoinDataComplete Green = new JoinDataComplete(new JoinData { JoinNumber = 29, JoinSpan = 1 },
+ new JoinMetadata { Description = "Green", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Yellow
+ ///
+ [JoinName("Yellow")]
+ public JoinDataComplete Yellow = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },
+ new JoinMetadata { Description = "Yellow", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Blue
+ ///
+ [JoinName("Blue")]
+ public JoinDataComplete Blue = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 1 },
+ new JoinMetadata { Description = "Blue", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Play
+ ///
+ [JoinName("Play")]
+ public JoinDataComplete Play = new JoinDataComplete(new JoinData { JoinNumber = 33, JoinSpan = 1 },
+ new JoinMetadata { Description = "Play", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Pause
+ ///
+ [JoinName("Pause")]
+ public JoinDataComplete Pause = new JoinDataComplete(new JoinData { JoinNumber = 34, JoinSpan = 1 },
+ new JoinMetadata { Description = "Pause", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Stop
+ ///
+ [JoinName("Stop")]
+ public JoinDataComplete Stop = new JoinDataComplete(new JoinData { JoinNumber = 35, JoinSpan = 1 },
+ new JoinMetadata { Description = "Stop", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Fast Forward
+ ///
+ [JoinName("FFwd")]
+ public JoinDataComplete FFwd = new JoinDataComplete(new JoinData { JoinNumber = 36, JoinSpan = 1 },
+ new JoinMetadata { Description = "FFwd", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Rewind
+ ///
+ [JoinName("Rewind")]
+ public JoinDataComplete Rewind = new JoinDataComplete(new JoinData { JoinNumber = 37, JoinSpan = 1 },
+ new JoinMetadata { Description = "Rewind", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Chapter Plus
+ ///
+ [JoinName("ChapPlus")]
+ public JoinDataComplete ChapPlus = new JoinDataComplete(new JoinData { JoinNumber = 38, JoinSpan = 1 },
+ new JoinMetadata { Description = "Chapter Plus", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Chapter Minus
+ ///
+ [JoinName("ChapMinus")]
+ public JoinDataComplete ChapMinus = new JoinDataComplete(new JoinData { JoinNumber = 39, JoinSpan = 1 },
+ new JoinMetadata { Description = "Chapter Minus", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Replay
+ ///
+ [JoinName("Replay")]
+ public JoinDataComplete Replay = new JoinDataComplete(new JoinData { JoinNumber = 40, JoinSpan = 1 },
+ new JoinMetadata { Description = "Replay", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Record
+ ///
+ [JoinName("Record")]
+ public JoinDataComplete Record = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 },
+ new JoinMetadata { Description = "Record", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Has Keypad Accessory Button 1
+ ///
+ [JoinName("HasKeypadAccessoryButton1")]
+ public JoinDataComplete HasKeypadAccessoryButton1 = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },
+ new JoinMetadata { Description = "Has Keypad Accessory Button 1", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Has Keypad Accessory Button 2
+ ///
+ [JoinName("HasKeypadAccessoryButton2")]
+ public JoinDataComplete HasKeypadAccessoryButton2 = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },
+ new JoinMetadata { Description = "Has Keypad Accessory Button 2", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Keypad Accessory Button 1 Press
+ ///
+ [JoinName("KeypadAccessoryButton1Press")]
+ public JoinDataComplete KeypadAccessoryButton1Press = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 2 },
+ new JoinMetadata { Description = "Keypad Accessory Button 1 Press", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Keypad Accessory Button 2 Press
+ ///
+ [JoinName("KeypadAccessoryButton2Press")]
+ public JoinDataComplete KeypadAccessoryButton2Press = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 2 },
+ new JoinMetadata { Description = "Keypad Accessory Button 2 Press", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Name
+ ///
+ [JoinName("Name")]
+ public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Keypad Accessory Button 1 Label
+ ///
+ [JoinName("KeypadAccessoryButton1Label")]
+ public JoinDataComplete KeypadAccessoryButton1Label = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },
+ new JoinMetadata { Description = "Keypad Accessory Button 1 Label", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Keypad Accessory Button 2 Label
+ ///
+ [JoinName("KeypadAccessoryButton2Label")]
+ public JoinDataComplete KeypadAccessoryButton2Label = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },
+ new JoinMetadata { Description = "Keypad Accessory Button 1 Label", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public IRBlurayBaseJoinMap(uint joinStart)
+ : this(joinStart, typeof(IRBlurayBaseJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected IRBlurayBaseJoinMap(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/PduJoinMapBase.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/PduJoinMapBase.cs
index 10bf590d..f52b96c4 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/PduJoinMapBase.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/PduJoinMapBase.cs
@@ -1,59 +1,87 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class PduJoinMapBase : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("Name")]
- public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "PDU Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("Online")]
- public JoinDataComplete Online = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("OutletCount")]
- public JoinDataComplete OutletCount = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Number of COntrolled Outlets", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("OutletName")]
- public JoinDataComplete OutletName = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
- new JoinMetadata { Description = "Outlet Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("OutletEnabled")]
- public JoinDataComplete OutletEnabled = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
- new JoinMetadata { Description = "Outlet Enabled", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("OutletPowerCycle")]
- public JoinDataComplete OutletPowerCycle = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
- new JoinMetadata { Description = "Outlet Power Cycle", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("OutletPowerOn")]
- public JoinDataComplete OutletPowerOn = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
- new JoinMetadata { Description = "Outlet Power On", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("OutletPowerOff")]
- public JoinDataComplete OutletPowerOff = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
- new JoinMetadata { Description = "Outlet Power Off", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
-
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a PduJoinMapBase
///
- /// Join this join map will start at
- public PduJoinMapBase(uint joinStart)
- :base(joinStart, typeof(PduJoinMapBase))
- {
- }
+ public class PduJoinMapBase : JoinMapBaseAdvanced
+ {
+ ///
+ /// PDU Name
+ ///
+ [JoinName("Name")]
+ public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "PDU Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- public PduJoinMapBase(uint joinStart, Type type)
- : base(joinStart, type)
- {
+ ///
+ /// PDU Online Status
+ ///
+ [JoinName("Online")]
+ public JoinDataComplete Online = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Number of Controlled Outlets
+ ///
+ [JoinName("OutletCount")]
+ public JoinDataComplete OutletCount = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Number of COntrolled Outlets", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Outlet Name
+ ///
+ [JoinName("OutletName")]
+ public JoinDataComplete OutletName = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
+ new JoinMetadata { Description = "Outlet Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// Outlet Enabled Status
+ ///
+ [JoinName("OutletEnabled")]
+ public JoinDataComplete OutletEnabled = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
+ new JoinMetadata { Description = "Outlet Enabled", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Outlet Power State
+ ///
+ [JoinName("OutletPowerCycle")]
+ public JoinDataComplete OutletPowerCycle = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
+ new JoinMetadata { Description = "Outlet Power Cycle", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Outlet Power On
+ ///
+ [JoinName("OutletPowerOn")]
+ public JoinDataComplete OutletPowerOn = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
+ new JoinMetadata { Description = "Outlet Power On", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Outlet Power Off
+ ///
+ [JoinName("OutletPowerOff")]
+ public JoinDataComplete OutletPowerOff = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
+ new JoinMetadata { Description = "Outlet Power Off", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public PduJoinMapBase(uint joinStart)
+ :base(joinStart, typeof(PduJoinMapBase))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ public PduJoinMapBase(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/SetTopBoxControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/SetTopBoxControllerJoinMap.cs
index 0705eb93..ce9a4290 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/SetTopBoxControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/SetTopBoxControllerJoinMap.cs
@@ -1,239 +1,402 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class SetTopBoxControllerJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("PowerOn")]
- public JoinDataComplete PowerOn = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Power On", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("PowerOff")]
- public JoinDataComplete PowerOff = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Power Off", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("PowerToggle")]
- public JoinDataComplete PowerToggle = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Power Toggle", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("HasDpad")]
- public JoinDataComplete HasDpad = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Has DPad", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Up")]
- public JoinDataComplete Up = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Nav Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Down")]
- public JoinDataComplete Down = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Nav Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Left")]
- public JoinDataComplete Left = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Nav Left", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Right")]
- public JoinDataComplete Right = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Nav Right", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Select")]
- public JoinDataComplete Select = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Select", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Menu")]
- public JoinDataComplete Menu = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Menu", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Exit")]
- public JoinDataComplete Exit = new JoinDataComplete(new JoinData { JoinNumber = 10, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Exit", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("HasNumeric")]
- public JoinDataComplete HasNumeric = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Has Numeric", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit0")]
- public JoinDataComplete Digit0 = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Digit 0", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit1")]
- public JoinDataComplete Digit1 = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Digit 1", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit2")]
- public JoinDataComplete Digit2 = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Digit 2", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit3")]
- public JoinDataComplete Digit3 = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Digit 3", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit4")]
- public JoinDataComplete Digit4 = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Digit 4", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit5")]
- public JoinDataComplete Digit5 = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Digit 5", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit6")]
- public JoinDataComplete Digit6 = new JoinDataComplete(new JoinData { JoinNumber = 17, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Digit 6", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit7")]
- public JoinDataComplete Digit7 = new JoinDataComplete(new JoinData { JoinNumber = 18, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Digit 7", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit8")]
- public JoinDataComplete Digit8 = new JoinDataComplete(new JoinData { JoinNumber = 19, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Digit 8", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Digit9")]
- public JoinDataComplete Digit9 = new JoinDataComplete(new JoinData { JoinNumber = 20, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Digit 9", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Dash")]
- public JoinDataComplete Dash = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Dash", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("KeypadEnter")]
- public JoinDataComplete KeypadEnter = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Keypad Enter", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("ChannelUp")]
- public JoinDataComplete ChannelUp = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Channel Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("ChannelDown")]
- public JoinDataComplete ChannelDown = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Channel Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("LastChannel")]
- public JoinDataComplete LastChannel = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Last Channel", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Guide")]
- public JoinDataComplete Guide = new JoinDataComplete(new JoinData { JoinNumber = 26, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Guide", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Info")]
- public JoinDataComplete Info = new JoinDataComplete(new JoinData { JoinNumber = 27, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Info", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Red")]
- public JoinDataComplete Red = new JoinDataComplete(new JoinData { JoinNumber = 28, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Red", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Green")]
- public JoinDataComplete Green = new JoinDataComplete(new JoinData { JoinNumber = 29, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Green", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Yellow")]
- public JoinDataComplete Yellow = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Yellow", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Blue")]
- public JoinDataComplete Blue = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Blue", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("HasDvr")]
- public JoinDataComplete HasDvr = new JoinDataComplete(new JoinData { JoinNumber = 32, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Has DVR", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("DvrList")]
- public JoinDataComplete DvrList = new JoinDataComplete(new JoinData { JoinNumber = 32, JoinSpan = 1 },
- new JoinMetadata { Description = "STB DvrList", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Play")]
- public JoinDataComplete Play = new JoinDataComplete(new JoinData { JoinNumber = 33, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Play", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Pause")]
- public JoinDataComplete Pause = new JoinDataComplete(new JoinData { JoinNumber = 34, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Pause", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Stop")]
- public JoinDataComplete Stop = new JoinDataComplete(new JoinData { JoinNumber = 35, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Stop", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("FFwd")]
- public JoinDataComplete FFwd = new JoinDataComplete(new JoinData { JoinNumber = 36, JoinSpan = 1 },
- new JoinMetadata { Description = "STB FFwd", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Rewind")]
- public JoinDataComplete Rewind = new JoinDataComplete(new JoinData { JoinNumber = 37, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Rewind", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("ChapPlus")]
- public JoinDataComplete ChapPlus = new JoinDataComplete(new JoinData { JoinNumber = 38, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Chapter Plus", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("ChapMinus")]
- public JoinDataComplete ChapMinus = new JoinDataComplete(new JoinData { JoinNumber = 39, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Chapter Minus", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Replay")]
- public JoinDataComplete Replay = new JoinDataComplete(new JoinData { JoinNumber = 40, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Replay", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Record")]
- public JoinDataComplete Record = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Record", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("HasKeypadAccessoryButton1")]
- public JoinDataComplete HasKeypadAccessoryButton1 = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Has Keypad Accessory Button 1", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("HasKeypadAccessoryButton2")]
- public JoinDataComplete HasKeypadAccessoryButton2 = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Has Keypad Accessory Button 2", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("KeypadAccessoryButton1Press")]
- public JoinDataComplete KeypadAccessoryButton1Press = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 2 },
- new JoinMetadata { Description = "STB Keypad Accessory Button 1 Press", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("KeypadAccessoryButton2Press")]
- public JoinDataComplete KeypadAccessoryButton2Press = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 2 },
- new JoinMetadata { Description = "STB Keypad Accessory Button 2 Press", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Name")]
- public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("KeypadAccessoryButton1Label")]
- public JoinDataComplete KeypadAccessoryButton1Label = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Keypad Accessory Button 1 Label", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("KeypadAccessoryButton2Label")]
- public JoinDataComplete KeypadAccessoryButton2Label = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Keypad Accessory Button 1 Label", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("LoadPresets")]
- public JoinDataComplete LoadPresets = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Load Presets", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("HasPresets")]
- public JoinDataComplete HasPresets = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1 },
- new JoinMetadata { Description = "STB Load Presets", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a SetTopBoxControllerJoinMap
///
- /// Join this join map will start at
- public SetTopBoxControllerJoinMap(uint joinStart)
- : this(joinStart, typeof(SetTopBoxControllerJoinMap))
+ public class SetTopBoxControllerJoinMap : JoinMapBaseAdvanced
{
- }
+ ///
+ /// STB Power On
+ ///
+ [JoinName("PowerOn")]
+ public JoinDataComplete PowerOn = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Power On", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected SetTopBoxControllerJoinMap(uint joinStart, Type type)
- : base(joinStart, type)
- {
+ ///
+ /// STB Power Off
+ ///
+ [JoinName("PowerOff")]
+ public JoinDataComplete PowerOff = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Power Off", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Power Toggle
+ ///
+ [JoinName("PowerToggle")]
+ public JoinDataComplete PowerToggle = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Power Toggle", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Has DPad
+ ///
+ [JoinName("HasDpad")]
+ public JoinDataComplete HasDpad = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Has DPad", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Nav Up
+ ///
+ [JoinName("Up")]
+ public JoinDataComplete Up = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Nav Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Nav Down
+ ///
+ [JoinName("Down")]
+ public JoinDataComplete Down = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Nav Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Nav Left
+ ///
+ [JoinName("Left")]
+ public JoinDataComplete Left = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Nav Left", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Nav Right
+ ///
+ [JoinName("Right")]
+ public JoinDataComplete Right = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Nav Right", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Select
+ ///
+ [JoinName("Select")]
+ public JoinDataComplete Select = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Select", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Menu
+ ///
+ [JoinName("Menu")]
+ public JoinDataComplete Menu = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Menu", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Exit
+ ///
+ [JoinName("Exit")]
+ public JoinDataComplete Exit = new JoinDataComplete(new JoinData { JoinNumber = 10, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Exit", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Has Numeric
+ ///
+ [JoinName("HasNumeric")]
+ public JoinDataComplete HasNumeric = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Has Numeric", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Digit 0
+ ///
+ [JoinName("Digit0")]
+ public JoinDataComplete Digit0 = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Digit 0", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Digit 1
+ ///
+ [JoinName("Digit1")]
+ public JoinDataComplete Digit1 = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Digit 1", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Digit 2
+ ///
+ [JoinName("Digit2")]
+ public JoinDataComplete Digit2 = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Digit 2", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Digit 3
+ ///
+ [JoinName("Digit3")]
+ public JoinDataComplete Digit3 = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Digit 3", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Digit 4
+ ///
+ [JoinName("Digit4")]
+ public JoinDataComplete Digit4 = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Digit 4", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Digit 5
+ ///
+ [JoinName("Digit5")]
+ public JoinDataComplete Digit5 = new JoinDataComplete(new JoinData { JoinNumber = 16, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Digit 5", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Digit 6
+ ///
+ [JoinName("Digit6")]
+ public JoinDataComplete Digit6 = new JoinDataComplete(new JoinData { JoinNumber = 17, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Digit 6", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Digit 7
+ ///
+ [JoinName("Digit7")]
+ public JoinDataComplete Digit7 = new JoinDataComplete(new JoinData { JoinNumber = 18, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Digit 7", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Digit 8
+ ///
+ [JoinName("Digit8")]
+ public JoinDataComplete Digit8 = new JoinDataComplete(new JoinData { JoinNumber = 19, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Digit 8", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Digit 9
+ ///
+ [JoinName("Digit9")]
+ public JoinDataComplete Digit9 = new JoinDataComplete(new JoinData { JoinNumber = 20, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Digit 9", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Dash
+ ///
+ [JoinName("Dash")]
+ public JoinDataComplete Dash = new JoinDataComplete(new JoinData { JoinNumber = 21, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Dash", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Keypad Enter
+ ///
+ [JoinName("KeypadEnter")]
+ public JoinDataComplete KeypadEnter = new JoinDataComplete(new JoinData { JoinNumber = 22, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Keypad Enter", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Channel Up
+ ///
+ [JoinName("ChannelUp")]
+ public JoinDataComplete ChannelUp = new JoinDataComplete(new JoinData { JoinNumber = 23, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Channel Up", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Channel Down
+ ///
+ [JoinName("ChannelDown")]
+ public JoinDataComplete ChannelDown = new JoinDataComplete(new JoinData { JoinNumber = 24, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Channel Down", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Last Channel
+ ///
+ [JoinName("LastChannel")]
+ public JoinDataComplete LastChannel = new JoinDataComplete(new JoinData { JoinNumber = 25, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Last Channel", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Guide
+ ///
+ [JoinName("Guide")]
+ public JoinDataComplete Guide = new JoinDataComplete(new JoinData { JoinNumber = 26, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Guide", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Info
+ ///
+ [JoinName("Info")]
+ public JoinDataComplete Info = new JoinDataComplete(new JoinData { JoinNumber = 27, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Info", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Red
+ ///
+ [JoinName("Red")]
+ public JoinDataComplete Red = new JoinDataComplete(new JoinData { JoinNumber = 28, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Red", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Green
+ ///
+ [JoinName("Green")]
+ public JoinDataComplete Green = new JoinDataComplete(new JoinData { JoinNumber = 29, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Green", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Yellow
+ ///
+ [JoinName("Yellow")]
+ public JoinDataComplete Yellow = new JoinDataComplete(new JoinData { JoinNumber = 30, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Yellow", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Blue
+ ///
+ [JoinName("Blue")]
+ public JoinDataComplete Blue = new JoinDataComplete(new JoinData { JoinNumber = 31, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Blue", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Has DVR
+ ///
+ [JoinName("HasDvr")]
+ public JoinDataComplete HasDvr = new JoinDataComplete(new JoinData { JoinNumber = 32, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Has DVR", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Dvr List
+ ///
+ [JoinName("DvrList")]
+ public JoinDataComplete DvrList = new JoinDataComplete(new JoinData { JoinNumber = 32, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB DvrList", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Play
+ ///
+ [JoinName("Play")]
+ public JoinDataComplete Play = new JoinDataComplete(new JoinData { JoinNumber = 33, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Play", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Pause
+ ///
+ [JoinName("Pause")]
+ public JoinDataComplete Pause = new JoinDataComplete(new JoinData { JoinNumber = 34, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Pause", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Stop
+ ///
+ [JoinName("Stop")]
+ public JoinDataComplete Stop = new JoinDataComplete(new JoinData { JoinNumber = 35, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Stop", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB FFwd
+ ///
+ [JoinName("FFwd")]
+ public JoinDataComplete FFwd = new JoinDataComplete(new JoinData { JoinNumber = 36, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB FFwd", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Rewind
+ ///
+ [JoinName("Rewind")]
+ public JoinDataComplete Rewind = new JoinDataComplete(new JoinData { JoinNumber = 37, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Rewind", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Chapter Plus
+ ///
+ [JoinName("ChapPlus")]
+ public JoinDataComplete ChapPlus = new JoinDataComplete(new JoinData { JoinNumber = 38, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Chapter Plus", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Chapter Minus
+ ///
+ [JoinName("ChapMinus")]
+ public JoinDataComplete ChapMinus = new JoinDataComplete(new JoinData { JoinNumber = 39, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Chapter Minus", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Replay
+ ///
+ [JoinName("Replay")]
+ public JoinDataComplete Replay = new JoinDataComplete(new JoinData { JoinNumber = 40, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Replay", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Record
+ ///
+ [JoinName("Record")]
+ public JoinDataComplete Record = new JoinDataComplete(new JoinData { JoinNumber = 41, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Record", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Has Keypad Accessory Button 1
+ ///
+ [JoinName("HasKeypadAccessoryButton1")]
+ public JoinDataComplete HasKeypadAccessoryButton1 = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Has Keypad Accessory Button 1", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Has Keypad Accessory Button 2
+ ///
+ [JoinName("HasKeypadAccessoryButton2")]
+ public JoinDataComplete HasKeypadAccessoryButton2 = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Has Keypad Accessory Button 2", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Keypad Accessory Button 1 Press
+ ///
+ [JoinName("KeypadAccessoryButton1Press")]
+ public JoinDataComplete KeypadAccessoryButton1Press = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 2 },
+ new JoinMetadata { Description = "STB Keypad Accessory Button 1 Press", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Keypad Accessory Button 2 Press
+ ///
+ [JoinName("KeypadAccessoryButton2Press")]
+ public JoinDataComplete KeypadAccessoryButton2Press = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 2 },
+ new JoinMetadata { Description = "STB Keypad Accessory Button 2 Press", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Name
+ ///
+ [JoinName("Name")]
+ public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Keypad Accessory Button 1 Label
+ ///
+ [JoinName("KeypadAccessoryButton1Label")]
+ public JoinDataComplete KeypadAccessoryButton1Label = new JoinDataComplete(new JoinData { JoinNumber = 42, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Keypad Accessory Button 1 Label", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// STB Keypad Accessory Button 2 Label
+ ///
+ [JoinName("KeypadAccessoryButton2Label")]
+ public JoinDataComplete KeypadAccessoryButton2Label = new JoinDataComplete(new JoinData { JoinNumber = 43, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Keypad Accessory Button 1 Label", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+
+ ///
+ /// STB Load Presets
+ ///
+ [JoinName("LoadPresets")]
+ public JoinDataComplete LoadPresets = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Load Presets", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// STB Has Presets
+ ///
+ [JoinName("HasPresets")]
+ public JoinDataComplete HasPresets = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1 },
+ new JoinMetadata { Description = "STB Load Presets", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
+
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public SetTopBoxControllerJoinMap(uint joinStart)
+ : this(joinStart, typeof(SetTopBoxControllerJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected SetTopBoxControllerJoinMap(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/StatusSignControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/StatusSignControllerJoinMap.cs
index 98cd9218..79ada2c8 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/StatusSignControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/StatusSignControllerJoinMap.cs
@@ -1,58 +1,86 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class StatusSignControllerJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("IsOnline")]
- public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Status Sign Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("Name")]
- public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Status Sign Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
-
- [JoinName("RedControl")]
- public JoinDataComplete RedControl = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Status Red LED Enable / Disable", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("RedLed")]
- public JoinDataComplete RedLed = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Status Red LED Intensity", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("GreenControl")]
- public JoinDataComplete GreenControl = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Status Green LED Enable / Disable", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("GreenLed")]
- public JoinDataComplete GreenLed = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Status Green LED Intensity", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
- [JoinName("BlueControl")]
- public JoinDataComplete BlueControl = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "Status Blue LED Enable / Disable", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
-
- [JoinName("BlueLed")]
- public JoinDataComplete BlueLed = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "Status Blue LED Intensity", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
-
///
- /// Constructor to use when instantiating this Join Map without inheriting from it
+ /// Represents a StatusSignControllerJoinMap
///
- /// Join this join map will start at
- public StatusSignControllerJoinMap(uint joinStart)
- : this(joinStart, typeof(StatusSignControllerJoinMap))
+ public class StatusSignControllerJoinMap : JoinMapBaseAdvanced
{
- }
+ ///
+ /// Status Sign Online
+ ///
+ [JoinName("IsOnline")]
+ public JoinDataComplete IsOnline = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Status Sign Online", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected StatusSignControllerJoinMap(uint joinStart, Type type)
- : base(joinStart, type)
- {
- }
+ ///
+ /// Status Sign Name
+ ///
+ [JoinName("Name")]
+ public JoinDataComplete Name = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Status Sign Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Red LED Control
+ ///
+ [JoinName("RedControl")]
+ public JoinDataComplete RedControl = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Status Red LED Enable / Disable", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Red LED Intensity
+ ///
+ [JoinName("RedLed")]
+ public JoinDataComplete RedLed = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Status Red LED Intensity", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Green LED Control
+ ///
+ [JoinName("GreenControl")]
+ public JoinDataComplete GreenControl = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Status Green LED Enable / Disable", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Green LED Intensity
+ ///
+ [JoinName("GreenLed")]
+ public JoinDataComplete GreenLed = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Status Green LED Intensity", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Blue LED Control
+ ///
+ [JoinName("BlueControl")]
+ public JoinDataComplete BlueControl = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "Status Blue LED Enable / Disable", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+
+ ///
+ /// Blue LED Intensity
+ ///
+ [JoinName("BlueLed")]
+ public JoinDataComplete BlueLed = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "Status Blue LED Intensity", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog });
+
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public StatusSignControllerJoinMap(uint joinStart)
+ : this(joinStart, typeof(StatusSignControllerJoinMap))
+ {
+ }
+
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected StatusSignControllerJoinMap(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+ }
+
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/SystemMonitorJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/SystemMonitorJoinMap.cs
index 3d9ac81b..c53c9fe6 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/SystemMonitorJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/SystemMonitorJoinMap.cs
@@ -1,136 +1,235 @@
using System;
-namespace PepperDash.Essentials.Core.Bridges;
-
-public class SystemMonitorJoinMap : JoinMapBaseAdvanced
+namespace PepperDash.Essentials.Core.Bridges
{
- [JoinName("TimeZone")]
- public JoinDataComplete TimeZone = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Timezone", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
+ ///
+ /// Represents a SystemMonitorJoinMap
+ ///
+ public class SystemMonitorJoinMap : JoinMapBaseAdvanced
+ {
+ ///
+ /// Processor Timezone
+ ///
+ [JoinName("TimeZone")]
+ public JoinDataComplete TimeZone = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Timezone", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog });
- [JoinName("TimeZoneName")]
- public JoinDataComplete TimeZoneName = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Timezone Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Timezone Name
+ ///
+ [JoinName("TimeZoneName")]
+ public JoinDataComplete TimeZoneName = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Timezone Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("IOControllerVersion")]
- public JoinDataComplete IOControllerVersion = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor IO Controller Version", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor OS Version
+ ///
+ [JoinName("IOControllerVersion")]
+ public JoinDataComplete IOControllerVersion = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor IO Controller Version", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("SnmpAppVersion")]
- public JoinDataComplete SnmpAppVersion = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor SNMP App Version", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor SNMP App Version
+ ///
+ [JoinName("SnmpAppVersion")]
+ public JoinDataComplete SnmpAppVersion = new JoinDataComplete(new JoinData { JoinNumber = 3, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor SNMP App Version", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("BACnetAppVersion")]
- public JoinDataComplete BACnetAppVersion = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor BACNet App Version", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor BACNet App Version
+ ///
+ [JoinName("BACnetAppVersion")]
+ public JoinDataComplete BACnetAppVersion = new JoinDataComplete(new JoinData { JoinNumber = 4, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor BACNet App Version", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("ControllerVersion")]
- public JoinDataComplete ControllerVersion = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Controller Version", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Controller Version
+ ///
+ [JoinName("ControllerVersion")]
+ public JoinDataComplete ControllerVersion = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Controller Version", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("SerialNumber")]
- public JoinDataComplete SerialNumber = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Serial Number", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Serial Number
+ ///
+ [JoinName("SerialNumber")]
+ public JoinDataComplete SerialNumber = new JoinDataComplete(new JoinData { JoinNumber = 6, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Serial Number", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("Model")]
- public JoinDataComplete Model = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Model", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Model
+ ///
+ [JoinName("Model")]
+ public JoinDataComplete Model = new JoinDataComplete(new JoinData { JoinNumber = 7, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Model", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("Uptime")]
- public JoinDataComplete Uptime = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Uptime", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Uptime
+ ///
+ [JoinName("Uptime")]
+ public JoinDataComplete Uptime = new JoinDataComplete(new JoinData { JoinNumber = 8, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Uptime", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("LastBoot")]
- public JoinDataComplete LastBoot = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Last Boot", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Last Boot Time
+ ///
+ [JoinName("LastBoot")]
+ public JoinDataComplete LastBoot = new JoinDataComplete(new JoinData { JoinNumber = 9, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Last Boot", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("ProgramOffsetJoin")]
- public JoinDataComplete ProgramOffsetJoin = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 5 },
- new JoinMetadata { Description = "All Program Data is offset between slots by 5 - First Joins Start at 11", JoinCapabilities = eJoinCapabilities.None, JoinType = eJoinType.None });
+ ///
+ /// Program Offset Join
+ ///
+ [JoinName("ProgramOffsetJoin")]
+ public JoinDataComplete ProgramOffsetJoin = new JoinDataComplete(new JoinData { JoinNumber = 5, JoinSpan = 5 },
+ new JoinMetadata { Description = "All Program Data is offset between slots by 5 - First Joins Start at 11", JoinCapabilities = eJoinCapabilities.None, JoinType = eJoinType.None });
- [JoinName("ProgramStart")]
- public JoinDataComplete ProgramStart = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Program Start / Fb", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+ ///
+ /// Processor Program Start
+ ///
+ [JoinName("ProgramStart")]
+ public JoinDataComplete ProgramStart = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Program Start / Fb", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
- [JoinName("ProgramStop")]
- public JoinDataComplete ProgramStop = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Program Stop / Fb", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+ ///
+ /// Processor Program Stop
+ ///
+ [JoinName("ProgramStop")]
+ public JoinDataComplete ProgramStop = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Program Stop / Fb", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
- [JoinName("ProgramRegister")]
- public JoinDataComplete ProgramRegister = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Program Register / Fb", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+ ///
+ /// Processor Program Register
+ ///
+ [JoinName("ProgramRegister")]
+ public JoinDataComplete ProgramRegister = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Program Register / Fb", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
- [JoinName("ProgramUnregister")]
- public JoinDataComplete ProgramUnregister = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Program UnRegister / Fb", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
+ ///
+ /// Processor Program Unregister
+ ///
+ [JoinName("ProgramUnregister")]
+ public JoinDataComplete ProgramUnregister = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Program UnRegister / Fb", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Digital });
- [JoinName("ProgramName")]
- public JoinDataComplete ProgramName = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Program Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Program Name
+ ///
+ [JoinName("ProgramName")]
+ public JoinDataComplete ProgramName = new JoinDataComplete(new JoinData { JoinNumber = 11, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Program Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("ProgramCompiledTime")]
- public JoinDataComplete ProgramCompiledTime = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Program Compile Time", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Program Version
+ ///
+ [JoinName("ProgramCompiledTime")]
+ public JoinDataComplete ProgramCompiledTime = new JoinDataComplete(new JoinData { JoinNumber = 12, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Program Compile Time", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("ProgramCrestronDatabaseVersion")]
- public JoinDataComplete ProgramCrestronDatabaseVersion = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Program Database Version", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Program Crestron Database Version
+ ///
+ [JoinName("ProgramCrestronDatabaseVersion")]
+ public JoinDataComplete ProgramCrestronDatabaseVersion = new JoinDataComplete(new JoinData { JoinNumber = 13, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Program Database Version", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("ProgramEnvironmentVersion")]
- public JoinDataComplete ProgramEnvironmentVersion = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Program Environment Version", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Program Environment Version
+ ///
+ [JoinName("ProgramEnvironmentVersion")]
+ public JoinDataComplete ProgramEnvironmentVersion = new JoinDataComplete(new JoinData { JoinNumber = 14, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Program Environment Version", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("AggregatedProgramInfo")]
- public JoinDataComplete AggregatedProgramInfo = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Program Aggregate Info Json", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Program Aggregate Info
+ ///
+ [JoinName("AggregatedProgramInfo")]
+ public JoinDataComplete AggregatedProgramInfo = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Program Aggregate Info Json", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("EthernetOffsetJoin")]
- public JoinDataComplete EthernetOffsetJoin = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
- new JoinMetadata { Description = "All Ethernet Data is offset between Nics by 5 - First Joins Start at 76", JoinCapabilities = eJoinCapabilities.None, JoinType = eJoinType.None });
+ ///
+ /// Ethernet Offset Join
+ ///
+ [JoinName("EthernetOffsetJoin")]
+ public JoinDataComplete EthernetOffsetJoin = new JoinDataComplete(new JoinData { JoinNumber = 15, JoinSpan = 1 },
+ new JoinMetadata { Description = "All Ethernet Data is offset between Nics by 5 - First Joins Start at 76", JoinCapabilities = eJoinCapabilities.None, JoinType = eJoinType.None });
- [JoinName("HostName")]
- public JoinDataComplete HostName = new JoinDataComplete(new JoinData { JoinNumber = 76, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Ethernet Hostname", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Ethernet Hostname
+ ///
+ [JoinName("HostName")]
+ public JoinDataComplete HostName = new JoinDataComplete(new JoinData { JoinNumber = 76, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Ethernet Hostname", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("CurrentIpAddress")]
- public JoinDataComplete CurrentIpAddress = new JoinDataComplete(new JoinData { JoinNumber = 77, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Ethernet Current Ip Address", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Ethernet Current Ip Address
+ ///
+ [JoinName("CurrentIpAddress")]
+ public JoinDataComplete CurrentIpAddress = new JoinDataComplete(new JoinData { JoinNumber = 77, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Ethernet Current Ip Address", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("CurrentSubnetMask")]
- public JoinDataComplete CurrentSubnetMask = new JoinDataComplete(new JoinData { JoinNumber = 78, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Ethernet Current Subnet Mask", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Ethernet Current Subnet Mask
+ ///
+ [JoinName("CurrentSubnetMask")]
+ public JoinDataComplete CurrentSubnetMask = new JoinDataComplete(new JoinData { JoinNumber = 78, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Ethernet Current Subnet Mask", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("CurrentDefaultGateway")]
- public JoinDataComplete CurrentDefaultGateway = new JoinDataComplete(new JoinData { JoinNumber = 79, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Ethernet Current Default Gateway", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Ethernet Current Default Gateway
+ ///
+ [JoinName("CurrentDefaultGateway")]
+ public JoinDataComplete CurrentDefaultGateway = new JoinDataComplete(new JoinData { JoinNumber = 79, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Ethernet Current Default Gateway", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("StaticIpAddress")]
- public JoinDataComplete StaticIpAddress = new JoinDataComplete(new JoinData { JoinNumber = 80, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Ethernet Static Ip Address", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Ethernet Static Ip Address
+ ///
+ [JoinName("StaticIpAddress")]
+ public JoinDataComplete StaticIpAddress = new JoinDataComplete(new JoinData { JoinNumber = 80, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Ethernet Static Ip Address", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("StaticSubnetMask")]
- public JoinDataComplete StaticSubnetMask = new JoinDataComplete(new JoinData { JoinNumber = 81, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Ethernet Static Subnet Mask", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Ethernet Static Subnet Mask
+ ///
+ [JoinName("StaticSubnetMask")]
+ public JoinDataComplete StaticSubnetMask = new JoinDataComplete(new JoinData { JoinNumber = 81, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Ethernet Static Subnet Mask", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("StaticDefaultGateway")]
- public JoinDataComplete StaticDefaultGateway = new JoinDataComplete(new JoinData { JoinNumber = 82, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Ethernet Static Default Gateway", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Ethernet Static Default Gateway
+ ///
+ [JoinName("StaticDefaultGateway")]
+ public JoinDataComplete StaticDefaultGateway = new JoinDataComplete(new JoinData { JoinNumber = 82, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Ethernet Static Default Gateway", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("Domain")]
- public JoinDataComplete Domain = new JoinDataComplete(new JoinData { JoinNumber = 83, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Ethernet Domain", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Ethernet Domain
+ ///
+ [JoinName("Domain")]
+ public JoinDataComplete Domain = new JoinDataComplete(new JoinData { JoinNumber = 83, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Ethernet Domain", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("DnsServer")]
- public JoinDataComplete DnsServer = new JoinDataComplete(new JoinData { JoinNumber = 84, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Ethernet Dns Server", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Ethernet Dns Server
+ ///
+ [JoinName("DnsServer")]
+ public JoinDataComplete DnsServer = new JoinDataComplete(new JoinData { JoinNumber = 84, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Ethernet Dns Server", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("MacAddress")]
- public JoinDataComplete MacAddress = new JoinDataComplete(new JoinData { JoinNumber = 85, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Ethernet Mac Address", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Ethernet Mac Address
+ ///
+ [JoinName("MacAddress")]
+ public JoinDataComplete MacAddress = new JoinDataComplete(new JoinData { JoinNumber = 85, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Ethernet Mac Address", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
- [JoinName("DhcpStatus")]
- public JoinDataComplete DhcpStatus = new JoinDataComplete(new JoinData { JoinNumber = 86, JoinSpan = 1 },
- new JoinMetadata { Description = "Processor Ethernet Dhcp Status", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
+ ///
+ /// Processor Ethernet Dhcp Status
+ ///
+ [JoinName("DhcpStatus")]
+ public JoinDataComplete DhcpStatus = new JoinDataComplete(new JoinData { JoinNumber = 86, JoinSpan = 1 },
+ new JoinMetadata { Description = "Processor Ethernet Dhcp Status", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial });
///
/// Processor Reboot
@@ -161,22 +260,23 @@ public class SystemMonitorJoinMap : JoinMapBaseAdvanced
new JoinMetadata { Description = "Resets the program", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital });
- ///
- /// Constructor to use when instantiating this Join Map without inheriting from it
- ///
- /// Join this join map will start at
- public SystemMonitorJoinMap(uint joinStart)
- : this(joinStart, typeof(SystemMonitorJoinMap))
- {
- }
+ ///
+ /// Constructor to use when instantiating this Join Map without inheriting from it
+ ///
+ /// Join this join map will start at
+ public SystemMonitorJoinMap(uint joinStart)
+ : this(joinStart, typeof(SystemMonitorJoinMap))
+ {
+ }
- ///
- /// Constructor to use when extending this Join map
- ///
- /// Join this join map will start at
- /// Type of the child join map
- protected SystemMonitorJoinMap(uint joinStart, Type type)
- : base(joinStart, type)
- {
+ ///
+ /// Constructor to use when extending this Join map
+ ///
+ /// Join this join map will start at
+ /// Type of the child join map
+ protected SystemMonitorJoinMap(uint joinStart, Type type)
+ : base(joinStart, type)
+ {
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs
index b691bbdb..2a7a46c2 100644
--- a/src/PepperDash.Essentials.Core/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs
+++ b/src/PepperDash.Essentials.Core/Bridges/JoinMaps/VideoCodecControllerJoinMap.cs
@@ -1,7 +1,10 @@
using System;
using PepperDash.Essentials.Core;
-namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
-
+namespace PepperDash.Essentials.Core.Bridges.JoinMaps
+{
+ ///
+ /// Represents a VideoCodecControllerJoinMap
+ ///
public class VideoCodecControllerJoinMap : JoinMapBaseAdvanced
{
#region Digital
@@ -23,21 +26,27 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("SendDtmfToSpecificCallIndex")]
- public JoinDataComplete SendDtmfToSpecificCallIndex = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 10,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "If High, will send DTMF tones to the call set by SelectCall analog. If low sends DTMF tones to last connected call.",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
+ ///
+ /// If High, will send DTMF tones to the call set by SelectCall analog. If low sends DTMF tones to last connected call.
+ ///
+ [JoinName("SendDtmfToSpecificCallIndex")]
+ public JoinDataComplete SendDtmfToSpecificCallIndex = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 10,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "If High, will send DTMF tones to the call set by SelectCall analog. If low sends DTMF tones to last connected call.",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
- [JoinName("Dtmf1")]
+ ///
+ /// DTMF 1
+ ///
+ [JoinName("Dtmf1")]
public JoinDataComplete Dtmf1 = new JoinDataComplete(
new JoinData
{
@@ -51,7 +60,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("Dtmf2")]
+ ///
+ /// DTMF 2
+ ///
+ [JoinName("Dtmf2")]
public JoinDataComplete Dtmf2 = new JoinDataComplete(
new JoinData
{
@@ -65,7 +77,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("Dtmf3")]
+ ///
+ /// DTMF 3
+ ///
+ [JoinName("Dtmf3")]
public JoinDataComplete Dtmf3 = new JoinDataComplete(
new JoinData
{
@@ -79,7 +94,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("Dtmf4")]
+ ///
+ /// DTMF 4
+ ///
+ [JoinName("Dtmf4")]
public JoinDataComplete Dtmf4 = new JoinDataComplete(
new JoinData
{
@@ -93,7 +111,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("Dtmf5")]
+ ///
+ /// DTMF 5
+ ///
+ [JoinName("Dtmf5")]
public JoinDataComplete Dtmf5 = new JoinDataComplete(
new JoinData
{
@@ -107,7 +128,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("Dtmf6")]
+ ///
+ /// DTMF 6
+ ///
+ [JoinName("Dtmf6")]
public JoinDataComplete Dtmf6 = new JoinDataComplete(
new JoinData
{
@@ -121,7 +145,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("Dtmf7")]
+ ///
+ /// DTMF 7
+ ///
+ [JoinName("Dtmf7")]
public JoinDataComplete Dtmf7 = new JoinDataComplete(
new JoinData
{
@@ -135,7 +162,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("Dtmf8")]
+ ///
+ /// DTMF 8
+ ///
+ [JoinName("Dtmf8")]
public JoinDataComplete Dtmf8 = new JoinDataComplete(
new JoinData
{
@@ -149,7 +179,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("Dtmf9")]
+ ///
+ /// DTMF 9
+ ///
+ [JoinName("Dtmf9")]
public JoinDataComplete Dtmf9 = new JoinDataComplete(
new JoinData
{
@@ -163,7 +196,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("Dtmf0")]
+ ///
+ /// DTMF 0
+ ///
+ [JoinName("Dtmf0")]
public JoinDataComplete Dtmf0 = new JoinDataComplete(
new JoinData
{
@@ -177,7 +213,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("DtmfStar")]
+ ///
+ /// DTMF *
+ ///
+ [JoinName("DtmfStar")]
public JoinDataComplete DtmfStar = new JoinDataComplete(
new JoinData
{
@@ -191,7 +230,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("DtmfPound")]
+ ///
+ /// DTMF #
+ ///
+ [JoinName("DtmfPound")]
public JoinDataComplete DtmfPound = new JoinDataComplete(
new JoinData
{
@@ -205,7 +247,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("EndAllCalls")]
+ ///
+ /// End All Calls
+ ///
+ [JoinName("EndAllCalls")]
public JoinDataComplete EndAllCalls = new JoinDataComplete(
new JoinData
{
@@ -321,7 +366,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("DialPhone")]
+ ///
+ /// Dial Phone
+ ///
+ [JoinName("DialPhone")]
public JoinDataComplete DialPhone = new JoinDataComplete(
new JoinData
{
@@ -352,7 +400,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("HangUpPhone")]
+ ///
+ /// Hang Up Phone
+ ///
+ [JoinName("HangUpPhone")]
public JoinDataComplete HangUpPhone = new JoinDataComplete(
new JoinData
{
@@ -366,47 +417,56 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("EndCallStart")]
- public JoinDataComplete EndCallStart = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 81,
- JoinSpan = 8
- },
- new JoinMetadata
- {
- Description = "End a specific call by call index. ",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
+ ///
+ /// End Call
+ ///
+ [JoinName("EndCallStart")]
+ public JoinDataComplete EndCallStart = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 81,
+ JoinSpan = 8
+ },
+ new JoinMetadata
+ {
+ Description = "End a specific call by call index. ",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
- [JoinName("JoinAllCalls")]
- public JoinDataComplete JoinAllCalls = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 90,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Join all calls",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
+ ///
+ /// Join All Calls
+ ///
+ [JoinName("JoinAllCalls")]
+ public JoinDataComplete JoinAllCalls = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 90,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Join all calls",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
- [JoinName("JoinCallStart")]
- public JoinDataComplete JoinCallStart = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 91,
- JoinSpan = 8
- },
- new JoinMetadata
- {
- Description = "Join a specific call by call index. ",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
+ ///
+ /// Join Call
+ ///
+ [JoinName("JoinCallStart")]
+ public JoinDataComplete JoinCallStart = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 91,
+ JoinSpan = 8
+ },
+ new JoinMetadata
+ {
+ Description = "Join a specific call by call index. ",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
///
/// Directory Search Busy
@@ -544,47 +604,56 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("DirectoryDisableAutoDialSelectedLine")]
- public JoinDataComplete DirectoryDisableAutoDialSelectedLine = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 107,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Set high to disable automatic dialing of a contact when selected",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
+ ///
+ /// Directory Disable Auto Dial Selected Line
+ ///
+ [JoinName("DirectoryDisableAutoDialSelectedLine")]
+ public JoinDataComplete DirectoryDisableAutoDialSelectedLine = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 107,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Set high to disable automatic dialing of a contact when selected",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
- [JoinName("DirectoryDialSelectedContactMethod")]
- public JoinDataComplete DirectoryDialSelectedContactMethod = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 108,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Pulse to dial the selected contact method",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
+ ///
+ /// Directory Dial Selected Contact Method
+ ///
+ [JoinName("DirectoryDialSelectedContactMethod")]
+ public JoinDataComplete DirectoryDialSelectedContactMethod = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 108,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Pulse to dial the selected contact method",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
- [JoinName("DirectoryClearSelected")]
- public JoinDataComplete DirectoryClearSelected = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 110,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Clear Selected Entry and String from Search",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
+ ///
+ /// Directory Clear Selected
+ ///
+ [JoinName("DirectoryClearSelected")]
+ public JoinDataComplete DirectoryClearSelected = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 110,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Clear Selected Entry and String from Search",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
///
/// Camera Tilt Up
@@ -688,47 +757,56 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("CameraFocusNear")]
- public JoinDataComplete CameraFocusNear = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 117,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Camera Focus Near",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
+ ///
+ /// Camera Focus Near
+ ///
+ [JoinName("CameraFocusNear")]
+ public JoinDataComplete CameraFocusNear = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 117,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Camera Focus Near",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
- [JoinName("CameraFocusFar")]
- public JoinDataComplete CameraFocusFar = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 118,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Camera Focus Far",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
+ ///
+ /// Camera Focus Far
+ ///
+ [JoinName("CameraFocusFar")]
+ public JoinDataComplete CameraFocusFar = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 118,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Camera Focus Far",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
- [JoinName("CameraFocusAuto")]
- public JoinDataComplete CameraFocusAuto = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 119,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Camera Auto Focus Trigger",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
+ ///
+ /// Camera Auto Focus
+ ///
+ [JoinName("CameraFocusAuto")]
+ public JoinDataComplete CameraFocusAuto = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 119,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Camera Auto Focus Trigger",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
///
/// Camera Preset Save
@@ -883,7 +961,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("DialMeetingStart")]
+ ///
+ /// Dial Meeting Start
+ ///
+ [JoinName("DialMeetingStart")]
public JoinDataComplete DialMeetingStart = new JoinDataComplete(
new JoinData
{
@@ -1033,33 +1114,39 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("RemoveSelectedRecentCallItem")]
- public JoinDataComplete RemoveSelectedRecentCallItem = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 181,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Pulse to remove the selected recent call item specified by the SelectRecentCallItem analog join",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
+ ///
+ /// Remove Selected Recent Call Item
+ ///
+ [JoinName("RemoveSelectedRecentCallItem")]
+ public JoinDataComplete RemoveSelectedRecentCallItem = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 181,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Pulse to remove the selected recent call item specified by the SelectRecentCallItem analog join",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
- [JoinName("DialSelectedRecentCallItem")]
- public JoinDataComplete DialSelectedRecentCallItem = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 182,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Pulse to dial the selected recent call item specified by the SelectRecentCallItem analog join",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
+ ///
+ /// Dial Selected Recent Call Item
+ ///
+ [JoinName("DialSelectedRecentCallItem")]
+ public JoinDataComplete DialSelectedRecentCallItem = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 182,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Pulse to dial the selected recent call item specified by the SelectRecentCallItem analog join",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
///
/// Source Share Start
@@ -1146,117 +1233,141 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Digital
});
- [JoinName("HoldAllCalls")]
- public JoinDataComplete HoldAllCalls = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 220,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Holds all calls",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
+ ///
+ /// Hold All Calls
+ ///
+ [JoinName("HoldAllCalls")]
+ public JoinDataComplete HoldAllCalls = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 220,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Holds all calls",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
- [JoinName("HoldCallsStart")]
- public JoinDataComplete HoldCallsStart = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 221,
- JoinSpan = 8
- },
- new JoinMetadata
- {
- Description = "Holds Call at specified index. FB reported on Call Status XSIG",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
+ ///
+ /// Hold Call at Index
+ ///
+ [JoinName("HoldCallsStart")]
+ public JoinDataComplete HoldCallsStart = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 221,
+ JoinSpan = 8
+ },
+ new JoinMetadata
+ {
+ Description = "Holds Call at specified index. FB reported on Call Status XSIG",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
- [JoinName("ResumeCallsStart")]
- public JoinDataComplete ResumeCallsStart = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 231,
- JoinSpan = 8
- },
- new JoinMetadata
- {
- Description = "Resume Call at specified index",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Digital
- });
+ ///
+ /// Resume All Calls
+ ///
+ [JoinName("ResumeCallsStart")]
+ public JoinDataComplete ResumeCallsStart = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 231,
+ JoinSpan = 8
+ },
+ new JoinMetadata
+ {
+ Description = "Resume Call at specified index",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Digital
+ });
- [JoinName("MultiSiteOptionIsEnabled")]
- public JoinDataComplete MultiSiteOptionIsEnabled = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 301,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Multi site option is enabled FB",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Digital
- });
+ ///
+ /// Multi Site Option Is Enabled
+ ///
+ [JoinName("MultiSiteOptionIsEnabled")]
+ public JoinDataComplete MultiSiteOptionIsEnabled = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 301,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Multi site option is enabled FB",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Digital
+ });
- [JoinName("AutoAnswerEnabled")]
- public JoinDataComplete AutoAnswerEnabled = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 302,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Auto Answer is enabled FB",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Digital
- });
+ ///
+ /// Auto Answer Enabled
+ ///
+ [JoinName("AutoAnswerEnabled")]
+ public JoinDataComplete AutoAnswerEnabled = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 302,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Auto Answer is enabled FB",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Digital
+ });
- [JoinName("ParticipantAudioMuteToggleStart")]
- public JoinDataComplete ParticipantAudioMuteToggleStart = new JoinDataComplete(
+ ///
+ /// Participant Audio Mute Toggle
+ ///
+ [JoinName("ParticipantAudioMuteToggleStart")]
+ public JoinDataComplete ParticipantAudioMuteToggleStart = new JoinDataComplete(
new JoinData
{
JoinNumber = 501,
JoinSpan = 50
},
- new JoinMetadata
- {
- Description = "Toggles the participant's audio mute status",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Digital
- });
+ new JoinMetadata
+ {
+ Description = "Toggles the participant's audio mute status",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Digital
+ });
- [JoinName("ParticipantVideoMuteToggleStart")]
- public JoinDataComplete ParticipantVideoMuteToggleStart = new JoinDataComplete(
+ ///
+ /// Participant Video Mute Toggle
+ ///
+ [JoinName("ParticipantVideoMuteToggleStart")]
+ public JoinDataComplete ParticipantVideoMuteToggleStart = new JoinDataComplete(
new JoinData
{
JoinNumber = 801,
JoinSpan = 50
},
- new JoinMetadata
- {
- Description = "Toggles the participant's video mute status",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Digital
- });
+ new JoinMetadata
+ {
+ Description = "Toggles the participant's video mute status",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Digital
+ });
- [JoinName("ParticipantPinToggleStart")]
- public JoinDataComplete ParticipantPinToggleStart = new JoinDataComplete(
+ ///
+ /// Participant Pin Toggle
+ ///
+ [JoinName("ParticipantPinToggleStart")]
+ public JoinDataComplete ParticipantPinToggleStart = new JoinDataComplete(
new JoinData
{
JoinNumber = 1101,
JoinSpan = 50
},
- new JoinMetadata
- {
- Description = "Toggles the participant's pin status",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Digital
- });
+ new JoinMetadata
+ {
+ Description = "Toggles the participant's pin status",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Digital
+ });
#endregion
@@ -1265,49 +1376,58 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
#region Analog
- // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set
- [JoinName("MeetingsToDisplay")]
- public JoinDataComplete MeetingsToDisplay = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 40,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Set/FB the number of meetings to display via the bridge xsig; default: 3 meetings.",
- JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
- JoinType = eJoinType.Analog
- });
+ // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set
- [JoinName("SelectCall")]
- public JoinDataComplete SelectCall = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 24,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Sets the selected Call for DTMF commands. Valid values 1-8",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Analog
- });
+ ///
+ /// Meetings To Display
+ ///
+ [JoinName("MeetingsToDisplay")]
+ public JoinDataComplete MeetingsToDisplay = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 40,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Set/FB the number of meetings to display via the bridge xsig; default: 3 meetings.",
+ JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinType = eJoinType.Analog
+ });
+ ///
+ /// Select Call
+ ///
+ [JoinName("SelectCall")]
+ public JoinDataComplete SelectCall = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 24,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Sets the selected Call for DTMF commands. Valid values 1-8",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Analog
+ });
- [JoinName("ConnectedCallCount")]
- public JoinDataComplete ConnectedCallCount = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 25,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Reports the number of currently connected calls",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Analog
- });
+ ///
+ /// Connected Call Count
+ ///
+ [JoinName("ConnectedCallCount")]
+ public JoinDataComplete ConnectedCallCount = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 25,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Reports the number of currently connected calls",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Analog
+ });
///
/// Minutes Before Meeting Start
@@ -1343,19 +1463,22 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Analog
});
- [JoinName("CameraCount")]
- public JoinDataComplete CameraCount = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 61,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Reports the number of cameras",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Analog
- });
+ ///
+ /// Camera Count
+ ///
+ [JoinName("CameraCount")]
+ public JoinDataComplete CameraCount = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 61,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Reports the number of cameras",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Analog
+ });
///
/// Directory Row Count
@@ -1391,48 +1514,56 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Analog
});
+ ///
+ /// Selected Contact Method Count
+ ///
+ [JoinName("SelectedContactMethodCount")]
+ public JoinDataComplete SelectedContactMethodCount = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 102,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Reports the number of contact methods for the selected contact",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Analog
+ });
- [JoinName("SelectedContactMethodCount")]
- public JoinDataComplete SelectedContactMethodCount = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 102,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Reports the number of contact methods for the selected contact",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Analog
- });
+ ///
+ /// Select Contact Method
+ ///
+ [JoinName("SelectContactMethod")]
+ public JoinDataComplete SelectContactMethod = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 103,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Selects a contact method by index",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Analog
+ });
- [JoinName("SelectContactMethod")]
- public JoinDataComplete SelectContactMethod = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 103,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Selects a contact method by index",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Analog
- });
-
- [JoinName("DirectorySelectRowFeedback")]
- public JoinDataComplete DirectorySelectRowFeedback = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 104,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Directory Select Row and Feedback",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Analog
- });
+ ///
+ /// Directory Select Row Feedback
+ ///
+ [JoinName("DirectorySelectRowFeedback")]
+ public JoinDataComplete DirectorySelectRowFeedback = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 104,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Directory Select Row and Feedback",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Analog
+ });
///
@@ -1452,19 +1583,22 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Analog
});
- [JoinName("FarEndPresetSelect")]
- public JoinDataComplete FarEndPresetSelect = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 122,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Far End Preset Preset Select",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Analog
- });
+ ///
+ /// Far End Preset Select
+ ///
+ [JoinName("FarEndPresetSelect")]
+ public JoinDataComplete FarEndPresetSelect = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 122,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Far End Preset Preset Select",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Analog
+ });
///
/// Participant Count
@@ -1517,47 +1651,56 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Analog
});
- [JoinName("SelectRecentCallItem")]
- public JoinDataComplete SelectRecentCallItem = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 180,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Select/FB for Recent Call Item. Valid values 1 - 10",
- JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
- JoinType = eJoinType.Analog
- });
+ ///
+ /// Select Recent Call Item
+ ///
+ [JoinName("SelectRecentCallItem")]
+ public JoinDataComplete SelectRecentCallItem = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 180,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Select/FB for Recent Call Item. Valid values 1 - 10",
+ JoinCapabilities = eJoinCapabilities.ToFromSIMPL,
+ JoinType = eJoinType.Analog
+ });
- [JoinName("RecentCallOccurrenceType")]
- public JoinDataComplete RecentCallOccurrenceType = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 181,
- JoinSpan = 10
- },
- new JoinMetadata
- {
- Description = "Recent Call Occurrence Type. [0-3] 0 = Unknown, 1 = Placed, 2 = Received, 3 = NoAnswer",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Analog
- });
+ ///
+ /// Recent Call Occurrence Type
+ ///
+ [JoinName("RecentCallOccurrenceType")]
+ public JoinDataComplete RecentCallOccurrenceType = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 181,
+ JoinSpan = 10
+ },
+ new JoinMetadata
+ {
+ Description = "Recent Call Occurrence Type. [0-3] 0 = Unknown, 1 = Placed, 2 = Received, 3 = NoAnswer",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Analog
+ });
- [JoinName("RecentCallCount")]
- public JoinDataComplete RecentCallCount = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 191,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Recent Call Count",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Analog
- });
+ ///
+ /// Recent Call Count
+ ///
+ [JoinName("RecentCallCount")]
+ public JoinDataComplete RecentCallCount = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 191,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Recent Call Count",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Analog
+ });
#endregion
@@ -1582,7 +1725,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Serial
});
- [JoinName("PhoneDialString")]
+ ///
+ /// Phone Dial String
+ ///
+ [JoinName("PhoneDialString")]
public JoinDataComplete PhoneDialString = new JoinDataComplete(
new JoinData
{
@@ -1596,7 +1742,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Serial
});
- [JoinName("CurrentCallData")]
+ ///
+ /// Current Call Data
+ ///
+ [JoinName("CurrentCallData")]
public JoinDataComplete CurrentCallData = new JoinDataComplete(
new JoinData
{
@@ -1712,19 +1861,22 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Serial
});
- [JoinName("ContactMethods")]
- public JoinDataComplete ContactMethods = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 103,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Contact Methods - XSig, 10 entries",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
+ ///
+ /// Contact Methods
+ ///
+ [JoinName("ContactMethods")]
+ public JoinDataComplete ContactMethods = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 103,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Contact Methods - XSig, 10 entries",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
///
/// Camera Preset Names
@@ -1743,7 +1895,10 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Serial
});
- [JoinName("CurrentLayoutStringFb")]
+ ///
+ /// Current Layout String
+ ///
+ [JoinName("CurrentLayoutStringFb")]
public JoinDataComplete CurrentLayoutStringFb = new JoinDataComplete(
new JoinData
{
@@ -1757,33 +1912,39 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Serial
});
- [JoinName("AvailableLayoutsFb")]
- public JoinDataComplete AvailableLayoutsFb = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 142,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "xSig of all available layouts",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
+ ///
+ /// Available Layouts XSig
+ ///
+ [JoinName("AvailableLayoutsFb")]
+ public JoinDataComplete AvailableLayoutsFb = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 142,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "xSig of all available layouts",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
- [JoinName("SelectLayout")]
- public JoinDataComplete SelectLayout = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 142,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Select Layout by string",
- JoinCapabilities = eJoinCapabilities.FromSIMPL,
- JoinType = eJoinType.Serial
- });
+ ///
+ /// Select Layout
+ ///
+ [JoinName("SelectLayout")]
+ public JoinDataComplete SelectLayout = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 142,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Select Layout by string",
+ JoinCapabilities = eJoinCapabilities.FromSIMPL,
+ JoinType = eJoinType.Serial
+ });
///
@@ -1803,75 +1964,90 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Serial
});
- [JoinName("CameraNamesFb")]
- public JoinDataComplete CameraNamesFb = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 161,
- JoinSpan = 10
- },
- new JoinMetadata
- {
- Description = "Camera Name Fb",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
+ ///
+ /// Camera Names XSig
+ ///
+ [JoinName("CameraNamesFb")]
+ public JoinDataComplete CameraNamesFb = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 161,
+ JoinSpan = 10
+ },
+ new JoinMetadata
+ {
+ Description = "Camera Name Fb",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
- [JoinName("SelectedRecentCallName")]
- public JoinDataComplete SelectedRecentCallName = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 171,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Selected Recent Call Name",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
+ ///
+ /// Selected Recent Call Name
+ ///
+ [JoinName("SelectedRecentCallName")]
+ public JoinDataComplete SelectedRecentCallName = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 171,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Selected Recent Call Name",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
- [JoinName("SelectedRecentCallNumber")]
- public JoinDataComplete SelectedRecentCallNumber = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 172,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "Selected Recent Call Number",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
+ ///
+ /// Selected Recent Call Number
+ ///
+ [JoinName("SelectedRecentCallNumber")]
+ public JoinDataComplete SelectedRecentCallNumber = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 172,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "Selected Recent Call Number",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
- [JoinName("RecentCallNamesStart")]
- public JoinDataComplete RecentCallNamesStart = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 181,
- JoinSpan = 10
- },
- new JoinMetadata
- {
- Description = "Recent Call Names",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
+ ///
+ /// Recent Call Names
+ ///
+ [JoinName("RecentCallNamesStart")]
+ public JoinDataComplete RecentCallNamesStart = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 181,
+ JoinSpan = 10
+ },
+ new JoinMetadata
+ {
+ Description = "Recent Call Names",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
- [JoinName("RecentCallTimesStart")]
- public JoinDataComplete RecentCallTimesStart = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 191,
- JoinSpan = 10
- },
- new JoinMetadata
- {
- Description = "Recent Calls Times",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
+ ///
+ /// Recent Call Numbers
+ ///
+ [JoinName("RecentCallTimesStart")]
+ public JoinDataComplete RecentCallTimesStart = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 191,
+ JoinSpan = 10
+ },
+ new JoinMetadata
+ {
+ Description = "Recent Calls Times",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
///
/// Current Source
@@ -1907,75 +2083,90 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
JoinType = eJoinType.Serial
});
- [JoinName("DeviceIpAddresss")]
- public JoinDataComplete DeviceIpAddresss = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 301,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "IP Address of device",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
+ ///
+ /// Device IP Address
+ ///
+ [JoinName("DeviceIpAddresss")]
+ public JoinDataComplete DeviceIpAddresss = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 301,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "IP Address of device",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
- [JoinName("SipPhoneNumber")]
- public JoinDataComplete SipPhoneNumber = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 302,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "SIP phone number of device",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
+ ///
+ /// SIP Phone Number
+ ///
+ [JoinName("SipPhoneNumber")]
+ public JoinDataComplete SipPhoneNumber = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 302,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "SIP phone number of device",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
- [JoinName("E164Alias")]
- public JoinDataComplete E164Alias = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 303,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "E164 alias of device",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
+ ///
+ /// E164 Alias
+ ///
+ [JoinName("E164Alias")]
+ public JoinDataComplete E164Alias = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 303,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "E164 alias of device",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
- [JoinName("H323Id")]
- public JoinDataComplete H323Id = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 304,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "H323 ID of device",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
+ ///
+ /// H323 ID
+ ///
+ [JoinName("H323Id")]
+ public JoinDataComplete H323Id = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 304,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "H323 ID of device",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
- [JoinName("SipUri")]
- public JoinDataComplete SipUri = new JoinDataComplete(
- new JoinData
- {
- JoinNumber = 305,
- JoinSpan = 1
- },
- new JoinMetadata
- {
- Description = "SIP URI of device",
- JoinCapabilities = eJoinCapabilities.ToSIMPL,
- JoinType = eJoinType.Serial
- });
+ ///
+ /// SIP URI
+ ///
+ [JoinName("SipUri")]
+ public JoinDataComplete SipUri = new JoinDataComplete(
+ new JoinData
+ {
+ JoinNumber = 305,
+ JoinSpan = 1
+ },
+ new JoinMetadata
+ {
+ Description = "SIP URI of device",
+ JoinCapabilities = eJoinCapabilities.ToSIMPL,
+ JoinType = eJoinType.Serial
+ });
///
/// Selected Directory Entry Name
@@ -2050,3 +2241,4 @@ namespace PepperDash.Essentials.Core.Bridges.JoinMaps;
{
}
}
+}
diff --git a/src/PepperDash.Essentials.Core/Comm and IR/CecPortController.cs b/src/PepperDash.Essentials.Core/Comm and IR/CecPortController.cs
index 0c053d6b..a5aa0d13 100644
--- a/src/PepperDash.Essentials.Core/Comm and IR/CecPortController.cs
+++ b/src/PepperDash.Essentials.Core/Comm and IR/CecPortController.cs
@@ -10,116 +10,182 @@ using Crestron.SimplSharpPro.DM;
using PepperDash.Core;
using Serilog.Events;
-namespace PepperDash.Essentials.Core;
-
- public class CecPortController : Device, IBasicCommunicationWithStreamDebugging
+namespace PepperDash.Essentials.Core
{
- public CommunicationStreamDebugging StreamDebugging { get; private set; }
-
- public event EventHandler BytesReceived;
- public event EventHandler TextReceived;
-
- public bool IsConnected { get { return true; } }
-
- ICec Port;
-
- public CecPortController(string key, Func postActivationFunc,
- EssentialsControlPropertiesConfig config):base(key)
- {
- StreamDebugging = new CommunicationStreamDebugging(key);
-
- AddPostActivationAction(() =>
- {
- Port = postActivationFunc(config);
-
- Port.StreamCec.CecChange += StreamCec_CecChange;
- });
- }
-
- public CecPortController(string key, ICec port)
- : base(key)
- {
- Port = port;
-
- Port.StreamCec.CecChange += new CecChangeEventHandler(StreamCec_CecChange);
- }
-
- void StreamCec_CecChange(Cec cecDevice, CecEventArgs args)
- {
- if (args.EventId == CecEventIds.CecMessageReceivedEventId)
- OnDataReceived(cecDevice.Received.StringValue);
- else if (args.EventId == CecEventIds.ErrorFeedbackEventId)
- if(cecDevice.ErrorFeedback.BoolValue)
- Debug.LogMessage(LogEventLevel.Verbose, this, "CEC NAK Error");
- }
-
- void OnDataReceived(string s)
- {
- var bytesHandler = BytesReceived;
- if (bytesHandler != null)
- {
- var bytes = Encoding.GetEncoding(28591).GetBytes(s);
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", ComTextHelper.GetEscapedText(bytes));
- bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
- }
- var textHandler = TextReceived;
- if (textHandler != null)
- {
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", s);
- textHandler(this, new GenericCommMethodReceiveTextArgs(s));
- }
- }
-
- #region IBasicCommunication Members
-
- public void SendText(string text)
- {
- if (Port == null)
- return;
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} characters of text: '{1}'", text.Length, text);
- Port.StreamCec.Send.StringValue = text;
- }
-
- public void SendBytes(byte[] bytes)
- {
- if (Port == null)
- return;
- var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
- Port.StreamCec.Send.StringValue = text;
- }
-
- public void Connect()
- {
- }
-
- public void Disconnect()
- {
- }
-
- #endregion
-
///
- ///
+ /// Represents a CecPortController
///
- ///
- public void SimulateReceive(string s)
+ public class CecPortController : Device, IBasicCommunicationWithStreamDebugging
{
- // split out hex chars and build string
- var split = Regex.Split(s, @"(\\[Xx][0-9a-fA-F][0-9a-fA-F])");
- StringBuilder b = new StringBuilder();
- foreach (var t in split)
+ ///
+ /// Gets or sets the StreamDebugging
+ ///
+ public CommunicationStreamDebugging StreamDebugging { get; private set; }
+
+ ///
+ /// Event raised when bytes are received
+ ///
+ public event EventHandler BytesReceived;
+
+ ///
+ /// Event raised when text is received
+ ///
+ public event EventHandler TextReceived;
+
+ ///
+ /// Gets or sets the IsConnected
+ ///
+ public bool IsConnected { get { return true; } }
+
+ ICec Port;
+
+ bool _cecSubscribed;
+
+ ///
+ /// Constructor
+ ///
+ /// key of the device
+ /// post activation function for the device
+ /// configuration for the device
+ public CecPortController(string key, Func postActivationFunc,
+ EssentialsControlPropertiesConfig config) : base(key)
{
- if (t.StartsWith(@"\") && t.Length == 4)
- b.Append((char)(Convert.ToByte(t.Substring(2, 2), 16)));
- else
- b.Append(t);
+ StreamDebugging = new CommunicationStreamDebugging(key);
+
+ AddPostActivationAction(() =>
+ {
+ Port = postActivationFunc(config);
+
+ TryEnsureCecSubscription();
+ });
}
- OnDataReceived(b.ToString());
+ ///
+ /// Constructor
+ ///
+ /// key of the device
+ /// CEC port
+ public CecPortController(string key, ICec port)
+ : base(key)
+ {
+ Port = port;
+
+ TryEnsureCecSubscription();
+ }
+
+ ///
+ /// Subscribes to the CEC change event once is available.
+ /// Safe to call repeatedly; the subscription is only wired a single time.
+ /// If StreamCec is null during construction, this is retried when send methods invoke
+ /// this method later.
+ ///
+ void TryEnsureCecSubscription()
+ {
+ if (_cecSubscribed)
+ return;
+
+ if (Port?.StreamCec == null)
+ {
+ Debug.LogMessage(LogEventLevel.Warning, this, "StreamCec is not available; CEC feedback is deferred until the device is ready");
+ return;
+ }
+
+ Port.StreamCec.CecChange += new CecChangeEventHandler(StreamCec_CecChange);
+ _cecSubscribed = true;
+ Debug.LogMessage(LogEventLevel.Information, this, "Subscribed to CEC feedback");
+ }
+
+ void StreamCec_CecChange(Cec cecDevice, CecEventArgs args)
+ {
+ if (args.EventId == CecEventIds.CecMessageReceivedEventId)
+ OnDataReceived(cecDevice.Received.StringValue);
+ else if (args.EventId == CecEventIds.ErrorFeedbackEventId)
+ if (cecDevice.ErrorFeedback.BoolValue)
+ Debug.LogMessage(LogEventLevel.Verbose, this, "CEC NAK Error");
+ }
+
+ void OnDataReceived(string s)
+ {
+ var bytesHandler = BytesReceived;
+ if (bytesHandler != null)
+ {
+ var bytes = Encoding.GetEncoding(28591).GetBytes(s);
+ this.PrintReceivedBytes(bytes);
+ bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
+ }
+ var textHandler = TextReceived;
+ if (textHandler != null)
+ {
+ this.PrintReceivedText(s);
+ textHandler(this, new GenericCommMethodReceiveTextArgs(s));
+ }
+ }
+
+ #region IBasicCommunication Members
+
+ ///
+ /// SendText method
+ ///
+ public void SendText(string text)
+ {
+ if (Port?.StreamCec == null)
+ return;
+ TryEnsureCecSubscription();
+ this.PrintSentText(text);
+ Port.StreamCec.Send.StringValue = text;
+ }
+
+ ///
+ /// SendBytes method
+ ///
+ public void SendBytes(byte[] bytes)
+ {
+ if (Port?.StreamCec == null)
+ return;
+ TryEnsureCecSubscription();
+ var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
+ this.PrintSentBytes(bytes);
+ Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
+ Port.StreamCec.Send.StringValue = text;
+ }
+
+ ///
+ /// Connect method
+ ///
+ public void Connect()
+ {
+ }
+
+ ///
+ /// Disconnect method
+ ///
+ public void Disconnect()
+ {
+ }
+
+ #endregion
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// SimulateReceive method
+ ///
+ public void SimulateReceive(string s)
+ {
+ // split out hex chars and build string
+ var split = Regex.Split(s, @"(\\[Xx][0-9a-fA-F][0-9a-fA-F])");
+ StringBuilder b = new StringBuilder();
+ foreach (var t in split)
+ {
+ if (t.StartsWith(@"\") && t.Length == 4)
+ b.Append((char)(Convert.ToByte(t.Substring(2, 2), 16)));
+ else
+ b.Append(t);
+ }
+
+ OnDataReceived(b.ToString());
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Comm and IR/ComPortController.cs b/src/PepperDash.Essentials.Core/Comm and IR/ComPortController.cs
index a4407e01..ec679fae 100644
--- a/src/PepperDash.Essentials.Core/Comm and IR/ComPortController.cs
+++ b/src/PepperDash.Essentials.Core/Comm and IR/ComPortController.cs
@@ -9,11 +9,17 @@ using PepperDash.Core.Logging;
using Serilog.Events;
-namespace PepperDash.Essentials.Core;
-
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Represents a ComPortController
+ ///
public class ComPortController : Device, IBasicCommunicationWithStreamDebugging
{
- public CommunicationStreamDebugging StreamDebugging { get; private set; }
+ ///
+ /// Gets or sets the StreamDebugging
+ ///
+ public CommunicationStreamDebugging StreamDebugging { get; private set; }
///
/// Event fired when bytes are received
@@ -33,20 +39,27 @@ namespace PepperDash.Essentials.Core;
ComPort Port;
ComPort.ComPortSpec Spec;
- public ComPortController(string key, Func postActivationFunc,
- ComPort.ComPortSpec spec, EssentialsControlPropertiesConfig config) : base(key)
- {
- StreamDebugging = new CommunicationStreamDebugging(key);
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ ///
+ ///
+ public ComPortController(string key, Func postActivationFunc,
+ ComPort.ComPortSpec spec, EssentialsControlPropertiesConfig config) : base(key)
+ {
+ StreamDebugging = new CommunicationStreamDebugging(key);
Spec = spec;
- AddPostActivationAction(() =>
- {
- Port = postActivationFunc(config);
+ AddPostActivationAction(() =>
+ {
+ Port = postActivationFunc(config);
- RegisterAndConfigureComPort();
- });
- }
+ RegisterAndConfigureComPort();
+ });
+ }
///
/// Constructor
@@ -70,22 +83,13 @@ namespace PepperDash.Essentials.Core;
RegisterAndConfigureComPort();
}
- private void RegisterAndConfigureComPort()
- {
- if (Port == null)
- {
- Debug.LogMessage(LogEventLevel.Information, this, "Configured com Port for this device does not exist.");
- return;
- }
- if (Port.Parent is CrestronControlSystem)
- {
- var result = Port.Register();
- if (result != eDeviceRegistrationUnRegistrationResponse.Success)
- {
- Debug.LogMessage(LogEventLevel.Information, this, "ERROR: Cannot register Com port: {0}", result);
- return; // false
- }
- }
+ private void RegisterAndConfigureComPort()
+ {
+ if (Port == null)
+ {
+ this.LogInformation($"Configured {Port.Parent.GetType().Name}-comport-{Port.ID} for {Key} does not exist.");
+ return;
+ }
if (Port.Parent is CrestronControlSystem || Port.Parent is CenIoCom102)
@@ -120,33 +124,31 @@ namespace PepperDash.Essentials.Core;
void Port_SerialDataReceived(ComPort ReceivingComPort, ComPortSerialDataEventArgs args)
{
- OnDataReceived(args.SerialData);
+ OnDataReceived(args.SerialData);
}
- void OnDataReceived(string s)
- {
+ void OnDataReceived(string s)
+ {
var eventSubscribed = false;
- var bytesHandler = BytesReceived;
- if (bytesHandler != null)
- {
- var bytes = Encoding.GetEncoding(28591).GetBytes(s);
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", ComTextHelper.GetEscapedText(bytes));
- bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
+ var bytesHandler = BytesReceived;
+ if (bytesHandler != null)
+ {
+ var bytes = Encoding.GetEncoding(28591).GetBytes(s);
+ this.PrintReceivedBytes(bytes);
+ bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
eventSubscribed = true;
- }
- var textHandler = TextReceived;
- if (textHandler != null)
- {
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", s);
- textHandler(this, new GenericCommMethodReceiveTextArgs(s));
+ }
+ var textHandler = TextReceived;
+ if (textHandler != null)
+ {
+ this.PrintReceivedText(s);
+ textHandler(this, new GenericCommMethodReceiveTextArgs(s));
eventSubscribed = true;
- }
+ }
- if(!eventSubscribed) Debug.LogMessage(LogEventLevel.Warning, this, "Received data but no handler is registered");
- }
+ if (!eventSubscribed) Debug.LogMessage(LogEventLevel.Warning, this, "Received data but no handler is registered");
+ }
///
/// Deactivate method
@@ -167,9 +169,8 @@ namespace PepperDash.Essentials.Core;
if (Port == null)
return;
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} characters of text: '{1}'", text.Length, text);
- Port.Send(text);
+ this.PrintSentText(text);
+ Port.Send(text);
}
///
@@ -180,8 +181,7 @@ namespace PepperDash.Essentials.Core;
if (Port == null)
return;
var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
+ this.PrintSentBytes(bytes);
Port.Send(text);
}
@@ -202,23 +202,27 @@ namespace PepperDash.Essentials.Core;
#endregion
- ///
- ///
- ///
- ///
- public void SimulateReceive(string s)
- {
- // split out hex chars and build string
- var split = Regex.Split(s, @"(\\[Xx][0-9a-fA-F][0-9a-fA-F])");
- StringBuilder b = new StringBuilder();
- foreach (var t in split)
- {
- if (t.StartsWith(@"\") && t.Length == 4)
- b.Append((char)(Convert.ToByte(t.Substring(2, 2), 16)));
- else
- b.Append(t);
- }
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// SimulateReceive method
+ ///
+ public void SimulateReceive(string s)
+ {
+ // split out hex chars and build string
+ var split = Regex.Split(s, @"(\\[Xx][0-9a-fA-F][0-9a-fA-F])");
+ StringBuilder b = new StringBuilder();
+ foreach (var t in split)
+ {
+ if (t.StartsWith(@"\") && t.Length == 4)
+ b.Append((char)(Convert.ToByte(t.Substring(2, 2), 16)));
+ else
+ b.Append(t);
+ }
- OnDataReceived(b.ToString());
- }
+ OnDataReceived(b.ToString());
+ }
}
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Comm and IR/ComSpecJsonConverter.cs b/src/PepperDash.Essentials.Core/Comm and IR/ComSpecJsonConverter.cs
index 6ef2f916..11e2bc74 100644
--- a/src/PepperDash.Essentials.Core/Comm and IR/ComSpecJsonConverter.cs
+++ b/src/PepperDash.Essentials.Core/Comm and IR/ComSpecJsonConverter.cs
@@ -13,89 +13,119 @@ using Newtonsoft.Json.Linq;
using PepperDash.Core;
-namespace PepperDash.Essentials.Core;
-
-///
-/// This converter creates a proper ComPort.ComPortSpec struct from more-friendly JSON values. It uses
-/// ComSpecPropsJsonConverter to finish the individual properties.
-///
-public class ComSpecJsonConverter : JsonConverter
+namespace PepperDash.Essentials.Core
{
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ ///
+ /// This converter creates a proper ComPort.ComPortSpec struct from more-friendly JSON values. It uses
+ /// ComSpecPropsJsonConverter to finish the individual properties.
+ ///
+ public class ComSpecJsonConverter : JsonConverter
{
- if (objectType == typeof(ComPort.ComPortSpec?))
+ ///
+ /// ReadJson method
+ ///
+ /// reader to use
+ /// type of the object being read
+ /// existing value of the object being read
+ /// serializer to use
+ /// deserialized object
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
- var newSer = new JsonSerializer();
- newSer.Converters.Add(new ComSpecPropsJsonConverter());
- newSer.ObjectCreationHandling = ObjectCreationHandling.Replace;
- return newSer.Deserialize(reader);
+ if (objectType == typeof(ComPort.ComPortSpec?))
+ {
+ var newSer = new JsonSerializer();
+ newSer.Converters.Add(new ComSpecPropsJsonConverter());
+ newSer.ObjectCreationHandling = ObjectCreationHandling.Replace;
+ return newSer.Deserialize(reader);
+ }
+ return null;
+ }
+
+ ///
+ /// CanConvert method
+ ///
+ ///
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(ComPort.ComPortSpec?);
+ }
+
+ ///
+ /// Gets or sets the CanRead
+ ///
+ public override bool CanRead { get { return true; } }
+
+ ///
+ /// Gets or sets the CanWrite
+ ///
+ ///
+ public override bool CanWrite { get { return false; } }
+
+ ///
+ /// WriteJson method
+ ///
+ ///
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ throw new NotImplementedException();
}
- return null;
}
///
- ///
+ /// Represents a ComSpecPropsJsonConverter
///
- public override bool CanConvert(Type objectType)
+ public class ComSpecPropsJsonConverter : JsonConverter
{
- return objectType == typeof(ComPort.ComPortSpec?);
+ ///
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(ComPort.eComBaudRates)
+ || objectType == typeof(ComPort.eComDataBits)
+ || objectType == typeof(ComPort.eComParityType)
+ || objectType == typeof(ComPort.eComHardwareHandshakeType)
+ || objectType == typeof(ComPort.eComSoftwareHandshakeType)
+ || objectType == typeof(ComPort.eComProtocolType)
+ || objectType == typeof(ComPort.eComStopBits);
+ }
+
+ ///
+ /// Gets or sets the CanRead
+ ///
+ ///
+ public override bool CanRead { get { return true; } }
+
+ ///
+ /// ReadJson method
+ ///
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ //Debug.LogMessage(LogEventLevel.Verbose, "ReadJson type: " + objectType.Name);
+ if (objectType == typeof(ComPort.eComBaudRates))
+ return Enum.Parse(typeof(ComPort.eComBaudRates), "ComspecBaudRate" + reader.Value, false);
+ else if (objectType == typeof(ComPort.eComDataBits))
+ return Enum.Parse(typeof(ComPort.eComDataBits), "ComspecDataBits" + reader.Value, true);
+ else if (objectType == typeof(ComPort.eComHardwareHandshakeType))
+ return Enum.Parse(typeof(ComPort.eComHardwareHandshakeType), "ComspecHardwareHandshake" + reader.Value, true);
+ else if (objectType == typeof(ComPort.eComParityType))
+ return Enum.Parse(typeof(ComPort.eComParityType), "ComspecParity" + reader.Value, true);
+ else if (objectType == typeof(ComPort.eComProtocolType))
+ return Enum.Parse(typeof(ComPort.eComProtocolType), "ComspecProtocol" + reader.Value, true);
+ else if (objectType == typeof(ComPort.eComSoftwareHandshakeType))
+ return Enum.Parse(typeof(ComPort.eComSoftwareHandshakeType), "ComspecSoftwareHandshake" + reader.Value, true);
+ else if (objectType == typeof(ComPort.eComStopBits))
+ return Enum.Parse(typeof(ComPort.eComStopBits), "ComspecStopBits" + reader.Value, true);
+ return null;
+ }
+
+ ///
+ /// WriteJson method
+ ///
+ ///
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ throw new NotImplementedException();
+ }
}
- public override bool CanRead { get { return true; } }
- ///
- /// This converter will not be used for writing
- ///
- public override bool CanWrite { get { return false; } }
-
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- throw new NotImplementedException();
- }
-}
-
-///
-/// The gist of this converter: The comspec JSON comes in with normal values that need to be converted
-/// into enum names. This converter takes the value and applies the appropriate enum's name prefix to the value
-/// and then returns the enum value using Enum.Parse. NOTE: Does not write
-///
-public class ComSpecPropsJsonConverter : JsonConverter
-{
- public override bool CanConvert(Type objectType)
- {
- return objectType == typeof(ComPort.eComBaudRates)
- || objectType == typeof(ComPort.eComDataBits)
- || objectType == typeof(ComPort.eComParityType)
- || objectType == typeof(ComPort.eComHardwareHandshakeType)
- || objectType == typeof(ComPort.eComSoftwareHandshakeType)
- || objectType == typeof(ComPort.eComProtocolType)
- || objectType == typeof(ComPort.eComStopBits);
- }
-
- public override bool CanRead { get { return true; } }
-
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- //Debug.LogMessage(LogEventLevel.Verbose, "ReadJson type: " + objectType.Name);
- if (objectType == typeof(ComPort.eComBaudRates))
- return Enum.Parse(typeof(ComPort.eComBaudRates), "ComspecBaudRate" + reader.Value, false);
- else if (objectType == typeof(ComPort.eComDataBits))
- return Enum.Parse(typeof(ComPort.eComDataBits), "ComspecDataBits" + reader.Value, true);
- else if (objectType == typeof(ComPort.eComHardwareHandshakeType))
- return Enum.Parse(typeof(ComPort.eComHardwareHandshakeType), "ComspecHardwareHandshake" + reader.Value, true);
- else if (objectType == typeof(ComPort.eComParityType))
- return Enum.Parse(typeof(ComPort.eComParityType), "ComspecParity" + reader.Value, true);
- else if (objectType == typeof(ComPort.eComProtocolType))
- return Enum.Parse(typeof(ComPort.eComProtocolType), "ComspecProtocol" + reader.Value, true);
- else if (objectType == typeof(ComPort.eComSoftwareHandshakeType))
- return Enum.Parse(typeof(ComPort.eComSoftwareHandshakeType), "ComspecSoftwareHandshake" + reader.Value, true);
- else if (objectType == typeof(ComPort.eComStopBits))
- return Enum.Parse(typeof(ComPort.eComStopBits), "ComspecStopBits" + reader.Value, true);
- return null;
- }
-
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- throw new NotImplementedException();
- }
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Comm and IR/CommFactory.cs b/src/PepperDash.Essentials.Core/Comm and IR/CommFactory.cs
index c6a10622..b28331a0 100644
--- a/src/PepperDash.Essentials.Core/Comm and IR/CommFactory.cs
+++ b/src/PepperDash.Essentials.Core/Comm and IR/CommFactory.cs
@@ -8,8 +8,8 @@ using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
using Serilog.Events;
-namespace PepperDash.Essentials.Core;
-
+namespace PepperDash.Essentials.Core
+{
///
///
///
@@ -59,35 +59,54 @@ namespace PepperDash.Essentials.Core;
case eControlMethod.Com:
comm = new ComPortController(deviceConfig.Key + "-com", GetComPort, controlConfig.ComParams.Value, controlConfig);
break;
- case eControlMethod.Cec:
- comm = new CecPortController(deviceConfig.Key + "-cec", GetCecPort, controlConfig);
- break;
+ case eControlMethod.ComBridge:
+ comm = new CommBridge(deviceConfig.Key + "-simpl", deviceConfig.Name + " Simpl");
+ break;
+ case eControlMethod.Cec:
+ comm = new CecPortController(deviceConfig.Key + "-cec", GetCecPort, controlConfig);
+ break;
case eControlMethod.IR:
break;
- case eControlMethod.Ssh:
- {
- var ssh = new GenericSshClient(deviceConfig.Key + "-ssh", c.Address, c.Port, c.Username, c.Password);
- ssh.AutoReconnect = c.AutoReconnect;
- if(ssh.AutoReconnect)
- ssh.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
- comm = ssh;
- break;
- }
- case eControlMethod.Tcpip:
- {
- var tcp = new GenericTcpIpClient(deviceConfig.Key + "-tcp", c.Address, c.Port, c.BufferSize);
- tcp.AutoReconnect = c.AutoReconnect;
- if (tcp.AutoReconnect)
- tcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
- comm = tcp;
- break;
- }
- case eControlMethod.Udp:
- {
- var udp = new GenericUdpServer(deviceConfig.Key + "-udp", c.Address, c.Port, c.BufferSize);
- comm = udp;
- break;
- }
+ case eControlMethod.Ssh:
+ {
+ var ssh = new GenericSshClient(deviceConfig.Key + "-ssh", c.Address, c.Port, c.Username, c.Password)
+ {
+ AutoReconnect = c.AutoReconnect,
+ DisableEcho = c.DisableSshEcho
+ };
+ if (ssh.AutoReconnect)
+ ssh.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
+ comm = ssh;
+ break;
+ }
+ case eControlMethod.Tcpip:
+ {
+ var tcp = new GenericTcpIpClient(deviceConfig.Key + "-tcp", c.Address, c.Port, c.BufferSize)
+ {
+ AutoReconnect = c.AutoReconnect
+ };
+ if (tcp.AutoReconnect)
+ tcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
+ comm = tcp;
+ break;
+ }
+ case eControlMethod.Udp:
+ {
+ var udp = new GenericUdpServer(deviceConfig.Key + "-udp", c.Address, c.Port, c.BufferSize);
+ comm = udp;
+ break;
+ }
+ case eControlMethod.UdpClient:
+ {
+ var udpClient = new GenericUdpClient(deviceConfig.Key + "-udpClient", c.Address, c.Port, c.BufferSize)
+ {
+ AutoReconnect = c.AutoReconnect
+ };
+ if (udpClient.AutoReconnect)
+ udpClient.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
+ comm = udpClient;
+ break;
+ }
case eControlMethod.Telnet:
break;
case eControlMethod.SecureTcpIp:
@@ -130,16 +149,19 @@ namespace PepperDash.Essentials.Core;
return null;
}
- ///
- /// Gets an ICec port from a RoutingInput or RoutingOutput on a device
- ///
- ///
- ///
- public static ICec GetCecPort(ControlPropertiesConfig config)
- {
- try
- {
- var dev = DeviceManager.GetDeviceForKey(config.ControlPortDevKey);
+ ///
+ /// Gets an ICec port from a RoutingInput or RoutingOutput on a device
+ ///
+ ///
+ ///
+ ///
+ /// GetCecPort method
+ ///
+ public static ICec GetCecPort(ControlPropertiesConfig config)
+ {
+ try
+ {
+ var dev = DeviceManager.GetDeviceForKey(config.ControlPortDevKey);
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: device '{0}' {1}", config.ControlPortDevKey, dev == null
? "is not valid, failed to get cec port"
@@ -185,7 +207,7 @@ namespace PepperDash.Essentials.Core;
config.ControlPortDevKey, config.ControlPortName);
return null;
- }
+ }
///
/// Helper to grab the IComPorts device for this PortDeviceKey. Key "controlSystem" will
@@ -211,65 +233,86 @@ namespace PepperDash.Essentials.Core;
}
}
-///
-///
-///
-public class EssentialsControlPropertiesConfig :
- ControlPropertiesConfig
-{
+ ///
+ /// Represents a EssentialsControlPropertiesConfig
+ ///
+ public class EssentialsControlPropertiesConfig :
+ ControlPropertiesConfig
+ {
+ ///
+ /// Gets or sets the ComParams
+ ///
+ [JsonProperty("comParams", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonConverter(typeof(ComSpecJsonConverter))]
+ public ComPort.ComPortSpec? ComParams { get; set; }
- [JsonProperty("comParams", NullValueHandling = NullValueHandling.Ignore)]
- [JsonConverter(typeof(ComSpecJsonConverter))]
- public ComPort.ComPortSpec? ComParams { get; set; }
+ ///
+ /// Gets or sets the CresnetId
+ ///
+ [JsonProperty("cresnetId", NullValueHandling = NullValueHandling.Ignore)]
+ public string CresnetId { get; set; }
- [JsonProperty("cresnetId", NullValueHandling = NullValueHandling.Ignore)]
- public string CresnetId { get; set; }
+ ///
+ /// Attempts to provide uint conversion of string CresnetId
+ ///
+ [JsonIgnore]
+ public uint CresnetIdInt
+ {
+ get
+ {
+ try
+ {
+ return Convert.ToUInt32(CresnetId, 16);
+ }
+ catch (Exception)
+ {
+ throw new FormatException(string.Format("ERROR:Unable to convert Cresnet ID: {0} to hex. Error:\n{1}", CresnetId));
+ }
+ }
+ }
- ///
- /// Attempts to provide uint conversion of string CresnetId
- ///
- [JsonIgnore]
- public uint CresnetIdInt
- {
- get
- {
- try
- {
- return Convert.ToUInt32(CresnetId, 16);
- }
- catch (Exception)
- {
- throw new FormatException(string.Format("ERROR:Unable to convert Cresnet ID: {0} to hex. Error:\n{1}", CresnetId));
- }
- }
- }
+ ///
+ /// Gets or sets the InfinetId
+ ///
+ [JsonProperty("infinetId", NullValueHandling = NullValueHandling.Ignore)]
+ public string InfinetId { get; set; }
- [JsonProperty("infinetId", NullValueHandling = NullValueHandling.Ignore)]
- public string InfinetId { get; set; }
+ ///
+ /// Attepmts to provide uiont conversion of string InifinetId
+ ///
+ [JsonIgnore]
+ public uint InfinetIdInt
+ {
+ get
+ {
+ try
+ {
+ return Convert.ToUInt32(InfinetId, 16);
+ }
+ catch (Exception)
+ {
+ throw new FormatException(string.Format("ERROR:Unable to conver Infinet ID: {0} to hex. Error:\n{1}", InfinetId));
+ }
+ }
+ }
+ }
- ///
- /// Attepmts to provide uiont conversion of string InifinetId
- ///
- [JsonIgnore]
- public uint InfinetIdInt
- {
- get
- {
- try
- {
- return Convert.ToUInt32(InfinetId, 16);
- }
- catch (Exception)
- {
- throw new FormatException(string.Format("ERROR:Unable to conver Infinet ID: {0} to hex. Error:\n{1}", InfinetId));
- }
- }
- }
-}
-
-public class IrControlSpec
-{
- public string PortDeviceKey { get; set; }
- public uint PortNumber { get; set; }
- public string File { get; set; }
+ ///
+ /// Represents a IrControlSpec
+ ///
+ public class IrControlSpec
+ {
+ ///
+ /// Gets or sets the PortDeviceKey
+ ///
+ public string PortDeviceKey { get; set; }
+ ///
+ /// Gets or sets the PortNumber
+ ///
+ public uint PortNumber { get; set; }
+ ///
+ /// Gets or sets the File
+ ///
+ public string File { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Comm and IR/CommunicationExtras.cs b/src/PepperDash.Essentials.Core/Comm and IR/CommunicationExtras.cs
index cc188fa7..d9bd0974 100644
--- a/src/PepperDash.Essentials.Core/Comm and IR/CommunicationExtras.cs
+++ b/src/PepperDash.Essentials.Core/Comm and IR/CommunicationExtras.cs
@@ -13,8 +13,8 @@ using Newtonsoft.Json.Linq;
using PepperDash.Core;
-namespace PepperDash.Essentials.Core;
-
+namespace PepperDash.Essentials.Core
+{
///
///
///
@@ -24,4 +24,5 @@ namespace PepperDash.Essentials.Core;
/// Gets the Device
///
IComPorts Device { get; }
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Comm and IR/ConsoleCommMockDevice.cs b/src/PepperDash.Essentials.Core/Comm and IR/ConsoleCommMockDevice.cs
index 91a2d76b..7c2af905 100644
--- a/src/PepperDash.Essentials.Core/Comm and IR/ConsoleCommMockDevice.cs
+++ b/src/PepperDash.Essentials.Core/Comm and IR/ConsoleCommMockDevice.cs
@@ -9,8 +9,11 @@ using PepperDash.Essentials.Core.Config;
using Serilog.Events;
-namespace PepperDash.Essentials.Core;
-
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Represents a ConsoleCommMockDevice
+ ///
public class ConsoleCommMockDevice : EssentialsDevice, ICommunicationMonitor
{
///
@@ -57,7 +60,7 @@ namespace PepperDash.Essentials.Core;
/// CustomActivate method
///
///
- protected override bool CustomActivate()
+ public override bool CustomActivate()
{
Communication.Connect();
CommunicationMonitor.StatusChange += (o, a) => { Debug.LogMessage(LogEventLevel.Verbose, this, "Communication monitor state: {0}", CommunicationMonitor.Status); };
@@ -100,19 +103,31 @@ namespace PepperDash.Essentials.Core;
}
}
-public class ConsoleCommMockDeviceFactory : EssentialsDeviceFactory
-{
- public ConsoleCommMockDeviceFactory()
+ ///
+ /// Represents a ConsoleCommMockDeviceFactory
+ ///
+ public class ConsoleCommMockDeviceFactory : EssentialsDeviceFactory
{
- TypeNames = new List() { "commmock" };
+ ///
+ /// Initializes a new instance of the ConsoleCommMockDeviceFactory class.
+ ///
+ public ConsoleCommMockDeviceFactory()
+ {
+ TypeNames = new List() { "commmock" };
+ }
+
+ ///
+ /// BuildDevice method
+ ///
+ ///
+ public override EssentialsDevice BuildDevice(DeviceConfig dc)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Comm Mock Device");
+ var comm = CommFactory.CreateCommForDevice(dc);
+ var props = Newtonsoft.Json.JsonConvert.DeserializeObject(
+ dc.Properties.ToString());
+ return new ConsoleCommMockDevice(dc.Key, dc.Name, props, comm);
+ }
}
- public override EssentialsDevice BuildDevice(DeviceConfig dc)
- {
- Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Comm Mock Device");
- var comm = CommFactory.CreateCommForDevice(dc);
- var props = Newtonsoft.Json.JsonConvert.DeserializeObject(
- dc.Properties.ToString());
- return new ConsoleCommMockDevice(dc.Key, dc.Name, props, comm);
- }
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Comm and IR/GenericComm.cs b/src/PepperDash.Essentials.Core/Comm and IR/GenericComm.cs
index 6e913968..069244c3 100644
--- a/src/PepperDash.Essentials.Core/Comm and IR/GenericComm.cs
+++ b/src/PepperDash.Essentials.Core/Comm and IR/GenericComm.cs
@@ -13,132 +13,164 @@ using PepperDash.Essentials.Core.Config;
using Serilog.Events;
-namespace PepperDash.Essentials.Core;
-
-///
-/// Serves as a generic wrapper class for all styles of IBasicCommuncation ports
-///
-[Description("Generic communication wrapper class for any IBasicCommunication type")]
-public class GenericComm : ReconfigurableBridgableDevice
+namespace PepperDash.Essentials.Core
{
- EssentialsControlPropertiesConfig PropertiesConfig;
-
- public IBasicCommunication CommPort { get; private set; }
-
- public GenericComm(DeviceConfig config)
- : base(config)
+ ///
+ /// Serves as a generic wrapper class for all styles of IBasicCommuncation ports
+ ///
+ [Description("Generic communication wrapper class for any IBasicCommunication type")]
+ public class GenericComm : ReconfigurableBridgableDevice
{
+ EssentialsControlPropertiesConfig PropertiesConfig;
- PropertiesConfig = CommFactory.GetControlPropertiesConfig(config);
+ ///
+ /// Gets the CommPort
+ ///
+ public IBasicCommunication CommPort { get; private set; }
- var commPort = CommFactory.CreateCommForDevice(config);
-
- //Fixing decision to require '-comPorts' in delcaration for DGE in order to get a device with comports included
- if (commPort == null)
+ ///
+ /// Constructor
+ ///
+ /// the config of the device
+ public GenericComm(DeviceConfig config)
+ : base(config)
{
- config.Key = config.Key + "-comPorts";
- commPort = CommFactory.CreateCommForDevice(config);
- }
- CommPort = commPort;
+ PropertiesConfig = CommFactory.GetControlPropertiesConfig(config);
- }
+ var commPort = CommFactory.CreateCommForDevice(config);
- public static IKeyed BuildDevice(DeviceConfig dc)
- {
- Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Comm Device");
- return new GenericComm(dc);
- }
-
- public void SetPortConfig(string portConfig)
- {
- // TODO: Deserialize new EssentialsControlPropertiesConfig and handle as necessary
- try
- {
- PropertiesConfig = JsonConvert.DeserializeObject
- (portConfig);
- }
- catch (Exception e)
- {
- Debug.LogMessage(LogEventLevel.Verbose, this, "Error deserializing port config: {0}", e);
- }
- }
-
- protected override void CustomSetConfig(DeviceConfig config)
- {
- PropertiesConfig = CommFactory.GetControlPropertiesConfig(config);
-
- ConfigWriter.UpdateDeviceConfig(config);
- }
-
- public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
- {
- var joinMap = new IBasicCommunicationJoinMap(joinStart);
-
- var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);
-
- if (!string.IsNullOrEmpty(joinMapSerialized))
- joinMap = JsonConvert.DeserializeObject(joinMapSerialized);
-
- if (bridge != null)
- {
- bridge.AddJoinMap(Key, joinMap);
- }
- else
- {
- Debug.LogMessage(LogEventLevel.Information, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
- }
-
- if (CommPort == null)
- {
- Debug.LogMessage(LogEventLevel.Debug, this, "Unable to link device '{0}'. CommPort is null", Key);
- return;
- }
-
- Debug.LogMessage(LogEventLevel.Debug, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
-
- // this is a permanent event handler. This cannot be -= from event
- CommPort.TextReceived += (s, a) =>
- {
- trilist.SetString(joinMap.TextReceived.JoinNumber, a.Text);
- };
- trilist.SetStringSigAction(joinMap.SendText.JoinNumber, s => CommPort.SendText(s));
- trilist.SetStringSigAction(joinMap.SetPortConfig.JoinNumber, SetPortConfig);
-
-
- var sComm = CommPort as ISocketStatus;
- if (sComm == null) return;
- sComm.ConnectionChange += (s, a) =>
- {
- trilist.SetUshort(joinMap.Status.JoinNumber, (ushort)(a.Client.ClientStatus));
- trilist.SetBool(joinMap.Connected.JoinNumber, a.Client.ClientStatus ==
- SocketStatus.SOCKET_STATUS_CONNECTED);
- };
-
- trilist.SetBoolSigAction(joinMap.Connect.JoinNumber, b =>
- {
- if (b)
+ //Fixing decision to require '-comPorts' in declaration for DGE in order to get a device with comports included
+ if (commPort == null)
{
- sComm.Connect();
+ config.Key = config.Key + "-comPorts";
+ commPort = CommFactory.CreateCommForDevice(config);
+ }
+
+ CommPort = commPort;
+
+ }
+
+ ///
+ /// BuildDevice method
+ ///
+ public static IKeyed BuildDevice(DeviceConfig dc)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Comm Device");
+ return new GenericComm(dc);
+ }
+
+ ///
+ /// SetPortConfig method
+ ///
+ public void SetPortConfig(string portConfig)
+ {
+ // TODO: Deserialize new EssentialsControlPropertiesConfig and handle as necessary
+ try
+ {
+ PropertiesConfig = JsonConvert.DeserializeObject
+ (portConfig);
+ }
+ catch (Exception e)
+ {
+ Debug.LogMessage(LogEventLevel.Verbose, this, "Error deserializing port config: {0}", e);
+ }
+ }
+
+ ///
+ /// CustomSetConfig method
+ ///
+ /// the new device configuration
+ protected override void CustomSetConfig(DeviceConfig config)
+ {
+ PropertiesConfig = CommFactory.GetControlPropertiesConfig(config);
+
+ ConfigWriter.UpdateDeviceConfig(config);
+ }
+
+ ///
+ /// LinkToApi method
+ ///
+ ///
+ public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
+ {
+ var joinMap = new IBasicCommunicationJoinMap(joinStart);
+
+ var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);
+
+ if (!string.IsNullOrEmpty(joinMapSerialized))
+ joinMap = JsonConvert.DeserializeObject(joinMapSerialized);
+
+ if (bridge != null)
+ {
+ bridge.AddJoinMap(Key, joinMap);
}
else
{
- sComm.Disconnect();
+ Debug.LogMessage(LogEventLevel.Information, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
}
- });
- }
-}
-public class GenericCommFactory : EssentialsDeviceFactory
-{
- public GenericCommFactory()
- {
- TypeNames = new List() { "genericComm" };
+ if (CommPort == null)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, this, "Unable to link device '{0}'. CommPort is null", Key);
+ return;
+ }
+
+ Debug.LogMessage(LogEventLevel.Debug, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
+
+ // this is a permanent event handler. This cannot be -= from event
+ CommPort.TextReceived += (s, a) =>
+ {
+ trilist.SetString(joinMap.TextReceived.JoinNumber, a.Text);
+ };
+ trilist.SetStringSigAction(joinMap.SendText.JoinNumber, s => CommPort.SendText(s));
+ trilist.SetStringSigAction(joinMap.SetPortConfig.JoinNumber, SetPortConfig);
+
+
+ var sComm = CommPort as ISocketStatus;
+ if (sComm == null) return;
+ sComm.ConnectionChange += (s, a) =>
+ {
+ trilist.SetUshort(joinMap.Status.JoinNumber, (ushort)(a.Client.ClientStatus));
+ trilist.SetBool(joinMap.Connected.JoinNumber, a.Client.ClientStatus ==
+ SocketStatus.SOCKET_STATUS_CONNECTED);
+ };
+
+ trilist.SetBoolSigAction(joinMap.Connect.JoinNumber, b =>
+ {
+ if (b)
+ {
+ sComm.Connect();
+ }
+ else
+ {
+ sComm.Disconnect();
+ }
+ });
+ }
}
- public override EssentialsDevice BuildDevice(DeviceConfig dc)
+ ///
+ /// Represents a GenericCommFactory
+ ///
+ public class GenericCommFactory : EssentialsDeviceFactory
{
- Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Comm Device");
- return new GenericComm(dc);
+ ///
+ /// Initializes a new instance of the GenericCommFactory class.
+ ///
+ public GenericCommFactory()
+ {
+ TypeNames = new List() { "genericComm" };
+ }
+
+ ///
+ /// BuildDevice method
+ ///
+ ///
+ public override EssentialsDevice BuildDevice(DeviceConfig dc)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Comm Device");
+ return new GenericComm(dc);
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Comm and IR/GenericHttpClient.cs b/src/PepperDash.Essentials.Core/Comm and IR/GenericHttpClient.cs
new file mode 100644
index 00000000..e02027b3
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Comm and IR/GenericHttpClient.cs
@@ -0,0 +1,184 @@
+using Crestron.SimplSharp.Net.Http;
+using PepperDash.Core;
+using System;
+
+namespace PepperDash.Essentials.Core
+{
+
+ ///
+ /// Represents a GenericHttpClient
+ ///
+ [Obsolete("Please use the builtin HttpClient class instead: https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelines")]
+ public class GenericHttpClient : Device, IBasicCommunication
+ {
+ private readonly HttpClient Client;
+
+ ///
+ /// Event raised when response is received
+ ///
+ public event EventHandler ResponseRecived;
+
+ ///
+ /// Constructor
+ ///
+ /// key of the device
+ /// name of the device
+ /// hostname for the HTTP client
+ public GenericHttpClient(string key, string name, string hostname)
+ : base(key, name)
+ {
+ Client = new HttpClient
+ {
+ HostName = hostname
+ };
+
+
+ }
+
+ ///
+ /// SendText method
+ ///
+ /// the path to send the request to
+ public void SendText(string path)
+ {
+ HttpClientRequest request = new HttpClientRequest();
+ string url = string.Format("http://{0}/{1}", Client.HostName, path);
+ request.Url = new UrlParser(url);
+ HttpClient.DISPATCHASYNC_ERROR error = Client.DispatchAsyncEx(request, Response, request);
+ }
+
+ ///
+ /// SendText method
+ ///
+ /// format for the items
+ /// items to format
+ public void SendText(string format, params object[] items)
+ {
+ HttpClientRequest request = new HttpClientRequest();
+ string url = string.Format("http://{0}/{1}", Client.HostName, string.Format(format, items));
+ request.Url = new UrlParser(url);
+ HttpClient.DISPATCHASYNC_ERROR error = Client.DispatchAsyncEx(request, Response, request);
+ }
+
+ ///
+ /// SendTextNoResponse method
+ ///
+ /// format for the items
+ /// items to format
+ public void SendTextNoResponse(string format, params object[] items)
+ {
+ HttpClientRequest request = new HttpClientRequest();
+ string url = string.Format("http://{0}/{1}", Client.HostName, string.Format(format, items));
+ request.Url = new UrlParser(url);
+ Client.Dispatch(request);
+ }
+
+ ///
+ /// Response method
+ ///
+ /// response received from the HTTP client
+ /// error status of the HTTP callback
+ /// original HTTP client request
+ private void Response(HttpClientResponse response, HTTP_CALLBACK_ERROR error, object request)
+ {
+ if (error == HTTP_CALLBACK_ERROR.COMPLETED)
+ {
+ var responseReceived = response;
+
+ if (responseReceived.ContentString.Length > 0)
+ {
+ ResponseRecived?.Invoke(this, new GenericHttpClientEventArgs(responseReceived.ContentString, (request as HttpClientRequest).Url.ToString(), error));
+ }
+ }
+
+ }
+
+
+ #region IBasicCommunication Members
+
+ ///
+ /// SendBytes method
+ ///
+ /// bytes to send
+ public void SendBytes(byte[] bytes)
+ {
+ throw new NotImplementedException();
+ }
+
+
+
+ #endregion
+
+ #region ICommunicationReceiver Members
+
+ ///
+ /// BytesReceived event
+ ///
+ public event EventHandler BytesReceived;
+
+ ///
+ /// Connect method
+ ///
+ public void Connect()
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Disconnect method
+ ///
+ public void Disconnect()
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// IsConnected property
+ ///
+ public bool IsConnected
+ {
+ get { return true; }
+ }
+
+ ///
+ /// TextReceived event
+ ///
+ public event EventHandler TextReceived;
+
+ #endregion
+ }
+
+ ///
+ /// Represents a GenericHttpClientEventArgs
+ ///
+ public class GenericHttpClientEventArgs : EventArgs
+ {
+ ///
+ /// Gets or sets the ResponseText
+ ///
+ public string ResponseText { get; private set; }
+
+ ///
+ /// Gets or sets the RequestPath
+ ///
+ public string RequestPath { get; private set; }
+
+ ///
+ /// Gets or sets the Error
+ ///
+ public HTTP_CALLBACK_ERROR Error { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ /// response text
+ /// request path
+ /// error status
+ public GenericHttpClientEventArgs(string response, string request, HTTP_CALLBACK_ERROR error)
+ {
+ ResponseText = response;
+ RequestPath = request;
+ Error = error;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Comm and IR/IRPortHelper.cs b/src/PepperDash.Essentials.Core/Comm and IR/IRPortHelper.cs
index b4d4069d..6ca4b533 100644
--- a/src/PepperDash.Essentials.Core/Comm and IR/IRPortHelper.cs
+++ b/src/PepperDash.Essentials.Core/Comm and IR/IRPortHelper.cs
@@ -12,8 +12,8 @@ using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
using Serilog.Events;
-namespace PepperDash.Essentials.Core;
-
+namespace PepperDash.Essentials.Core
+{
///
/// Helper class for IR port operations
///
@@ -145,7 +145,7 @@ namespace PepperDash.Essentials.Core;
var port = irDev.IROutputPorts[portNum];
-
+
return port;
}
@@ -157,82 +157,82 @@ namespace PepperDash.Essentials.Core;
/// IrOutputPortController object
public static IrOutputPortController GetIrOutputPortController(DeviceConfig config)
{
- Debug.LogMessage(LogEventLevel.Debug, "Attempting to create new Ir Port Controller");
+ Debug.LogMessage(LogEventLevel.Debug, "Attempting to create new Ir Port Controller");
if (config == null)
{
return null;
}
- var postActivationFunc = new Func (GetIrOutputPort);
+ var postActivationFunc = new Func (GetIrOutputPort);
var irDevice = new IrOutputPortController(config.Key + "-ir", postActivationFunc, config);
return irDevice;
}
/*
- ///
- /// Returns a ready-to-go IrOutputPortController from a DeviceConfig object.
- ///
- public static IrOutputPortController GetIrOutputPortController(DeviceConfig devConf)
- {
- var irControllerKey = devConf.Key + "-ir";
- if (devConf.Properties == null)
+ ///
+ /// GetIrOutputPortController method
+ ///
+ public static IrOutputPortController GetIrOutputPortController(DeviceConfig devConf)
{
- Debug.LogMessage(LogEventLevel.Information, "[{0}] WARNING: Device config does not include properties. IR will not function.", devConf.Key);
- return new IrOutputPortController(irControllerKey, null, "");
- }
+ var irControllerKey = devConf.Key + "-ir";
+ if (devConf.Properties == null)
+ {
+ Debug.LogMessage(LogEventLevel.Information, "[{0}] WARNING: Device config does not include properties. IR will not function.", devConf.Key);
+ return new IrOutputPortController(irControllerKey, null, "");
+ }
- var control = devConf.Properties["control"];
- if (control == null)
- {
- var c = new IrOutputPortController(irControllerKey, null, "");
- Debug.LogMessage(LogEventLevel.Information, c, "WARNING: Device config does not include control properties. IR will not function");
- return c;
- }
+ var control = devConf.Properties["control"];
+ if (control == null)
+ {
+ var c = new IrOutputPortController(irControllerKey, null, "");
+ Debug.LogMessage(LogEventLevel.Information, c, "WARNING: Device config does not include control properties. IR will not function");
+ return c;
+ }
- var portDevKey = control.Value("controlPortDevKey");
- var portNum = control.Value("controlPortNumber");
- IIROutputPorts irDev = null;
+ var portDevKey = control.Value("controlPortDevKey");
+ var portNum = control.Value("controlPortNumber");
+ IIROutputPorts irDev = null;
- if (portDevKey == null)
- {
- var c = new IrOutputPortController(irControllerKey, null, "");
- Debug.LogMessage(LogEventLevel.Information, c, "WARNING: control properties is missing ir device");
- return c;
- }
+ if (portDevKey == null)
+ {
+ var c = new IrOutputPortController(irControllerKey, null, "");
+ Debug.LogMessage(LogEventLevel.Information, c, "WARNING: control properties is missing ir device");
+ return c;
+ }
- if (portNum == 0)
- {
- var c = new IrOutputPortController(irControllerKey, null, "");
- Debug.LogMessage(LogEventLevel.Information, c, "WARNING: control properties is missing ir port number");
- return c;
- }
+ if (portNum == 0)
+ {
+ var c = new IrOutputPortController(irControllerKey, null, "");
+ Debug.LogMessage(LogEventLevel.Information, c, "WARNING: control properties is missing ir port number");
+ return c;
+ }
- if (portDevKey.Equals("controlSystem", StringComparison.OrdinalIgnoreCase)
- || portDevKey.Equals("processor", StringComparison.OrdinalIgnoreCase))
- irDev = Global.ControlSystem;
- else
- irDev = DeviceManager.GetDeviceForKey(portDevKey) as IIROutputPorts;
+ if (portDevKey.Equals("controlSystem", StringComparison.OrdinalIgnoreCase)
+ || portDevKey.Equals("processor", StringComparison.OrdinalIgnoreCase))
+ irDev = Global.ControlSystem;
+ else
+ irDev = DeviceManager.GetDeviceForKey(portDevKey) as IIROutputPorts;
- if (irDev == null)
- {
- var c = new IrOutputPortController(irControllerKey, null, "");
- Debug.LogMessage(LogEventLevel.Information, c, "WARNING: device with IR ports '{0}' not found", portDevKey);
- return c;
- }
+ if (irDev == null)
+ {
+ var c = new IrOutputPortController(irControllerKey, null, "");
+ Debug.LogMessage(LogEventLevel.Information, c, "WARNING: device with IR ports '{0}' not found", portDevKey);
+ return c;
+ }
- if (portNum <= irDev.NumberOfIROutputPorts) // success!
- return new IrOutputPortController(irControllerKey, irDev.IROutputPorts[portNum],
- IrDriverPathPrefix + control["irFile"].Value());
- else
- {
- var c = new IrOutputPortController(irControllerKey, null, "");
- Debug.LogMessage(LogEventLevel.Information, c, "WARNING: device '{0}' IR port {1} out of range",
- portDevKey, portNum);
- return c;
- }
- }*/
+ if (portNum <= irDev.NumberOfIROutputPorts) // success!
+ return new IrOutputPortController(irControllerKey, irDev.IROutputPorts[portNum],
+ IrDriverPathPrefix + control["irFile"].Value());
+ else
+ {
+ var c = new IrOutputPortController(irControllerKey, null, "");
+ Debug.LogMessage(LogEventLevel.Information, c, "WARNING: device '{0}' IR port {1} out of range",
+ portDevKey, portNum);
+ return c;
+ }
+ }*/
}
///
@@ -265,4 +265,5 @@ namespace PepperDash.Essentials.Core;
{
FileName = "";
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Config/AudioControlPointListItem.cs b/src/PepperDash.Essentials.Core/Config/AudioControlPointListItem.cs
index 3a3e2fc8..54cc3574 100644
--- a/src/PepperDash.Essentials.Core/Config/AudioControlPointListItem.cs
+++ b/src/PepperDash.Essentials.Core/Config/AudioControlPointListItem.cs
@@ -6,14 +6,24 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace PepperDash.Essentials.Core.Config;
-
-public class AudioControlPointListItem
+namespace PepperDash.Essentials.Core.Config
{
- [JsonProperty("levelControls")]
- public Dictionary LevelControls { get; set; } = new Dictionary();
+ ///
+ /// Represents a AudioControlPointListItem
+ ///
+ public class AudioControlPointListItem
+ {
+ ///
+ /// Level controls for this audio control point
+ ///
+ [JsonProperty("levelControls")]
+ public Dictionary LevelControls { get; set; } = new Dictionary();
- [JsonProperty("presets")]
- public Dictionary Presets { get; set; } = new Dictionary();
+ ///
+ /// Presets for this audio control point
+ ///
+ [JsonProperty("presets")]
+ public Dictionary Presets { get; set; } = new Dictionary();
+ }
}
diff --git a/src/PepperDash.Essentials.Core/Config/BasicConfig.cs b/src/PepperDash.Essentials.Core/Config/BasicConfig.cs
index 2e499ed4..435d448f 100644
--- a/src/PepperDash.Essentials.Core/Config/BasicConfig.cs
+++ b/src/PepperDash.Essentials.Core/Config/BasicConfig.cs
@@ -7,8 +7,8 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Essentials.Core.Devices;
-namespace PepperDash.Essentials.Core.Config;
-
+namespace PepperDash.Essentials.Core.Config
+{
///
/// Override this and splice on specific room type behavior, as well as other properties
///
@@ -32,32 +32,50 @@ namespace PepperDash.Essentials.Core.Config;
[JsonProperty("sourceLists")]
public Dictionary> SourceLists { get; set; }
- [JsonProperty("destinationLists")]
- public Dictionary> DestinationLists { get; set; }
+ ///
+ /// Gets or sets the DestinationLists
+ ///
+ [JsonProperty("destinationLists")]
+ public Dictionary> DestinationLists { get; set; }
- [JsonProperty("audioControlPointLists")]
- public Dictionary AudioControlPointLists { get; set; }
+ ///
+ /// Gets or sets the AudioControlPointLists
+ ///
+ [JsonProperty("audioControlPointLists")]
+ public Dictionary AudioControlPointLists { get; set; }
- [JsonProperty("cameraLists")]
- public Dictionary> CameraLists { get; set; }
+ ///
+ /// Gets or sets the CameraLists
+ ///
+ [JsonProperty("cameraLists")]
+ public Dictionary> CameraLists { get; set; }
- [JsonProperty("tieLines")]
- public List TieLines { get; set; }
+ ///
+ /// Gets or sets the TieLines
+ ///
+ [JsonProperty("tieLines")]
+ public List TieLines { get; set; }
- [JsonProperty("joinMaps")]
- public Dictionary JoinMaps { get; set; }
+ ///
+ /// Gets or sets the JoinMaps
+ ///
+ [JsonProperty("joinMaps")]
+ public Dictionary JoinMaps { get; set; }
- public BasicConfig()
- {
- Info = new InfoConfig();
- Devices = new List();
- SourceLists = new Dictionary>();
- DestinationLists = new Dictionary>();
- AudioControlPointLists = new Dictionary();
- CameraLists = new Dictionary>();
- TieLines = new List();
- JoinMaps = new Dictionary();
- }
+ ///
+ /// BasicConfig Constructor
+ ///
+ public BasicConfig()
+ {
+ Info = new InfoConfig();
+ Devices = new List();
+ SourceLists = new Dictionary>();
+ DestinationLists = new Dictionary>();
+ AudioControlPointLists = new Dictionary();
+ CameraLists = new Dictionary>();
+ TieLines = new List();
+ JoinMaps = new Dictionary();
+ }
///
/// Checks SourceLists for a given list and returns it if found. Otherwise, returns null
@@ -70,11 +88,11 @@ namespace PepperDash.Essentials.Core.Config;
return SourceLists[key];
}
- ///
- /// Retrieves a DestinationListItem based on the key
- ///
- /// key of the list to retrieve
- /// DestinationList if the key exists, null otherwise
+ ///
+ /// Retrieves a DestinationListItem based on the key
+ ///
+ /// key of the list to retrieve
+ /// DestinationList if the key exists, null otherwise
public Dictionary GetDestinationListForKey(string key)
{
if (DestinationLists == null || string.IsNullOrEmpty(key) || !DestinationLists.ContainsKey(key))
@@ -85,47 +103,51 @@ namespace PepperDash.Essentials.Core.Config;
return DestinationLists[key];
}
- ///
- /// Retrieves a AudioControlPointList based on the key
- ///
- /// key of the list to retrieve
- /// AudioControlPointList if the key exists, null otherwise
- public AudioControlPointListItem GetAudioControlPointListForKey(string key)
- {
- if (AudioControlPointLists == null || string.IsNullOrEmpty(key) || !AudioControlPointLists.ContainsKey(key))
- return null;
-
- return AudioControlPointLists[key];
- }
-
- ///
- /// Checks CameraLists for a given list and returns it if found. Otherwise, returns null
- ///
- public Dictionary GetCameraListForKey(string key)
- {
- if (CameraLists == null || string.IsNullOrEmpty(key) || !CameraLists.ContainsKey(key))
- return null;
-
- return CameraLists[key];
- }
-
- ///
- /// Checks Devices for an item with a Key that matches and returns it if found. Otherwise, retunes null
- ///
- /// Key of desired device
- ///
- public DeviceConfig GetDeviceForKey(string key)
- {
- if (string.IsNullOrEmpty(key))
- return null;
-
- var deviceConfig = Devices.FirstOrDefault(d => d.Key.Equals(key));
-
- if (deviceConfig != null)
- return deviceConfig;
- else
+ ///
+ /// Retrieves a AudioControlPointList based on the key
+ ///
+ /// key of the list to retrieve
+ /// AudioControlPointList if the key exists, null otherwise
+ ///
+ /// GetAudioControlPointListForKey method
+ ///
+ public AudioControlPointListItem GetAudioControlPointListForKey(string key)
{
- return null;
+ if (AudioControlPointLists == null || string.IsNullOrEmpty(key) || !AudioControlPointLists.ContainsKey(key))
+ return null;
+
+ return AudioControlPointLists[key];
+ }
+
+ ///
+ /// Checks CameraLists for a given list and returns it if found. Otherwise, returns null
+ ///
+ /// Key of desired camera list
+ public Dictionary GetCameraListForKey(string key)
+ {
+ if (CameraLists == null || string.IsNullOrEmpty(key) || !CameraLists.ContainsKey(key))
+ return null;
+
+ return CameraLists[key];
+ }
+
+ ///
+ /// Checks Devices for an item with a Key that matches and returns it if found. Otherwise, retunes null
+ ///
+ /// Key of desired device
+ public DeviceConfig GetDeviceForKey(string key)
+ {
+ if (string.IsNullOrEmpty(key))
+ return null;
+
+ var deviceConfig = Devices.FirstOrDefault(d => d.Key.Equals(key));
+
+ if (deviceConfig != null)
+ return deviceConfig;
+ else
+ {
+ return null;
+ }
}
- }
}
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Config/ConfigPropertiesHelpers.cs b/src/PepperDash.Essentials.Core/Config/ConfigPropertiesHelpers.cs
index 9eb8e61d..99633266 100644
--- a/src/PepperDash.Essentials.Core/Config/ConfigPropertiesHelpers.cs
+++ b/src/PepperDash.Essentials.Core/Config/ConfigPropertiesHelpers.cs
@@ -9,23 +9,27 @@ using PepperDash.Essentials.Core;
using PepperDash.Core;
using Newtonsoft.Json.Linq;
-namespace PepperDash.Essentials.Core.Config;
-
-public class ConfigPropertiesHelpers
+namespace PepperDash.Essentials.Core.Config
{
///
- /// Returns the value of properties.hasAudio, or false if not defined
+ /// Represents a ConfigPropertiesHelpers
///
- public static bool GetHasAudio(DeviceConfig deviceConfig)
+ public class ConfigPropertiesHelpers
{
- return deviceConfig.Properties.Value("hasAudio");
- }
+ ///
+ /// GetHasAudio method
+ ///
+ public static bool GetHasAudio(DeviceConfig deviceConfig)
+ {
+ return deviceConfig.Properties.Value("hasAudio");
+ }
- ///
- /// Returns the value of properties.hasControls, or false if not defined
- ///
- public static bool GetHasControls(DeviceConfig deviceConfig)
- {
- return deviceConfig.Properties.Value("hasControls");
+ ///
+ /// Returns the value of properties.hasControls, or false if not defined
+ ///
+ public static bool GetHasControls(DeviceConfig deviceConfig)
+ {
+ return deviceConfig.Properties.Value("hasControls");
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Config/DeviceConfig.cs b/src/PepperDash.Essentials.Core/Config/DeviceConfig.cs
index 49994abc..b8620bb9 100644
--- a/src/PepperDash.Essentials.Core/Config/DeviceConfig.cs
+++ b/src/PepperDash.Essentials.Core/Config/DeviceConfig.cs
@@ -11,71 +11,116 @@ using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core;
-namespace PepperDash.Essentials.Core.Config;
-
-public class DeviceConfig
+namespace PepperDash.Essentials.Core.Config
{
- [JsonProperty("key")]
- public string Key { get; set; }
-
- [JsonProperty("uid")]
- public int Uid { get; set; }
-
- [JsonProperty("name")]
- public string Name { get; set; }
-
- [JsonProperty("group")]
- public string Group { get; set; }
-
- [JsonProperty("type")]
- public string Type { get; set; }
-
- [JsonProperty("properties")]
- [JsonConverter(typeof(DevicePropertiesConverter))]
- public JToken Properties { get; set; }
-
- public DeviceConfig(DeviceConfig dc)
+ ///
+ /// Represents a DeviceConfig
+ ///
+ public class DeviceConfig
{
- Key = dc.Key;
- Uid = dc.Uid;
- Name = dc.Name;
- Group = dc.Group;
- Type = dc.Type;
+ ///
+ /// Gets or sets the Key
+ ///
+ [JsonProperty("key")]
+ public string Key { get; set; }
- Properties = JToken.Parse(dc.Properties.ToString());
+ ///
+ /// Gets or sets the Uid
+ ///
+ [JsonProperty("uid")]
+ public int Uid { get; set; }
- //Properties = JToken.FromObject(dc.Properties);
- }
+ ///
+ /// Gets or sets the Name
+ ///
+ [JsonProperty("name")]
+ public string Name { get; set; }
- public DeviceConfig() {}
-}
+ ///
+ /// Gets or sets the Group
+ ///
+ [JsonProperty("group")]
+ public string Group { get; set; }
-///
-///
-///
-public class DevicePropertiesConverter : JsonConverter
-{
+ ///
+ /// Gets or sets the Type
+ ///
+ [JsonProperty("type")]
+ public string Type { get; set; }
- public override bool CanConvert(Type objectType)
- {
- return objectType == typeof(JToken);
- }
+ ///
+ /// Gets or sets the Properties
+ ///
+ [JsonProperty("properties")]
+ [JsonConverter(typeof(DevicePropertiesConverter))]
+ public JToken Properties { get; set; }
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- return JToken.ReadFrom(reader);
- }
-
- public override bool CanWrite
- {
- get
+ ///
+ /// Constructor
+ ///
+ /// device config
+ public DeviceConfig(DeviceConfig dc)
{
- return false;
+ Key = dc.Key;
+ Uid = dc.Uid;
+ Name = dc.Name;
+ Group = dc.Group;
+ Type = dc.Type;
+
+ Properties = JToken.Parse(dc.Properties.ToString());
+
+ //Properties = JToken.FromObject(dc.Properties);
+ }
+
+ ///
+ /// Default Constructor
+ ///
+ public DeviceConfig() { }
+ }
+
+ ///
+ /// Represents a DevicePropertiesConverter
+ ///
+ public class DevicePropertiesConverter : JsonConverter
+ {
+
+ ///
+ /// CanConvert method
+ ///
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(JToken);
+ }
+
+ ///
+ /// ReadJson method
+ ///
+ /// reader to use
+ /// type of object being read
+ /// existing value for the object
+ /// serializer to use
+ ///
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ return JToken.ReadFrom(reader);
+ }
+
+ ///
+ public override bool CanWrite
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// WriteJson method
+ ///
+ ///
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ throw new NotImplementedException("SOD OFF HOSER");
}
}
-
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- throw new NotImplementedException("SOD OFF HOSER");
- }
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs
index e402341e..37cd4eba 100644
--- a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs
+++ b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs
@@ -11,8 +11,8 @@ using PepperDash.Core;
using PepperDash.Core.Config;
using Serilog.Events;
-namespace PepperDash.Essentials.Core.Config;
-
+namespace PepperDash.Essentials.Core.Config
+{
///
/// Loads the ConfigObject from the file
///
@@ -22,7 +22,7 @@ namespace PepperDash.Essentials.Core.Config;
/// Local Config Present Message
///
public const string LocalConfigPresent =
- @"
+ @"
***************************************************
************* Using Local config file *************
***************************************************";
@@ -35,250 +35,256 @@ namespace PepperDash.Essentials.Core.Config;
///
/// LoadConfig2 method
///
- public static bool LoadConfig()
+ public static bool LoadConfig2()
{
Debug.LogMessage(LogEventLevel.Information, "Loading unmerged system/template portal configuration file.");
try
{
- // Check for local config file first
- var filePath = Global.FilePathPrefix + ConfigWriter.LocalConfigFolder + Global.DirectorySeparator + Global.ConfigFileName;
+ // Check for local config file first
+ var filePath = Global.FilePathPrefix + ConfigWriter.LocalConfigFolder + Global.DirectorySeparator + Global.ConfigFileName;
- bool localConfigFound = false;
+ bool localConfigFound = false;
- Debug.LogMessage(LogEventLevel.Information, "Attempting to load Local config file: '{0}'", filePath);
+ Debug.LogMessage(LogEventLevel.Information, "Attempting to load Local config file: '{0}'", filePath);
- // Check for local config directory first
+ // Check for local config directory first
- var configFiles = GetConfigFiles(filePath);
-
- if (configFiles != null)
- {
- if (configFiles.Length > 1)
- {
- Debug.LogMessage(LogEventLevel.Information,
- "****Error: Multiple Local Configuration files present. Please ensure only a single file exists and reset program.****");
- return false;
- }
- if(configFiles.Length == 1)
- {
- localConfigFound = true;
-
- }
- }
- else
- {
- Debug.LogMessage(LogEventLevel.Information,
- "Local Configuration file not present.", filePath);
-
- }
-
- // Check for Portal Config
- if(!localConfigFound)
- {
- filePath = Global.FilePathPrefix + Global.ConfigFileName;
-
- Debug.LogMessage(LogEventLevel.Information, "Attempting to load Portal config file: '{0}'", filePath);
-
- configFiles = GetConfigFiles(filePath);
+ var configFiles = GetConfigFiles(filePath);
if (configFiles != null)
{
- Debug.LogMessage(LogEventLevel.Verbose, "{0} config files found matching pattern", configFiles.Length);
-
if (configFiles.Length > 1)
{
Debug.LogMessage(LogEventLevel.Information,
- "****Error: Multiple Portal Configuration files present. Please ensure only a single file exists and reset program.****");
+ "****Error: Multiple Local Configuration files present. Please ensure only a single file exists and reset program.****");
return false;
}
- else if (configFiles.Length == 1)
+ if(configFiles.Length == 1)
{
- Debug.LogMessage(LogEventLevel.Information, "Found Portal config file: '{0}'", filePath);
- }
- else
- {
- Debug.LogMessage(LogEventLevel.Information, "No config file found.");
- return false;
+ localConfigFound = true;
+
}
}
else
{
Debug.LogMessage(LogEventLevel.Information,
- "ERROR: Portal Configuration file not present. Please load file and reset program.");
- return false;
+ "Local Configuration file not present.", filePath);
+
}
- }
- // Get the actual file path
- filePath = configFiles[0].FullName;
-
- // Generate debug statement if using a local file.
- if (localConfigFound)
- {
- GetLocalFileMessage(filePath);
- }
-
- // Read the file
- using (StreamReader fs = new StreamReader(filePath))
- {
- Debug.LogMessage(LogEventLevel.Information, "Loading config file: '{0}'", filePath);
-
- var fileContents = fs.ReadToEnd();
-
- if (localConfigFound)
+ // Check for Portal Config
+ if(!localConfigFound)
{
- ConfigObject = JObject.Parse(fileContents).ToObject();
- Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded Local Config");
- return ConfigObject != null;
- }
- else
- {
- var parsedConfig = JObject.Parse(fileContents);
+ filePath = Global.FilePathPrefix + Global.ConfigFileName;
- // Check if it's a v2 config (check for "version" node)
- // this means it's already merged by the Portal API
- // from the v2 config tool
- var isV2Config = parsedConfig["versions"] != null;
+ Debug.LogMessage(LogEventLevel.Information, "Attempting to load Portal config file: '{0}'", filePath);
- if (isV2Config)
+ configFiles = GetConfigFiles(filePath);
+
+ if (configFiles != null)
{
- Debug.LogMessage(LogEventLevel.Information, "Config file is a v2 format, no merge necessary.");
- ConfigObject = parsedConfig.ToObject();
- Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded v2 Config");
- return ConfigObject != null;
+ Debug.LogMessage(LogEventLevel.Verbose, "{0} config files found matching pattern", configFiles.Length);
+
+ if (configFiles.Length > 1)
+ {
+ Debug.LogMessage(LogEventLevel.Information,
+ "****Error: Multiple Portal Configuration files present. Please ensure only a single file exists and reset program.****");
+ return false;
+ }
+ else if (configFiles.Length == 1)
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Found Portal config file: '{0}'", filePath);
+ }
+ else
+ {
+ Debug.LogMessage(LogEventLevel.Information, "No config file found.");
+ return false;
+ }
}
-
- // Extract SystemUrl and TemplateUrl into final config output
- ConfigObject = PortalConfigReader.MergeConfigs(parsedConfig).ToObject();
-
- if (ConfigObject == null)
+ else
{
- Debug.LogMessage(LogEventLevel.Warning, "Config merge produced a null ConfigObject.");
+ Debug.LogMessage(LogEventLevel.Information,
+ "ERROR: Portal Configuration file not present. Please load file and reset program.");
return false;
}
-
- if (parsedConfig["system_url"] != null)
- {
- ConfigObject.SystemUrl = parsedConfig["system_url"].Value();
- }
-
- if (parsedConfig["template_url"] != null)
- {
- ConfigObject.TemplateUrl = parsedConfig["template_url"].Value();
- }
}
- Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded Merged Config");
+ // Get the actual file path
+ filePath = configFiles[0].FullName;
- return true;
- }
+ // Generate debug statement if using a local file.
+ if (localConfigFound)
+ {
+ GetLocalFileMessage(filePath);
+ }
+
+ // Read the file
+ using (StreamReader fs = new StreamReader(filePath))
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Loading config file: '{0}'", filePath);
+
+ if (localConfigFound)
+ {
+ ConfigObject = JObject.Parse(fs.ReadToEnd()).ToObject();
+
+ Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded Local Config");
+
+ return true;
+ }
+ else
+ {
+ var parsedConfig = JObject.Parse(fs.ReadToEnd());
+
+ // A config is v1 if it has separate "system" and "template" nodes that
+ // need to be merged. A v2 config is already merged by the Portal API and
+ // will not have "system"/"template" nodes. This is independent of whether
+ // a "versions" node is present, which only carries version metadata and
+ // can appear on either a v1 or v2 config.
+ var isV1Config = parsedConfig["system"] != null && parsedConfig["template"] != null;
+
+ if (!isV1Config)
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Config file is a v2 format, no merge necessary.");
+ ConfigObject = parsedConfig.ToObject();
+ Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded v2 Config");
+ return true;
+ }
+
+ Debug.LogMessage(LogEventLevel.Information, "Config file is a v1 format, merging system and template.");
+
+ // Extract SystemUrl and TemplateUrl into final config output
+ ConfigObject = PortalConfigReader.MergeConfigs(parsedConfig).ToObject();
+
+ if (parsedConfig["system_url"] != null)
+ {
+ ConfigObject.SystemUrl = parsedConfig["system_url"].Value();
+ }
+
+ if (parsedConfig["template_url"] != null)
+ {
+ ConfigObject.TemplateUrl = parsedConfig["template_url"].Value();
+ }
+
+ // MergeConfigs does not carry the "versions" node forward, so it must be
+ // applied separately to ensure it's preserved in the merged config.
+ if (parsedConfig["versions"] != null)
+ {
+ ConfigObject.Versions = parsedConfig["versions"].ToObject();
+ }
+ }
+
+ Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded Merged Config");
+
+ return true;
+ }
}
catch (Exception e)
{
- Debug.LogMessage(LogEventLevel.Information, "ERROR: Config load failed: \r{0}", e);
+ Debug.LogMessage(LogEventLevel.Information, "ERROR: Config load failed: \r{0}", e);
return false;
}
}
- ///
- /// Returns all the files from the directory specified.
- ///
- ///
- ///
- public static FileInfo[] GetConfigFiles(string filePath)
- {
- // Get the directory
- var dir = Path.GetDirectoryName(filePath);
-
- if (Directory.Exists(dir))
+ ///
+ /// Returns all the files from the directory specified.
+ ///
+ /// path to the directory
+ /// config files
+ public static FileInfo[] GetConfigFiles(string filePath)
{
- Debug.LogMessage(LogEventLevel.Debug, "Searching in Directory '{0}'", dir);
- // Get the directory info
- var dirInfo = new DirectoryInfo(dir);
+ // Get the directory
+ var dir = Path.GetDirectoryName(filePath);
- // Get the file name
- var fileName = Path.GetFileName(filePath);
- Debug.LogMessage(LogEventLevel.Debug, "For Config Files matching: '{0}'", fileName);
+ if (Directory.Exists(dir))
+ {
+ Debug.LogMessage(LogEventLevel.Debug, "Searching in Directory '{0}'", dir);
+ // Get the directory info
+ var dirInfo = new DirectoryInfo(dir);
- // Get the files that match from the directory
- return dirInfo.GetFiles(fileName);
+ // Get the file name
+ var fileName = Path.GetFileName(filePath);
+ Debug.LogMessage(LogEventLevel.Debug, "For Config Files matching: '{0}'", fileName);
+
+ // Get the files that match from the directory
+ return dirInfo.GetFiles(fileName);
+ }
+ else
+ {
+ Debug.LogMessage(LogEventLevel.Information,
+ "Directory not found: ", dir);
+
+ return null;
+ }
}
- else
- {
- Debug.LogMessage(LogEventLevel.Information,
- "Directory not found: ", dir);
-
- return null;
- }
- }
///
/// Returns the group for a given device key in config
///
- ///
- ///
- public static string GetGroupForDeviceKey(string key)
- {
- var dev = ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
- return dev == null ? null : dev.Group;
- }
+ /// Key of the device
+ /// Group name if the device is found, null otherwise
+ public static string GetGroupForDeviceKey(string key)
+ {
+ var dev = ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
+ return dev == null ? null : dev.Group;
+ }
private static void GetLocalFileMessage(string filePath)
{
- var filePathLength = filePath.Length + 2;
- var debugStringWidth = filePathLength + 12;
+ var filePathLength = filePath.Length + 2;
+ var debugStringWidth = filePathLength + 12;
- if (debugStringWidth < 51)
- {
- debugStringWidth = 51;
- }
- var qualifier = (filePathLength % 2 != 0)
- ? " Using Local Config File "
- : " Using Local Config File ";
- var bookend1 = (debugStringWidth - qualifier.Length) / 2;
- var bookend2 = (debugStringWidth - filePathLength) / 2;
+ if (debugStringWidth < 51)
+ {
+ debugStringWidth = 51;
+ }
+ var qualifier = (filePathLength % 2 != 0)
+ ? " Using Local Config File "
+ : " Using Local Config File ";
+ var bookend1 = (debugStringWidth - qualifier.Length) / 2;
+ var bookend2 = (debugStringWidth - filePathLength) / 2;
var newDebugString = new StringBuilder()
.Append(CrestronEnvironment.NewLine)
- // Line 1
+ // Line 1
.Append(new string('*', debugStringWidth))
.Append(CrestronEnvironment.NewLine)
- // Line 2
+ // Line 2
.Append(new string('*', debugStringWidth))
.Append(CrestronEnvironment.NewLine)
- // Line 3
+ // Line 3
.Append(new string('*', 2))
.Append(new string(' ', debugStringWidth - 4))
.Append(new string('*', 2))
.Append(CrestronEnvironment.NewLine)
- // Line 4
+ // Line 4
.Append(new string('*', 2))
.Append(new string(' ', bookend1 - 2))
.Append(qualifier)
.Append(new string(' ', bookend1 - 2))
.Append(new string('*', 2))
.Append(CrestronEnvironment.NewLine)
- // Line 5
+ // Line 5
.Append(new string('*', 2))
.Append(new string(' ', bookend2 - 2))
.Append(" " + filePath + " ")
.Append(new string(' ', bookend2 - 2))
.Append(new string('*', 2))
.Append(CrestronEnvironment.NewLine)
- // Line 6
+ // Line 6
.Append(new string('*', 2))
.Append(new string(' ', debugStringWidth - 4))
.Append(new string('*', 2))
.Append(CrestronEnvironment.NewLine)
- // Line 7
+ // Line 7
.Append(new string('*', debugStringWidth))
.Append(CrestronEnvironment.NewLine)
- // Line 8
+ // Line 8
.Append(new string('*', debugStringWidth));
- Debug.LogMessage(LogEventLevel.Verbose, "Found Local config file: '{0}'", filePath);
- Debug.LogMessage(LogEventLevel.Information, newDebugString.ToString());
+ Debug.LogMessage(LogEventLevel.Verbose, "Found Local config file: '{0}'", filePath);
+ Debug.LogMessage(LogEventLevel.Information, newDebugString.ToString());
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigUpdater.cs b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigUpdater.cs
new file mode 100644
index 00000000..2ba33df0
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigUpdater.cs
@@ -0,0 +1,281 @@
+
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronIO;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Crestron.SimplSharp.Net.Http;
+using Crestron.SimplSharpPro.Diagnostics;
+
+using PepperDash.Core;
+using Serilog.Events;
+
+namespace PepperDash.Essentials.Core.Config
+{
+ ///
+ /// ConfigUpdater class
+ ///
+ [Obsolete("ConfigUpdater is no longer supported and will be removed in a future release.")]
+ public static class ConfigUpdater
+ {
+ ///
+ /// ConfigStatusChanged event
+ ///
+ public static event EventHandler ConfigStatusChanged;
+
+ ///
+ /// GetConfigFromServer method
+ ///
+ /// URL of the config server
+ public static void GetConfigFromServer(string url)
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Attempting to get new config from '{0}'", url);
+
+ // HTTP GET
+ var req = new HttpClientRequest();
+
+ try
+ {
+ req.RequestType = RequestType.Get;
+ req.Url.Parse(url);
+
+ new HttpClient().DispatchAsync(req, (r, e) =>
+ {
+ if (e == HTTP_CALLBACK_ERROR.COMPLETED)
+ {
+ if (r.Code == 200)
+ {
+ var newConfig = r.ContentString;
+
+ OnStatusUpdate(eUpdateStatus.ConfigFileReceived);
+
+ ArchiveExistingPortalConfigs();
+
+ CheckForLocalConfigAndDelete();
+
+ WriteConfigToFile(newConfig);
+
+ RestartProgram();
+ }
+ else
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Config Update Process Stopped. Failed to get config file from server: {0}", r.Code);
+ OnStatusUpdate(eUpdateStatus.UpdateFailed);
+ }
+ }
+ else
+ Debug.LogMessage(LogEventLevel.Information, "Request for config from Server Failed: {0}", e);
+ });
+ }
+ catch (Exception e)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, "Error Getting Config from Server: {0}", e);
+ }
+
+ }
+
+ static void OnStatusUpdate(eUpdateStatus status)
+ {
+ var handler = ConfigStatusChanged;
+
+ if (handler != null)
+ {
+ handler(typeof(ConfigUpdater), new ConfigStatusEventArgs(status));
+ }
+ }
+
+ static void WriteConfigToFile(string configData)
+ {
+ var filePath = Global.FilePathPrefix + "configurationFile-updated.json";
+
+ try
+ {
+ var config = JObject.Parse(configData).ToObject();
+
+ ConfigWriter.WriteFile(filePath, configData);
+
+ OnStatusUpdate(eUpdateStatus.WritingConfigFile);
+ }
+ catch (Exception e)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, "Error parsing new config: {0}", e);
+
+ OnStatusUpdate(eUpdateStatus.UpdateFailed);
+ }
+ }
+
+ ///
+ /// Checks for any existing portal config files and archives them
+ ///
+ static void ArchiveExistingPortalConfigs()
+ {
+ var filePath = Global.FilePathPrefix + Global.ConfigFileName;
+
+ var configFiles = ConfigReader.GetConfigFiles(filePath);
+
+ if (configFiles != null)
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Existing config files found. Moving to Archive folder.");
+
+ OnStatusUpdate(eUpdateStatus.ArchivingConfigs);
+
+ MoveFilesToArchiveFolder(configFiles);
+ }
+ else
+ {
+ Debug.LogMessage(LogEventLevel.Information, "No Existing config files found in '{0}'. Nothing to archive", filePath);
+ }
+ }
+
+ ///
+ /// Checks for presence of archive folder and if found deletes contents.
+ /// Moves any config files to the archive folder and adds a .bak suffix
+ ///
+ ///
+ static void MoveFilesToArchiveFolder(FileInfo[] files)
+ {
+ string archiveDirectoryPath = Global.FilePathPrefix + "archive";
+
+ if (!Directory.Exists(archiveDirectoryPath))
+ {
+ // Directory does not exist, create it
+ Directory.Create(archiveDirectoryPath);
+ }
+ else
+ {
+ // Directory exists, first clear any contents
+ var archivedConfigFiles = ConfigReader.GetConfigFiles(archiveDirectoryPath + Global.DirectorySeparator + Global.ConfigFileName + ".bak");
+
+ if (archivedConfigFiles != null && archivedConfigFiles.Length > 0)
+ {
+ Debug.LogMessage(LogEventLevel.Information, "{0} Existing files found in archive folder. Deleting.", archivedConfigFiles.Length);
+
+ for (int i = 0; i < archivedConfigFiles.Length; i++)
+ {
+ var file = archivedConfigFiles[i];
+ Debug.LogMessage(LogEventLevel.Information, "Deleting archived file: '{0}'", file.FullName);
+ file.Delete();
+ }
+ }
+
+ }
+
+ // Move any files from the program folder to the archive folder
+ foreach (var file in files)
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Moving config file '{0}' to archive folder", file.FullName);
+
+ // Moves the file and appends the .bak extension
+ var fileDest = archiveDirectoryPath + "/" + file.Name + ".bak";
+ if (!File.Exists(fileDest))
+ {
+ file.MoveTo(fileDest);
+ }
+ else
+ Debug.LogMessage(LogEventLevel.Information, "Cannot move file to archive folder. Existing file already exists with same name: '{0}'", fileDest);
+ }
+ }
+
+ ///
+ /// Checks for LocalConfig folder in file system and deletes if found
+ ///
+ static void CheckForLocalConfigAndDelete()
+ {
+ var folderPath = Global.FilePathPrefix + ConfigWriter.LocalConfigFolder;
+
+ if (Directory.Exists(folderPath))
+ {
+ OnStatusUpdate(eUpdateStatus.DeletingLocalConfig);
+ Directory.Delete(folderPath);
+ Debug.LogMessage(LogEventLevel.Information, "Local Config Found in '{0}'. Deleting.", folderPath);
+ }
+ }
+
+ ///
+ /// Connects to the processor via SSH and restarts the program
+ ///
+ static void RestartProgram()
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Attempting to Reset Program");
+
+ OnStatusUpdate(eUpdateStatus.RestartingProgram);
+
+ string response = string.Empty;
+
+ CrestronConsole.SendControlSystemCommand(string.Format("progreset -p:{0}", InitialParametersClass.ApplicationNumber), ref response);
+
+ Debug.LogMessage(LogEventLevel.Debug, "Console Response: {0}", response);
+ }
+
+ }
+
+ ///
+ /// Enumeration of eUpdateStatus values
+ ///
+ public enum eUpdateStatus
+ {
+ ///
+ /// UpdateStarted status
+ ///
+ UpdateStarted,
+
+ ///
+ /// ConfigFileReceived status
+ ///
+ ConfigFileReceived,
+
+ ///
+ /// ArchivingConfigs status
+ ///
+ ArchivingConfigs,
+
+ ///
+ /// DeletingLocalConfig status
+ ///
+ DeletingLocalConfig,
+
+ ///
+ /// WritingConfigFile status
+ ///
+ WritingConfigFile,
+
+ ///
+ /// RestartingProgram status
+ ///
+ RestartingProgram,
+
+ ///
+ /// UpdateSucceeded status
+ ///
+ UpdateSucceeded,
+
+ ///
+ /// UpdateFailed status
+ ///
+ UpdateFailed
+ }
+
+ ///
+ /// Represents a ConfigStatusEventArgs
+ ///
+ public class ConfigStatusEventArgs : EventArgs
+ {
+ ///
+ /// Gets or sets the UpdateStatus
+ ///
+ public eUpdateStatus UpdateStatus { get; private set; }
+
+ ///
+ /// ConfigStatusEventArgs Constructor
+ ///
+ ///
+ public ConfigStatusEventArgs(eUpdateStatus status)
+ {
+ UpdateStatus = status;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigWriter.cs b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigWriter.cs
index 423e05be..93517a3c 100644
--- a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigWriter.cs
+++ b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigWriter.cs
@@ -1,182 +1,184 @@
using System;
+using System.Collections.Generic;
using System.Linq;
-using System.Threading;
-using Timer = System.Timers.Timer;
+using System.Text;
+using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using Serilog.Events;
-namespace PepperDash.Essentials.Core.Config;
-
-///
-/// Responsible for updating config at runtime, and writing the updates out to a local file
-///
-public class ConfigWriter
+namespace PepperDash.Essentials.Core.Config
{
///
- /// The name of the subfolder where the config file will be written
+ /// Responsible for updating config at runtime, and writing the updates out to a local file
///
- public const string LocalConfigFolder = "LocalConfig";
-
- ///
- /// The amount of time in milliseconds to wait after the last config update before writing the config file. This is to prevent multiple rapid updates from causing multiple file writes.
- /// Default is 30 seconds.
- ///
- public const long WriteTimeoutInMs = 30000;
-
- private static Timer WriteTimer;
- static readonly object _fileLock = new();
-
-
- static ConfigWriter()
+ public class ConfigWriter
{
- WriteTimer = new Timer(WriteTimeoutInMs);
- WriteTimer.Elapsed += (s, e) => WriteConfigFile(null);
+ ///
+ /// LocalConfigFolder constant
+ ///
+ public const string LocalConfigFolder = "LocalConfig";
- }
+ ///
+ /// WriteTimeout constant
+ ///
+ public const long WriteTimeout = 30000;
- ///
- /// Updates the config properties of a device
- ///
- ///
- ///
- ///
- public static bool UpdateDeviceProperties(string deviceKey, JToken properties)
- {
- bool success = false;
+ ///
+ /// WriteTimer variable
+ ///
+ public static CTimer WriteTimer;
- // Get the current device config
- var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(deviceKey));
+ static CCriticalSection fileLock = new CCriticalSection();
- if (deviceConfig != null)
+ ///
+ /// Updates the config properties of a device
+ ///
+ /// The key of the device to update
+ /// The new properties for the device
+ /// True if the update was successful, otherwise false
+ public static bool UpdateDeviceProperties(string deviceKey, JToken properties)
{
- // Replace the current properties JToken with the new one passed into this method
- deviceConfig.Properties = properties;
+ bool success = false;
- Debug.LogMessage(LogEventLevel.Debug, "Updated properties of device: '{0}'", deviceKey);
+ // Get the current device config
+ var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(deviceKey));
- success = true;
- }
-
- ResetTimer();
-
- return success;
- }
-
- ///
- /// Updates the config properties of a device
- ///
- public static bool UpdateDeviceConfig(DeviceConfig config)
- {
- bool success = false;
-
- var deviceConfigIndex = ConfigReader.ConfigObject.Devices.FindIndex(d => d.Key.Equals(config.Key));
-
- if (deviceConfigIndex >= 0)
- {
- ConfigReader.ConfigObject.Devices[deviceConfigIndex] = config;
-
- Debug.LogMessage(LogEventLevel.Debug, "Updated config of device: '{0}'", config.Key);
-
- success = true;
- }
-
- ResetTimer();
-
- return success;
- }
-
- ///
- /// Updates the config properties of a room
- ///
- public static bool UpdateRoomConfig(DeviceConfig config)
- {
- bool success = false;
-
- var roomConfigIndex = ConfigReader.ConfigObject.Rooms.FindIndex(d => d.Key.Equals(config.Key));
-
- if (roomConfigIndex >= 0)
- {
- ConfigReader.ConfigObject.Rooms[roomConfigIndex] = config;
-
- Debug.LogMessage(LogEventLevel.Debug, "Updated config of room: '{0}'", config.Key);
-
- success = true;
- }
-
- ResetTimer();
-
- return success;
- }
-
- ///
- /// Resets (or starts) the write timer
- ///
- static void ResetTimer()
- {
- WriteTimer.Stop();
- WriteTimer.Interval = WriteTimeoutInMs;
- WriteTimer.Start();
-
- Debug.LogMessage(LogEventLevel.Debug, "Config File write timer has been reset.");
- }
-
- ///
- /// Writes the current config to a file in the LocalConfig subfolder
- ///
- ///
- private static void WriteConfigFile(object o)
- {
- var filePath = Global.FilePathPrefix + LocalConfigFolder + Global.DirectorySeparator + "configurationFile.json";
-
- var configData = JsonConvert.SerializeObject(ConfigReader.ConfigObject);
-
- WriteFile(filePath, configData);
- }
-
- ///
- /// Writes the specified configuration data to a file.
- ///
- /// The path of the file to write to.
- /// The configuration data to write.
- public static void WriteFile(string filePath, string configData)
- {
- if (WriteTimer != null)
- WriteTimer.Stop();
-
- Debug.LogMessage(LogEventLevel.Information, "Writing Configuration to file");
-
- Debug.LogMessage(LogEventLevel.Information, "Attempting to write config file: '{0}'", filePath);
-
- var lockAcquired = false;
- try
- {
- lockAcquired = Monitor.TryEnter(_fileLock);
- if (lockAcquired)
+ if (deviceConfig != null)
{
- using (StreamWriter sw = new StreamWriter(filePath))
+ // Replace the current properties JToken with the new one passed into this method
+ deviceConfig.Properties = properties;
+
+ Debug.LogMessage(LogEventLevel.Debug, "Updated properties of device: '{0}'", deviceKey);
+
+ success = true;
+ }
+
+ ResetTimer();
+
+ return success;
+ }
+
+ ///
+ /// UpdateDeviceConfig method
+ ///
+ /// The new device config
+ /// True if the update was successful, otherwise false
+ public static bool UpdateDeviceConfig(DeviceConfig config)
+ {
+ bool success = false;
+
+ var deviceConfigIndex = ConfigReader.ConfigObject.Devices.FindIndex(d => d.Key.Equals(config.Key));
+
+ if (deviceConfigIndex >= 0)
+ {
+ ConfigReader.ConfigObject.Devices[deviceConfigIndex] = config;
+
+ Debug.LogMessage(LogEventLevel.Debug, "Updated config of device: '{0}'", config.Key);
+
+ success = true;
+ }
+
+ ResetTimer();
+
+ return success;
+ }
+
+ ///
+ /// UpdateRoomConfig method
+ ///
+ /// The new room config
+ /// True if the update was successful, otherwise false
+ public static bool UpdateRoomConfig(DeviceConfig config)
+ {
+ bool success = false;
+
+ var roomConfigIndex = ConfigReader.ConfigObject.Rooms.FindIndex(d => d.Key.Equals(config.Key));
+
+ if (roomConfigIndex >= 0)
+ {
+ ConfigReader.ConfigObject.Rooms[roomConfigIndex] = config;
+
+ Debug.LogMessage(LogEventLevel.Debug, "Updated room of device: '{0}'", config.Key);
+
+ success = true;
+ }
+
+ ResetTimer();
+
+ return success;
+ }
+
+ ///
+ /// Resets (or starts) the write timer
+ ///
+ static void ResetTimer()
+ {
+ if (WriteTimer == null)
+ WriteTimer = new CTimer(WriteConfigFile, WriteTimeout);
+
+ WriteTimer.Reset(WriteTimeout);
+
+ Debug.LogMessage(LogEventLevel.Debug, "Config File write timer has been reset.");
+ }
+
+ ///
+ /// Writes the current config to a file in the LocalConfig subfolder
+ ///
+ private static void WriteConfigFile(object o)
+ {
+ var filePath = Global.FilePathPrefix + LocalConfigFolder + Global.DirectorySeparator + "configurationFile.json";
+
+ var configData = JsonConvert.SerializeObject(ConfigReader.ConfigObject);
+
+ WriteFile(filePath, configData);
+ }
+
+ ///
+ /// Writes the current config data to a file
+ ///
+ /// The file path to write to
+ /// The config data to write
+ public static void WriteFile(string filePath, string configData)
+ {
+ if (WriteTimer != null)
+ WriteTimer.Stop();
+
+ Debug.LogMessage(LogEventLevel.Information, "Writing Configuration to file");
+
+ Debug.LogMessage(LogEventLevel.Information, "Attempting to write config file: '{0}'", filePath);
+
+ try
+ {
+ if (fileLock.TryEnter())
{
- sw.Write(configData);
- sw.Flush();
+ using (StreamWriter sw = new StreamWriter(filePath))
+ {
+ sw.Write(configData);
+ sw.Flush();
+ }
+ }
+ else
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Unable to enter FileLock");
}
}
- else
+ catch (Exception e)
{
- Debug.LogMessage(LogEventLevel.Information, "Unable to enter FileLock");
+ Debug.LogMessage(LogEventLevel.Information, "Error: Config write failed: \r{0}", e);
+ }
+ finally
+ {
+ if (fileLock != null && !fileLock.Disposed)
+ fileLock.Leave();
+
}
}
- catch (Exception e)
- {
- Debug.LogMessage(LogEventLevel.Information, "Error: Config write failed: \r{0}", e);
- }
- finally
- {
- if (lockAcquired)
- Monitor.Exit(_fileLock);
- }
+
+
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs b/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs
index d9a5918f..44275ef0 100644
--- a/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs
+++ b/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs
@@ -9,139 +9,197 @@ using Newtonsoft.Json;
using PepperDash.Core;
-namespace PepperDash.Essentials.Core.Config;
-
-///
-/// Loads the ConfigObject from the file
-///
-public class EssentialsConfig : BasicConfig
+namespace PepperDash.Essentials.Core.Config
{
- [JsonProperty("system_url")]
- public string SystemUrl { get; set; }
-
- [JsonProperty("template_url")]
- public string TemplateUrl { get; set; }
-
///
- /// Gets the SystemUuid extracted from the SystemUrl
+ /// Loads the ConfigObject from the file
///
- [JsonProperty("systemUuid")]
- public string SystemUuid
+ public class EssentialsConfig : BasicConfig
{
- get
- {
- if (string.IsNullOrEmpty(SystemUrl))
- return "missing url";
+ ///
+ /// Gets or sets the SystemUrl
+ ///
+ [JsonProperty("system_url")]
+ public string SystemUrl { get; set; }
- if (SystemUrl.Contains("#"))
+ ///
+ /// Gets or sets the TemplateUrl
+ ///
+ [JsonProperty("template_url")]
+ public string TemplateUrl { get; set; }
+
+ ///
+ /// Gets the SystemUuid extracted from the SystemUrl
+ ///
+ [JsonProperty("systemUuid")]
+ public string SystemUuid
+ {
+ get
{
- var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*");
- string uuid = result.Groups[1].Value;
+ string uuid;
+
+ if (string.IsNullOrEmpty(SystemUrl))
+ {
+ uuid = "missing url";
+ }
+ else if (SystemUrl.Contains("#"))
+ {
+ var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*");
+ uuid = result.Groups[1].Value;
+ }
+ else if (SystemUrl.Contains("detail"))
+ {
+ var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/detail\/(.*)\/.*");
+ uuid = result.Groups[1].Value;
+ }
+ else
+ {
+ var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/.*");
+ uuid = result.Groups[1].Value;
+ }
+
return uuid;
}
- else
+ }
+
+ ///
+ /// Gets the TemplateUuid extracted from the TemplateUrl
+ ///
+ [JsonProperty("templateUuid")]
+ public string TemplateUuid
+ {
+ get
{
- var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/.*");
- string uuid = result.Groups[1].Value;
+ string uuid;
+
+ if (string.IsNullOrEmpty(TemplateUrl))
+ {
+ uuid = "missing template url";
+ }
+ else if (TemplateUrl.Contains("#"))
+ {
+ var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*");
+ uuid = result.Groups[1].Value;
+ }
+ else if (TemplateUrl.Contains("detail"))
+ {
+ var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/system-templates\/detail\/(.*)\/system-template-versions\/detail\/(.*)\/.*");
+ uuid = result.Groups[2].Value;
+ }
+ else
+ {
+ var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/system-templates\/(.*)\/system-template-versions\/(.*)\/.*");
+ uuid = result.Groups[2].Value;
+ }
+
return uuid;
}
}
+
+ ///
+ /// Gets or sets the Rooms
+ ///
+ [JsonProperty("rooms")]
+ public List Rooms { get; set; }
+
+ ///
+ /// Gets or sets the Versions
+ ///
+ [JsonProperty("versions")]
+ public VersionData Versions { get; set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public EssentialsConfig()
+ : base()
+ {
+ Rooms = new List();
+ }
}
///
- /// Gets the TemplateUuid extracted from the TemplateUrl
+ /// Represents version data for Essentials and its packages
///
- [JsonProperty("templateUuid")]
- public string TemplateUuid
+ public class VersionData
{
- get
- {
- if (string.IsNullOrEmpty(TemplateUrl))
- return "missing template url";
+ ///
+ /// Gets or sets the Essentials version
+ ///
+ [JsonProperty("essentials")]
+ public NugetVersion Essentials { get; set; }
- if (TemplateUrl.Contains("#"))
- {
- var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*");
- string uuid = result.Groups[1].Value;
- return uuid;
- }
- else
- {
- var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/system-templates\/(.*)\/system-template-versions\/(.*)\/.*");
- string uuid = result.Groups[2].Value;
- return uuid;
- }
+ ///
+ /// Gets or sets the list of Packages
+ ///
+ [JsonProperty("packages")]
+ public List Packages { get; set; }
+
+ ///
+ /// Gets or sets the touchpanel wrapper app version
+ ///
+ [JsonProperty("touchpanelWrapperApp")]
+ public NugetVersion TouchpanelWrapperApp { get; set; }
+
+ ///
+ /// Gets or sets the list of user interface packages
+ ///
+ [JsonProperty("userInterfaces")]
+ public List UserInterfaces { get; set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public VersionData()
+ {
+ Packages = new List();
+ UserInterfaces = new List();
}
}
- [JsonProperty("rooms")]
- public List Rooms { get; set; }
-
///
- /// Gets or sets the Versions
+ /// Represents a NugetVersion
///
- public VersionData Versions { get; set; }
-
- public EssentialsConfig()
- : base()
+ public class NugetVersion
{
- Rooms = new List();
+ ///
+ /// Gets or sets the Version
+ ///
+ [JsonProperty("version")]
+ public string Version { get; set; }
+
+ ///
+ /// Gets or sets the PackageId
+ ///
+ [JsonProperty("packageId", NullValueHandling = NullValueHandling.Ignore)]
+ public string PackageId { get; set; }
+
+ ///
+ /// Gets or sets the RepoUrl
+ ///
+ [JsonProperty("repoUrl", NullValueHandling = NullValueHandling.Ignore)]
+ public string RepoUrl { get; set; }
+
+ ///
+ /// Gets or sets the human-readable name
+ ///
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; set; }
}
-}
-
-///
-/// Represents version data for Essentials and its packages
-///
-public class VersionData
-{
- ///
- /// Gets or sets the Essentials version
- ///
- [JsonProperty("essentials")]
- public NugetVersion Essentials { get; set; }
///
- /// Gets or sets the list of Packages
+ /// Represents a SystemTemplateConfigs
///
- [JsonProperty("packages")]
- public List Packages { get; set; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- public VersionData()
+ public class SystemTemplateConfigs
{
- Packages = new List();
+ ///
+ /// Gets or sets the System
+ ///
+ public EssentialsConfig System { get; set; }
+
+ ///
+ /// Gets or sets the Template
+ ///
+ public EssentialsConfig Template { get; set; }
}
-}
-
-///
-/// Represents a NugetVersion
-///
-public class NugetVersion
-{
- ///
- /// Gets or sets the Version
- ///
- [JsonProperty("version")]
- public string Version { get; set; }
-
- ///
- /// Gets or sets the PackageId
- ///
- [JsonProperty("packageId")]
- public string PackageId { get; set; }
-}
-
-///
-///
-///
-public class SystemTemplateConfigs
-{
- public EssentialsConfig System { get; set; }
-
- ///
- /// Gets or sets the Template
- ///
- public EssentialsConfig Template { get; set; }
-}
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Config/ILoadConfig.cs b/src/PepperDash.Essentials.Core/Config/ILoadConfig.cs
index 8c093176..ed6ff61f 100644
--- a/src/PepperDash.Essentials.Core/Config/ILoadConfig.cs
+++ b/src/PepperDash.Essentials.Core/Config/ILoadConfig.cs
@@ -4,15 +4,16 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace PepperDash.Essentials.Core;
-
-///
-/// Defines the contract for loading configuration. This is called after all plugins have been loaded, but before any devices are initialized. This allows for any necessary configuration merging or processing to be done before devices are created and initialized.
-///
-public interface ILoadConfig
+namespace PepperDash.Essentials.Core
{
///
- /// Executes the loading of the configuration. This is called after all plugins have been loaded, but before any devices are initialized. This allows for any necessary configuration merging or processing to be done before devices are created and initialized.
+ /// Defines the contract for ILoadConfig
///
- void GoWithLoad();
+ public interface ILoadConfig
+ {
+ ///
+ /// GoWithLoad method
+ ///
+ void GoWithLoad();
+ }
}
diff --git a/src/PepperDash.Essentials.Core/Config/InfoConfig.cs b/src/PepperDash.Essentials.Core/Config/InfoConfig.cs
index 7c2f81e4..1ce2c60b 100644
--- a/src/PepperDash.Essentials.Core/Config/InfoConfig.cs
+++ b/src/PepperDash.Essentials.Core/Config/InfoConfig.cs
@@ -5,12 +5,12 @@ using Newtonsoft.Json;
using System;
using System.Collections.Generic;
-namespace PepperDash.Essentials.Core.Config;
-
-///
-/// Represents the info section of a Config file
-///
-public class InfoConfig
+namespace PepperDash.Essentials.Core.Config
+{
+ ///
+ /// Represents the info section of a Config file
+ ///
+ public class InfoConfig
{
///
/// Gets or sets the Name
@@ -70,49 +70,49 @@ public class InfoConfig
Type = "";
Version = "";
Comment = "";
- HostName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0);
- AppNumber = InitialParametersClass.ApplicationNumber;
+ HostName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0);
+ AppNumber = InitialParametersClass.ApplicationNumber;
- RuntimeInfo = new RuntimeInfo();
+ RuntimeInfo = new RuntimeInfo();
}
}
-
-///
-/// Represents runtime information about the program/processor
-///
-public class RuntimeInfo
-{
+
///
- /// The name of the running application
+ /// Represents a RuntimeInfo
///
- [JsonProperty("appName")]
- public string AppName {get; set;}
- //{
- // get
- // {
- // return Assembly.GetExecutingAssembly().GetName().Name;
- // }
- //}
+ public class RuntimeInfo
+ {
+ ///
+ /// The name of the running application
+ ///
+ [JsonProperty("appName")]
+ public string AppName {get; set;}
+ //{
+ // get
+ // {
+ // return Assembly.GetExecutingAssembly().GetName().Name;
+ // }
+ //}
- ///
- /// The Assembly version of the running application
- ///
- [JsonProperty("assemblyVersion")]
- public string AssemblyVersion {get; set;}
- //{
- // get
- // {
- // var version = Assembly.GetExecutingAssembly().GetName().Version;
- // return string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
- // }
- //}
+ ///
+ /// The Assembly version of the running application
+ ///
+ [JsonProperty("assemblyVersion")]
+ public string AssemblyVersion {get; set;}
+ //{
+ // get
+ // {
+ // var version = Assembly.GetExecutingAssembly().GetName().Version;
+ // return string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
+ // }
+ //}
- /// ,
- /// The OS Version of the processor (Firmware Version)
- ///
- [JsonProperty("osVersion")]
- public string OsVersion {get; set;}
+ /// ,
+ /// The OS Version of the processor (Firmware Version)
+ ///
+ [JsonProperty("osVersion")]
+ public string OsVersion {get; set;}
//{
// get
// {
@@ -120,15 +120,17 @@ public class RuntimeInfo
// }
//}
- ///
- /// The information gathered by the processor at runtime about it's NICs and their IP addresses.
- ///
- [JsonProperty("ipInfo")]
- public Dictionary IpInfo
- {
- get
+ ///
+ /// The information gathered by the processor at runtime about it's NICs and their IP addresses.
+ ///
+ [JsonProperty("ipInfo")]
+ public Dictionary IpInfo
{
- return Global.EthernetAdapterInfoCollection;
+ get
+ {
+ return Global.EthernetAdapterInfoCollection;
+ }
}
}
+
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Config/SourceDevicePropertiesConfigBase.cs b/src/PepperDash.Essentials.Core/Config/SourceDevicePropertiesConfigBase.cs
index ae30de0a..42a576a2 100644
--- a/src/PepperDash.Essentials.Core/Config/SourceDevicePropertiesConfigBase.cs
+++ b/src/PepperDash.Essentials.Core/Config/SourceDevicePropertiesConfigBase.cs
@@ -4,12 +4,16 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Essentials.Core.Config;
-
-public class SourceDevicePropertiesConfigBase
+namespace PepperDash.Essentials.Core.Config
{
- ///
- /// Gets or sets the DisableSharing
- ///
- public bool DisableSharing { get; set; }
+ ///
+ /// Represents a SourceDevicePropertiesConfigBase
+ ///
+ public class SourceDevicePropertiesConfigBase
+ {
+ ///
+ /// Gets or sets the DisableSharing
+ ///
+ public bool DisableSharing { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Crestron/CrestronGenericBaseDevice.cs b/src/PepperDash.Essentials.Core/Crestron/CrestronGenericBaseDevice.cs
index a8547ba3..bdb3e461 100644
--- a/src/PepperDash.Essentials.Core/Crestron/CrestronGenericBaseDevice.cs
+++ b/src/PepperDash.Essentials.Core/Crestron/CrestronGenericBaseDevice.cs
@@ -7,8 +7,11 @@ using PepperDash.Core.JsonStandardObjects;
using PepperDash.Essentials.Core.Bridges;
using Serilog.Events;
-namespace PepperDash.Essentials.Core;
-
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Abstract base class for Crestron GenericBase devices
+ ///
public abstract class CrestronGenericBaseDevice : EssentialsDevice, IOnline, IHasFeedback, ICommunicationMonitor, IUsageTracking
{
///
@@ -16,10 +19,10 @@ namespace PepperDash.Essentials.Core;
///
protected GenericBase Hardware;
- ///
- /// Returns a list containing the Outputs that we want to expose.
- ///
- public FeedbackCollection Feedbacks { get; private set; }
+ ///
+ /// Gets or sets the Feedbacks
+ ///
+ public FeedbackCollection Feedbacks { get; private set; }
///
/// Gets or sets the IsOnline
@@ -48,25 +51,30 @@ namespace PepperDash.Essentials.Core;
/// name of the device
/// hardware instance
protected CrestronGenericBaseDevice(string key, string name, GenericBase hardware)
- : base(key, name)
- {
- Feedbacks = new FeedbackCollection();
+ : base(key, name)
+ {
+ Feedbacks = new FeedbackCollection();
- Hardware = hardware;
- IsOnline = new BoolFeedback("IsOnlineFeedback", () => Hardware.IsOnline);
- IsRegistered = new BoolFeedback("IsRegistered", () => Hardware.Registered);
- IpConnectionsText = new StringFeedback("IpConnectionsText", () => Hardware.ConnectedIpList != null ? string.Join(",", Hardware.ConnectedIpList.Select(cip => cip.DeviceIpAddress).ToArray()) : string.Empty);
- AddToFeedbackList(IsOnline, IpConnectionsText);
+ Hardware = hardware;
+ IsOnline = new BoolFeedback("IsOnlineFeedback", () => Hardware.IsOnline);
+ IsRegistered = new BoolFeedback("IsRegistered", () => Hardware.Registered);
+ IpConnectionsText = new StringFeedback("IpConnectionsText", () => Hardware.ConnectedIpList != null ? string.Join(",", Hardware.ConnectedIpList.Select(cip => cip.DeviceIpAddress).ToArray()) : string.Empty);
+ AddToFeedbackList(IsOnline, IpConnectionsText);
- CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, hardware, 120000, 300000);
- }
+ CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, hardware, 120000, 300000);
+ }
- protected CrestronGenericBaseDevice(string key, string name)
- : base(key, name)
- {
- Feedbacks = new FeedbackCollection();
+ ///
+ /// Constructor without hardware instance
+ ///
+ /// key of the device
+ /// name of the device
+ protected CrestronGenericBaseDevice(string key, string name)
+ : base(key, name)
+ {
+ Feedbacks = new FeedbackCollection();
- }
+ }
///
/// Registers the Crestron GenericBase hardware instance
@@ -74,58 +82,58 @@ namespace PepperDash.Essentials.Core;
/// hardware instance
protected void RegisterCrestronGenericBase(GenericBase hardware)
{
- Hardware = hardware;
- IsOnline = new BoolFeedback("IsOnlineFeedback", () => Hardware.IsOnline);
- IsRegistered = new BoolFeedback("IsRegistered", () => Hardware.Registered);
- IpConnectionsText = new StringFeedback("IpConnectionsText", () => Hardware.ConnectedIpList != null ? string.Join(",", Hardware.ConnectedIpList.Select(cip => cip.DeviceIpAddress).ToArray()) : string.Empty);
- AddToFeedbackList(IsOnline, IpConnectionsText);
+ Hardware = hardware;
+ IsOnline = new BoolFeedback("IsOnlineFeedback", () => Hardware.IsOnline);
+ IsRegistered = new BoolFeedback("IsRegistered", () => Hardware.Registered);
+ IpConnectionsText = new StringFeedback("IpConnectionsText", () => Hardware.ConnectedIpList != null ? string.Join(",", Hardware.ConnectedIpList.Select(cip => cip.DeviceIpAddress).ToArray()) : string.Empty);
+ AddToFeedbackList(IsOnline, IpConnectionsText);
- CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, hardware, 120000, 300000);
+ CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, hardware, 120000, 300000);
}
///
/// CustomActivate method
///
///
- protected override bool CustomActivate()
+ public override bool CustomActivate()
{
- Debug.LogMessage(LogEventLevel.Information, this, "Activating");
- if (!PreventRegistration)
- {
- //Debug.LogMessage(LogEventLevel.Debug, this, " Does not require registration. Skipping");
-
- if (Hardware.Registerable && !Hardware.Registered)
+ Debug.LogMessage(LogEventLevel.Information, this, "Activating");
+ if (!PreventRegistration)
{
- var response = Hardware.RegisterWithLogging(Key);
- if (response != eDeviceRegistrationUnRegistrationResponse.Success)
+ //Debug.LogMessage(LogEventLevel.Debug, this, " Does not require registration. Skipping");
+
+ if (Hardware.Registerable && !Hardware.Registered)
{
- //Debug.LogMessage(LogEventLevel.Information, this, "ERROR: Cannot register Crestron device: {0}", response);
- return false;
+ var response = Hardware.RegisterWithLogging(Key);
+ if (response != eDeviceRegistrationUnRegistrationResponse.Success)
+ {
+ //Debug.LogMessage(LogEventLevel.Information, this, "ERROR: Cannot register Crestron device: {0}", response);
+ return false;
+ }
}
+
+ IsRegistered.FireUpdate();
+ }
+ else
+ {
+ AddPostActivationAction(() =>
+ {
+ if (Hardware.Registerable && !Hardware.Registered)
+ {
+ var response = Hardware.RegisterWithLogging(Key);
+ }
+
+ IsRegistered.FireUpdate();
+ });
}
- IsRegistered.FireUpdate();
- }
- else
- {
- AddPostActivationAction(() =>
- {
- if (Hardware.Registerable && !Hardware.Registered)
- {
- var response = Hardware.RegisterWithLogging(Key);
- }
+ foreach (var f in Feedbacks)
+ {
+ f.FireUpdate();
+ }
- IsRegistered.FireUpdate();
- });
- }
-
- foreach (var f in Feedbacks)
- {
- f.FireUpdate();
- }
-
- Hardware.OnlineStatusChange += Hardware_OnlineStatusChange;
- CommunicationMonitor.Start();
+ Hardware.OnlineStatusChange += Hardware_OnlineStatusChange;
+ CommunicationMonitor.Start();
return base.CustomActivate();
}
@@ -141,42 +149,42 @@ namespace PepperDash.Essentials.Core;
var success = Hardware.UnRegister() == eDeviceRegistrationUnRegistrationResponse.Success;
- IsRegistered.FireUpdate();
+ IsRegistered.FireUpdate();
- return success;
+ return success;
}
///
- /// Adds feedback(s) to the list
- ///
- ///
- public void AddToFeedbackList(params Feedback[] newFbs)
- {
- foreach (var f in newFbs)
+ /// Adds feedback(s) to the list
+ ///
+ /// feedback(s) to be added to the list
+ public void AddToFeedbackList(params Feedback[] newFbs)
{
- if (f == null) continue;
-
- if (!Feedbacks.Contains(f))
+ foreach (var f in newFbs)
{
- Feedbacks.Add(f);
+ if (f == null) continue;
+
+ if (!Feedbacks.Contains(f))
+ {
+ Feedbacks.Add(f);
+ }
}
}
- }
void Hardware_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args)
{
- Debug.LogMessage(LogEventLevel.Verbose, this, "OnlineStatusChange Event. Online = {0}", args.DeviceOnLine);
+ Debug.LogMessage(LogEventLevel.Verbose, this, "OnlineStatusChange Event. Online = {0}", args.DeviceOnLine);
- if (!Hardware.Registered)
- {
- return; // protects in cases where device has been unregistered and feedbacks would attempt to access null sigs.
- }
+ if (!Hardware.Registered)
+ {
+ return; // protects in cases where device has been unregistered and feedbacks would attempt to access null sigs.
+ }
- foreach (var feedback in Feedbacks)
- {
- if (feedback != null)
- feedback.FireUpdate();
- }
+ foreach (var feedback in Feedbacks)
+ {
+ if (feedback != null)
+ feedback.FireUpdate();
+ }
}
#region IStatusMonitor Members
@@ -187,28 +195,52 @@ namespace PepperDash.Essentials.Core;
public StatusMonitorBase CommunicationMonitor { get; private set; }
#endregion
- #region IUsageTracking Members
+ #region IUsageTracking Members
- public UsageTracking UsageTracker { get; set; }
+ ///
+ /// Gets or sets the UsageTracker
+ ///
+ public UsageTracking UsageTracker { get; set; }
- #endregion
+ #endregion
}
-public abstract class CrestronGenericBridgeableBaseDevice : CrestronGenericBaseDevice, IBridgeAdvanced
-{
- protected CrestronGenericBridgeableBaseDevice(string key, string name, GenericBase hardware) : base(key, name, hardware)
+ ///
+ /// Abstract base class for Crestron GenericBase devices that are bridgeable
+ ///
+ public abstract class CrestronGenericBridgeableBaseDevice : CrestronGenericBaseDevice, IBridgeAdvanced
{
+
+ ///
+ /// Constructor
+ ///
+ /// key of the device
+ /// name of the device
+ /// hardware instance
+ protected CrestronGenericBridgeableBaseDevice(string key, string name, GenericBase hardware) : base(key, name, hardware)
+ {
+ }
+
+ ///
+ /// Constructor without hardware instance
+ ///
+ /// key of the device
+ /// name of the device
+ protected CrestronGenericBridgeableBaseDevice(string key, string name)
+ : base(key, name)
+ {
+ }
+
+ ///
+ /// Links to API
+ ///
+ /// the trilist
+ /// the starting join number
+ /// the join map key
+ /// the bridge instance
+ public abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge);
}
- protected CrestronGenericBridgeableBaseDevice(string key, string name)
- : base(key, name)
- {
- }
-
-
- public abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge);
-}
-
//***********************************************************************************
///
@@ -249,4 +281,5 @@ public abstract class CrestronGenericBridgeableBaseDevice : CrestronGenericBaseD
return result;
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Crestron/CrestronGenericBaseDevice.cs.orig b/src/PepperDash.Essentials.Core/Crestron/CrestronGenericBaseDevice.cs.orig
index 8dc38428..bef4ae01 100644
--- a/src/PepperDash.Essentials.Core/Crestron/CrestronGenericBaseDevice.cs.orig
+++ b/src/PepperDash.Essentials.Core/Crestron/CrestronGenericBaseDevice.cs.orig
@@ -36,7 +36,7 @@ namespace PepperDash.Essentials.Core
/// Make sure that overriding classes call this!
/// Registers the Crestron device, connects up to the base events, starts communication monitor
///
- protected override bool CustomActivate()
+ public override bool CustomActivate()
{
Debug.LogMessage(LogEventLevel.Information, this, "Activating");
var response = Hardware.RegisterWithLogging(Key);
diff --git a/src/PepperDash.Essentials.Core/CrestronIO/GenericRelayDevice.cs b/src/PepperDash.Essentials.Core/CrestronIO/GenericRelayDevice.cs
index 15dfcd36..a41e1411 100644
--- a/src/PepperDash.Essentials.Core/CrestronIO/GenericRelayDevice.cs
+++ b/src/PepperDash.Essentials.Core/CrestronIO/GenericRelayDevice.cs
@@ -13,209 +13,253 @@ using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config;
using Serilog.Events;
-namespace PepperDash.Essentials.Core.CrestronIO;
-
-///
-/// Represents a generic device controlled by relays
-///
-[Description("Wrapper class for a Relay")]
-public class GenericRelayDevice : EssentialsBridgeableDevice, ISwitchedOutput
+namespace PepperDash.Essentials.Core.CrestronIO
{
- public Relay RelayOutput { get; private set; }
-
- public BoolFeedback OutputIsOnFeedback { get; private set; }
-
- //Maintained for compatibility with PepperDash.Essentials.Core.Devices.CrestronProcessor
- public GenericRelayDevice(string key, Relay relay) :
- base(key)
+ ///
+ /// Represents a generic device controlled by relays
+ ///
+ [Description("Wrapper class for a Relay")]
+ public class GenericRelayDevice : EssentialsBridgeableDevice, ISwitchedOutput
{
- OutputIsOnFeedback = new BoolFeedback(new Func(() => RelayOutput.State));
+ ///
+ /// The RelayOutput controlled by this device
+ ///
+ public Relay RelayOutput { get; private set; }
- RelayOutput = relay;
- RelayOutput.Register();
+ ///
+ /// Feedback to indicate whether the output is on
+ ///
+ public BoolFeedback OutputIsOnFeedback { get; private set; }
- RelayOutput.StateChange += RelayOutput_StateChange;
- }
-
- public GenericRelayDevice(string key, string name, Func postActivationFunc,
- IOPortConfig config)
- : base(key, name)
- {
- OutputIsOnFeedback = new BoolFeedback(() => RelayOutput.State);
-
- AddPostActivationAction(() =>
+ //Maintained for compatibility with PepperDash.Essentials.Core.Devices.CrestronProcessor
+ ///
+ /// Constructor for GenericRelayDevice
+ ///
+ /// key of the device
+ /// Relay output controlled by this device
+ public GenericRelayDevice(string key, Relay relay) :
+ base(key)
{
- RelayOutput = postActivationFunc(config);
-
- if (RelayOutput == null)
- {
- Debug.LogMessage(LogEventLevel.Information, this, "Unable to get parent relay device for device key {0} and port {1}", config.PortDeviceKey, config.PortNumber);
- return;
- }
+ OutputIsOnFeedback = new BoolFeedback(new Func(() => RelayOutput.State));
+ RelayOutput = relay;
RelayOutput.Register();
RelayOutput.StateChange += RelayOutput_StateChange;
- });
- }
+ }
- #region PreActivate
-
- private static Relay GetRelay(IOPortConfig dc)
- {
-
- IRelayPorts relayDevice;
-
- if(dc.PortDeviceKey.Equals("processor"))
+ ///
+ /// Constructor for GenericRelayDevice
+ ///
+ /// key of the device
+ /// name of the device
+ /// function to get the relay output
+ /// IO port configuration
+ public GenericRelayDevice(string key, string name, Func postActivationFunc,
+ IOPortConfig config)
+ : base(key, name)
{
- if (!Global.ControlSystem.SupportsRelay)
+ OutputIsOnFeedback = new BoolFeedback(() => RelayOutput.State);
+
+ AddPostActivationAction(() =>
{
- Debug.LogMessage(LogEventLevel.Information, "Processor does not support relays");
+ RelayOutput = postActivationFunc(config);
+
+ if (RelayOutput == null)
+ {
+ Debug.LogMessage(LogEventLevel.Information, this, "Unable to get parent relay device for device key {0} and port {1}", config.PortDeviceKey, config.PortNumber);
+ return;
+ }
+
+ RelayOutput.Register();
+
+ RelayOutput.StateChange += RelayOutput_StateChange;
+ });
+ }
+
+ #region PreActivate
+
+ private static Relay GetRelay(IOPortConfig dc)
+ {
+
+ IRelayPorts relayDevice;
+
+ if(dc.PortDeviceKey.Equals("processor"))
+ {
+ if (!Global.ControlSystem.SupportsRelay)
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Processor does not support relays");
+ return null;
+ }
+ relayDevice = Global.ControlSystem;
+
+ return relayDevice.RelayPorts[dc.PortNumber];
+ }
+
+ var essentialsDevice = DeviceManager.GetDeviceForKey(dc.PortDeviceKey);
+ if (essentialsDevice == null)
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Device {0} was not found in Device Manager. Check configuration or for errors with device.", dc.PortDeviceKey);
return null;
}
- relayDevice = Global.ControlSystem;
- return relayDevice.RelayPorts[dc.PortNumber];
- }
-
- var essentialsDevice = DeviceManager.GetDeviceForKey(dc.PortDeviceKey);
- if (essentialsDevice == null)
- {
- Debug.LogMessage(LogEventLevel.Information, "Device {0} was not found in Device Manager. Check configuration or for errors with device.", dc.PortDeviceKey);
+ relayDevice = essentialsDevice as IRelayPorts;
+
+ if (relayDevice == null)
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Device {0} is not a valid relay parent. Please check configuration.", dc.PortDeviceKey);
+ return null;
+ }
+
+ if (dc.PortNumber <= relayDevice.NumberOfRelayPorts)
+ {
+ return relayDevice.RelayPorts[dc.PortNumber];
+ }
+
+ Debug.LogMessage(LogEventLevel.Information, "Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber);
return null;
}
- relayDevice = essentialsDevice as IRelayPorts;
-
- if (relayDevice == null)
+ #endregion
+
+ #region Events
+
+ void RelayOutput_StateChange(Relay relay, RelayEventArgs args)
{
- Debug.LogMessage(LogEventLevel.Information, "Device {0} is not a valid relay parent. Please check configuration.", dc.PortDeviceKey);
- return null;
+ OutputIsOnFeedback.FireUpdate();
}
- if (dc.PortNumber <= relayDevice.NumberOfRelayPorts)
+ #endregion
+
+ #region Methods
+
+ ///
+ /// OpenRelay method
+ ///
+ public void OpenRelay()
{
- return relayDevice.RelayPorts[dc.PortNumber];
+ RelayOutput.State = false;
}
- Debug.LogMessage(LogEventLevel.Information, "Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber);
- return null;
- }
-
- #endregion
-
- #region Events
-
- void RelayOutput_StateChange(Relay relay, RelayEventArgs args)
- {
- OutputIsOnFeedback.FireUpdate();
- }
-
- #endregion
-
- #region Methods
-
- public void OpenRelay()
- {
- RelayOutput.State = false;
- }
-
- public void CloseRelay()
- {
- RelayOutput.State = true;
- }
-
- public void ToggleRelayState()
- {
- if (RelayOutput.State == true)
- OpenRelay();
- else
- CloseRelay();
- }
-
- #endregion
-
- #region ISwitchedOutput Members
-
- void ISwitchedOutput.On()
- {
- CloseRelay();
- }
-
- void ISwitchedOutput.Off()
- {
- OpenRelay();
- }
-
- #endregion
-
- #region Bridge Linking
-
- public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
- {
- var joinMap = new GenericRelayControllerJoinMap(joinStart);
-
- var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);
-
- if (!string.IsNullOrEmpty(joinMapSerialized))
- joinMap = JsonConvert.DeserializeObject(joinMapSerialized);
-
- if (bridge != null)
+ ///
+ /// CloseRelay method
+ ///
+ public void CloseRelay()
{
- bridge.AddJoinMap(Key, joinMap);
- }
- else
- {
- Debug.LogMessage(LogEventLevel.Information, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
+ RelayOutput.State = true;
}
- if (RelayOutput == null)
+ ///
+ /// ToggleRelayState method
+ ///
+ public void ToggleRelayState()
{
- Debug.LogMessage(LogEventLevel.Debug, this, "Unable to link device '{0}'. Relay is null", Key);
- return;
- }
-
- Debug.LogMessage(LogEventLevel.Debug, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
-
- trilist.SetBoolSigAction(joinMap.Relay.JoinNumber, b =>
- {
- if (b)
- CloseRelay();
- else
+ if (RelayOutput.State == true)
OpenRelay();
- });
-
- // feedback for relay state
-
- OutputIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Relay.JoinNumber]);
- }
-
- #endregion
-
- #region Factory
-
- public class GenericRelayDeviceFactory : EssentialsDeviceFactory
- {
- public GenericRelayDeviceFactory()
- {
- TypeNames = new List() { "relayoutput" };
+ else
+ CloseRelay();
}
- public override EssentialsDevice BuildDevice(DeviceConfig dc)
+ #endregion
+
+ #region ISwitchedOutput Members
+
+ void ISwitchedOutput.On()
{
- Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Relay Device");
-
- var props = JsonConvert.DeserializeObject(dc.Properties.ToString());
-
- if (props == null) return null;
-
- var portDevice = new GenericRelayDevice(dc.Key, dc.Name, GetRelay, props);
-
- return portDevice;
+ CloseRelay();
}
- }
- #endregion
+ void ISwitchedOutput.Off()
+ {
+ OpenRelay();
+ }
+
+ #endregion
+
+ #region Bridge Linking
+
+ ///
+ /// LinkToApi method
+ ///
+ ///
+ public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
+ {
+ var joinMap = new GenericRelayControllerJoinMap(joinStart);
+
+ var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);
+
+ if (!string.IsNullOrEmpty(joinMapSerialized))
+ joinMap = JsonConvert.DeserializeObject(joinMapSerialized);
+
+ if (bridge != null)
+ {
+ bridge.AddJoinMap(Key, joinMap);
+ }
+ else
+ {
+ Debug.LogMessage(LogEventLevel.Information, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
+ }
+
+ if (RelayOutput == null)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, this, "Unable to link device '{0}'. Relay is null", Key);
+ return;
+ }
+
+ Debug.LogMessage(LogEventLevel.Debug, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
+
+ trilist.SetBoolSigAction(joinMap.Relay.JoinNumber, b =>
+ {
+ if (b)
+ CloseRelay();
+ else
+ OpenRelay();
+ });
+
+ // feedback for relay state
+
+ OutputIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Relay.JoinNumber]);
+ }
+
+ #endregion
+
+ #region Factory
+
+ ///
+ /// Represents a GenericRelayDeviceFactory
+ ///
+ public class GenericRelayDeviceFactory : EssentialsDeviceFactory
+ {
+ ///
+ /// Constructor for GenericRelayDeviceFactory
+ ///
+ public GenericRelayDeviceFactory()
+ {
+ TypeNames = new List() { "relayoutput" };
+ }
+
+ ///
+ /// BuildDevice method
+ ///
+ ///
+ public override EssentialsDevice BuildDevice(DeviceConfig dc)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Relay Device");
+
+ var props = JsonConvert.DeserializeObject(dc.Properties.ToString());
+
+ if (props == null) return null;
+
+ var portDevice = new GenericRelayDevice(dc.Key, dc.Name, GetRelay, props);
+
+ return portDevice;
+ }
+ }
+
+ #endregion
+
+
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportAnalogInputDevice.cs b/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportAnalogInputDevice.cs
index d9deb961..ad5507d0 100644
--- a/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportAnalogInputDevice.cs
+++ b/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportAnalogInputDevice.cs
@@ -1,4 +1,6 @@
-using System;
+
+
+using System;
using System.Collections.Generic;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
diff --git a/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportInputDevice.cs b/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportInputDevice.cs
index 2088e952..47e8aef6 100644
--- a/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportInputDevice.cs
+++ b/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportInputDevice.cs
@@ -1,4 +1,6 @@
-using System;
+
+
+using System;
using System.Collections.Generic;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
diff --git a/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportOutputDevice.cs b/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportOutputDevice.cs
index 39c26cd6..ee3f0320 100644
--- a/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportOutputDevice.cs
+++ b/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportOutputDevice.cs
@@ -11,176 +11,173 @@ using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config;
using Serilog.Events;
-namespace PepperDash.Essentials.Core.CrestronIO;
-
-///
-/// Represents a generic digital input deviced tied to a versiport
-///
-public class GenericVersiportDigitalOutputDevice : EssentialsBridgeableDevice, IDigitalOutput
+namespace PepperDash.Essentials.Core.CrestronIO
{
- public Versiport OutputPort { get; private set; }
-
- public BoolFeedback OutputStateFeedback { get; private set; }
-
- Func OutputStateFeedbackFunc
+ ///
+ /// Represents a generic digital input deviced tied to a versiport
+ ///
+ public class GenericVersiportDigitalOutputDevice : EssentialsBridgeableDevice, IDigitalOutput, IHasFeedback
{
- get
+ private Versiport outputPort;
+
+ ///
+ /// Gets or sets the OutputStateFeedback
+ ///
+ public BoolFeedback OutputStateFeedback { get; private set; }
+
+ ///
+ public FeedbackCollection Feedbacks { get; private set; } = new FeedbackCollection();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GenericVersiportDigitalOutputDevice(string key, string name, Func postActivationFunc, IOPortConfig config) :
+ base(key, name)
{
- return () => OutputPort.DigitalOut;
- }
- }
+ OutputStateFeedback = new BoolFeedback("outputState", () => outputPort.DigitalOut);
- public GenericVersiportDigitalOutputDevice(string key, string name, Func postActivationFunc, IOPortConfig config) :
- base(key, name)
- {
- OutputStateFeedback = new BoolFeedback(OutputStateFeedbackFunc);
-
- AddPostActivationAction(() =>
- {
- OutputPort = postActivationFunc(config);
-
- OutputPort.Register();
-
-
- if (!OutputPort.SupportsDigitalOutput)
+ AddPostActivationAction(() =>
{
- Debug.LogMessage(LogEventLevel.Information, this, "Device does not support configuration as a Digital Output");
+ outputPort = postActivationFunc(config);
+
+ outputPort.Register();
+
+
+ if (!outputPort.SupportsDigitalOutput)
+ {
+ this.LogError("Device does not support configuration as a Digital Output");
+ return;
+ }
+
+ outputPort.SetVersiportConfiguration(eVersiportConfiguration.DigitalOutput);
+
+
+ outputPort.VersiportChange += OutputPort_VersiportChange;
+
+ });
+ }
+
+ void OutputPort_VersiportChange(Versiport port, VersiportEventArgs args)
+ {
+ this.LogDebug("Versiport change: {event}", args.Event);
+
+ if (args.Event == eVersiportEvent.DigitalOutChange)
+ OutputStateFeedback.FireUpdate();
+ }
+
+ ///
+ /// Set value of the versiport digital output
+ ///
+ /// value to set the output to
+ public void SetOutput(bool state)
+ {
+ if (!outputPort.SupportsDigitalOutput)
+ {
+ this.LogError("Versiport does not support Digital Output Mode");
return;
}
- OutputPort.SetVersiportConfiguration(eVersiportConfiguration.DigitalOutput);
+ outputPort.DigitalOut = state;
+ }
+ #region Bridge Linking
- OutputPort.VersiportChange += OutputPort_VersiportChange;
+ ///
+ /// LinkToApi method
+ ///
+ ///
+ public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
+ {
+ var joinMap = new IDigitalOutputJoinMap(joinStart);
- });
+ var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);
- }
+ if (!string.IsNullOrEmpty(joinMapSerialized))
+ joinMap = JsonConvert.DeserializeObject(joinMapSerialized);
- void OutputPort_VersiportChange(Versiport port, VersiportEventArgs args)
- {
- Debug.LogMessage(LogEventLevel.Debug, this, "Versiport change: {0}", args.Event);
-
- if(args.Event == eVersiportEvent.DigitalOutChange)
- OutputStateFeedback.FireUpdate();
- }
-
- ///
- /// Set value of the versiport digital output
- ///
- /// value to set the output to
- public void SetOutput(bool state)
- {
- if (OutputPort.SupportsDigitalOutput)
+ if (bridge != null)
{
- Debug.LogMessage(LogEventLevel.Information, this, "Passed the Check");
-
- OutputPort.DigitalOut = state;
-
+ bridge.AddJoinMap(Key, joinMap);
}
else
{
- Debug.LogMessage(LogEventLevel.Information, this, "Versiport does not support Digital Output Mode");
+ this.LogWarning("Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
}
- }
+ try
+ {
+ this.LogDebug("Linking to Trilist '{0}'", trilist.ID.ToString("X"));
- #region Bridge Linking
-
- public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
- {
- var joinMap = new IDigitalOutputJoinMap(joinStart);
-
- var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);
-
- if (!string.IsNullOrEmpty(joinMapSerialized))
- joinMap = JsonConvert.DeserializeObject(joinMapSerialized);
-
- if (bridge != null)
- {
- bridge.AddJoinMap(Key, joinMap);
- }
- else
- {
- Debug.LogMessage(LogEventLevel.Information, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
+ // Link feedback for input state
+ OutputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.OutputState.JoinNumber]);
+ trilist.SetBoolSigAction(joinMap.OutputState.JoinNumber, SetOutput);
+ }
+ catch (Exception e)
+ {
+ this.LogError("Unable to link device: {message}", e.Message);
+ this.LogDebug(e, "Stack Trace: ");
+ }
}
- try
+ #endregion
+
+
+ ///
+ /// GetVersiportDigitalOutput method
+ ///
+ public static Versiport GetVersiportDigitalOutput(IOPortConfig dc)
{
- Debug.LogMessage(LogEventLevel.Debug, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
-
- // Link feedback for input state
- OutputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.OutputState.JoinNumber]);
- trilist.SetBoolSigAction(joinMap.OutputState.JoinNumber, SetOutput);
- }
- catch (Exception e)
- {
- Debug.LogMessage(LogEventLevel.Debug, this, "Unable to link device '{0}'. Input is null", Key);
- Debug.LogMessage(LogEventLevel.Debug, this, "Error: {0}", e);
- }
- }
-
- #endregion
-
-
- public static Versiport GetVersiportDigitalOutput(IOPortConfig dc)
- {
-
- IIOPorts ioPortDevice;
-
if (dc.PortDeviceKey.Equals("processor"))
{
if (!Global.ControlSystem.SupportsVersiport)
{
- Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalOuptut: Processor does not support Versiports");
+ Debug.LogError("GetVersiportDigitalOutput: Processor does not support Versiports");
return null;
}
- ioPortDevice = Global.ControlSystem;
+ return Global.ControlSystem.VersiPorts[dc.PortNumber];
}
- else
+
+ if (!(DeviceManager.GetDeviceForKey(dc.PortDeviceKey) is IIOPorts ioPortDevice))
{
- var ioPortDev = DeviceManager.GetDeviceForKey(dc.PortDeviceKey) as IIOPorts;
- if (ioPortDev == null)
- {
- Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalOuptut: Device {0} is not a valid device", dc.PortDeviceKey);
- return null;
- }
- ioPortDevice = ioPortDev;
- }
- if (ioPortDevice == null)
- {
- Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalOuptut: Device '0' is not a valid IOPorts Device", dc.PortDeviceKey);
+ Debug.LogError("GetVersiportDigitalOutput: Device {key} is not a valid device", dc.PortDeviceKey);
return null;
}
if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts)
{
- Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalOuptut: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber);
+ Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalOutput: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber);
+ return null;
}
- var port = ioPortDevice.VersiPorts[dc.PortNumber];
- return port;
-
- }
-}
-
-
-public class GenericVersiportDigitalOutputDeviceFactory : EssentialsDeviceFactory
-{
- public GenericVersiportDigitalOutputDeviceFactory()
- {
- TypeNames = new List() { "versiportoutput" };
+ return ioPortDevice.VersiPorts[dc.PortNumber];
+ }
}
- public override EssentialsDevice BuildDevice(DeviceConfig dc)
+
+ ///
+ /// Represents a GenericVersiportDigitalOutputDeviceFactory
+ ///
+ public class GenericVersiportDigitalOutputDeviceFactory : EssentialsDeviceFactory
{
- Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Versiport Device");
+ ///
+ /// Initialize a new instance of the class.
+ ///
+ public GenericVersiportDigitalOutputDeviceFactory()
+ {
+ TypeNames = new List() { "versiportoutput" };
+ }
- var props = JsonConvert.DeserializeObject(dc.Properties.ToString());
+ ///
+ public override EssentialsDevice BuildDevice(DeviceConfig dc)
+ {
+ Debug.LogDebug("Factory Attempting to create new Generic Versiport Device");
- if (props == null) return null;
+ var props = JsonConvert.DeserializeObject(dc.Properties.ToString());
- var portDevice = new GenericVersiportDigitalOutputDevice(dc.Key, dc.Name, GenericVersiportDigitalOutputDevice.GetVersiportDigitalOutput, props);
+ if (props == null) return null;
- return portDevice;
+ var portDevice = new GenericVersiportDigitalOutputDevice(dc.Key, dc.Name, GenericVersiportDigitalOutputDevice.GetVersiportDigitalOutput, props);
+
+ return portDevice;
+ }
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/CrestronIO/IAnalogInput.cs b/src/PepperDash.Essentials.Core/CrestronIO/IAnalogInput.cs
index c42e273c..426834ed 100644
--- a/src/PepperDash.Essentials.Core/CrestronIO/IAnalogInput.cs
+++ b/src/PepperDash.Essentials.Core/CrestronIO/IAnalogInput.cs
@@ -5,9 +5,7 @@ using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
-namespace PepperDash.Essentials.Core.CrestronIO;
-
-public interface IAnalogInput
+namespace PepperDash.Essentials.Core.CrestronIO
{
///
/// Defines the contract for IAnalogInput
diff --git a/src/PepperDash.Essentials.Core/CrestronIO/IDigitalInput.cs b/src/PepperDash.Essentials.Core/CrestronIO/IDigitalInput.cs
index 963fa8b1..8e1b4c18 100644
--- a/src/PepperDash.Essentials.Core/CrestronIO/IDigitalInput.cs
+++ b/src/PepperDash.Essentials.Core/CrestronIO/IDigitalInput.cs
@@ -1,14 +1,19 @@
-
-namespace PepperDash.Essentials.Core.CrestronIO;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
-
-///
-/// Represents a device that provides digital input
-///
-public interface IDigitalInput
+namespace PepperDash.Essentials.Core.CrestronIO
{
///
- /// Feedback to indicate the state of the input
+ /// Represents a device that provides digital input
///
- BoolFeedback InputStateFeedback { get; }
-}
+ public interface IDigitalInput
+ {
+ ///
+ /// Feedback to indicate the state of the input
+ ///
+ BoolFeedback InputStateFeedback { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/CrestronIO/IDigitalOutput.cs b/src/PepperDash.Essentials.Core/CrestronIO/IDigitalOutput.cs
index 7c4f720a..82aa0686 100644
--- a/src/PepperDash.Essentials.Core/CrestronIO/IDigitalOutput.cs
+++ b/src/PepperDash.Essentials.Core/CrestronIO/IDigitalOutput.cs
@@ -4,12 +4,7 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Essentials.Core.CrestronIO;
-
-///
-/// Represents a device that provides digital input
-///
-public interface IDigitalOutput
+namespace PepperDash.Essentials.Core.CrestronIO
{
///
/// Represents a device that provides digital input
diff --git a/src/PepperDash.Essentials.Core/CrestronIO/IHasCresnetBranches.cs b/src/PepperDash.Essentials.Core/CrestronIO/IHasCresnetBranches.cs
index b882f50e..ee86b5fe 100644
--- a/src/PepperDash.Essentials.Core/CrestronIO/IHasCresnetBranches.cs
+++ b/src/PepperDash.Essentials.Core/CrestronIO/IHasCresnetBranches.cs
@@ -6,12 +6,7 @@ using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
-namespace PepperDash.Essentials.Core;
-
-///
-/// Defines the contract for a class that has Cresnet branches
-///
-public interface IHasCresnetBranches
+namespace PepperDash.Essentials.Core
{
///
/// Defines the contract for IHasCresnetBranches
diff --git a/src/PepperDash.Essentials.Core/CrestronIO/IOPortConfig.cs b/src/PepperDash.Essentials.Core/CrestronIO/IOPortConfig.cs
index f89b3b0f..7b76958b 100644
--- a/src/PepperDash.Essentials.Core/CrestronIO/IOPortConfig.cs
+++ b/src/PepperDash.Essentials.Core/CrestronIO/IOPortConfig.cs
@@ -1,4 +1,5 @@
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/PepperDash.Essentials.Core/CrestronIO/ISwitchedOutput.cs b/src/PepperDash.Essentials.Core/CrestronIO/ISwitchedOutput.cs
index 9f5bec7b..30b4b8f9 100644
--- a/src/PepperDash.Essentials.Core/CrestronIO/ISwitchedOutput.cs
+++ b/src/PepperDash.Essentials.Core/CrestronIO/ISwitchedOutput.cs
@@ -6,36 +6,37 @@ using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
-namespace PepperDash.Essentials.Core.CrestronIO;
-
-///
-/// Describes an output capable of switching on and off
-///
-public interface ISwitchedOutput
+namespace PepperDash.Essentials.Core.CrestronIO
{
///
- /// Feedback to indicate the state of the output
+ /// Describes an output capable of switching on and off
///
- BoolFeedback OutputIsOnFeedback {get;}
+ public interface ISwitchedOutput
+ {
+ ///
+ /// Feedback to indicate whether the output is on
+ ///
+ BoolFeedback OutputIsOnFeedback {get;}
+
+ ///
+ /// Turns the output on
+ ///
+ void On();
+
+ ///
+ /// Turns the output off
+ ///
+ void Off();
+ }
///
- /// Turns the output on
+ /// Describes a collection of switched outputs
///
- void On();
-
- ///
- /// Turns the output off
- ///
- void Off();
-}
-
-///
-/// Defines the contract for a class that has a collection of switched outputs
-///
-public interface ISwitchedOutputCollection
-{
- ///
- /// Collection of switched outputs, indexed by their output number
- ///
- Dictionary SwitchedOutputs { get; }
+ public interface ISwitchedOutputCollection
+ {
+ ///
+ /// Dictionary of switched outputs by their port number
+ ///
+ Dictionary SwitchedOutputs { get; }
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Device Info/DeviceInfo.cs b/src/PepperDash.Essentials.Core/Device Info/DeviceInfo.cs
index abd011ef..6e55c9b3 100644
--- a/src/PepperDash.Essentials.Core/Device Info/DeviceInfo.cs
+++ b/src/PepperDash.Essentials.Core/Device Info/DeviceInfo.cs
@@ -1,28 +1,29 @@
-namespace PepperDash.Essentials.Core.DeviceInfo;
-
-///
-/// Represents a DeviceInfo
-///
-public class DeviceInfo
+namespace PepperDash.Essentials.Core.DeviceInfo
{
///
- /// Gets or sets the HostName
+ /// Represents a DeviceInfo
///
- public string HostName { get; set; }
- ///
- /// Gets or sets the IpAddress
- ///
- public string IpAddress { get; set; }
- ///
- /// Gets or sets the MacAddress
- ///
- public string MacAddress { get; set; }
- ///
- /// Gets or sets the SerialNumber
- ///
- public string SerialNumber { get; set; }
- ///
- /// Gets or sets the FirmwareVersion
- ///
- public string FirmwareVersion { get; set; }
-}
+ public class DeviceInfo
+ {
+ ///
+ /// Gets or sets the HostName
+ ///
+ public string HostName { get; set; }
+ ///
+ /// Gets or sets the IpAddress
+ ///
+ public string IpAddress { get; set; }
+ ///
+ /// Gets or sets the MacAddress
+ ///
+ public string MacAddress { get; set; }
+ ///
+ /// Gets or sets the SerialNumber
+ ///
+ public string SerialNumber { get; set; }
+ ///
+ /// Gets or sets the FirmwareVersion
+ ///
+ public string FirmwareVersion { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Device Info/DeviceInfoEventArgs.cs b/src/PepperDash.Essentials.Core/Device Info/DeviceInfoEventArgs.cs
index df4ea69d..266b0d0b 100644
--- a/src/PepperDash.Essentials.Core/Device Info/DeviceInfoEventArgs.cs
+++ b/src/PepperDash.Essentials.Core/Device Info/DeviceInfoEventArgs.cs
@@ -1,32 +1,32 @@
using System;
-namespace PepperDash.Essentials.Core.DeviceInfo;
-
-
-///
-/// Represents a DeviceInfoEventArgs
-///
-public class DeviceInfoEventArgs : EventArgs
+namespace PepperDash.Essentials.Core.DeviceInfo
{
///
- /// Gets or sets the DeviceInfo
+ /// Represents a DeviceInfoEventArgs
///
- public DeviceInfo DeviceInfo { get; set; }
-
- ///
- /// Constructor
- ///
- public DeviceInfoEventArgs()
+ public class DeviceInfoEventArgs:EventArgs
{
+ ///
+ /// Gets or sets the DeviceInfo
+ ///
+ public DeviceInfo DeviceInfo { get; set; }
- }
+ ///
+ /// Constructor
+ ///
+ public DeviceInfoEventArgs()
+ {
+
+ }
- ///
- /// Constructor with DeviceInfo
- ///
- /// the DeviceInfo instance
- public DeviceInfoEventArgs(DeviceInfo devInfo)
- {
- DeviceInfo = devInfo;
+ ///
+ /// Constructor with DeviceInfo
+ ///
+ /// the DeviceInfo instance
+ public DeviceInfoEventArgs(DeviceInfo devInfo)
+ {
+ DeviceInfo = devInfo;
+ }
}
-}
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Device Info/IDeviceInfoProvider.cs b/src/PepperDash.Essentials.Core/Device Info/IDeviceInfoProvider.cs
index d4dc0852..3f5a4735 100644
--- a/src/PepperDash.Essentials.Core/Device Info/IDeviceInfoProvider.cs
+++ b/src/PepperDash.Essentials.Core/Device Info/IDeviceInfoProvider.cs
@@ -1,31 +1,31 @@
using System;
using PepperDash.Core;
-namespace PepperDash.Essentials.Core.DeviceInfo;
-
-///
-/// Defines the contract for IDeviceInfoProvider
-///
-public interface IDeviceInfoProvider : IKeyed
+namespace PepperDash.Essentials.Core.DeviceInfo
{
///
- /// Gets the DeviceInfo
+ /// Defines the contract for IDeviceInfoProvider
///
- DeviceInfo DeviceInfo { get; }
+ public interface IDeviceInfoProvider:IKeyed
+ {
+ ///
+ /// Gets the DeviceInfo
+ ///
+ DeviceInfo DeviceInfo { get; }
+
+ ///
+ /// Event fired when DeviceInfo changes
+ ///
+ event DeviceInfoChangeHandler DeviceInfoChanged;
+
+ ///
+ /// Updates the DeviceInfo
+ ///
+ void UpdateDeviceInfo();
+ }
///
- /// Event fired when DeviceInfo changes
+ /// Delegate for DeviceInfoChangeHandler
///
- event DeviceInfoChangeHandler DeviceInfoChanged;
-
- ///
- /// Updates the DeviceInfo
- ///
- void UpdateDeviceInfo();
-}
-
-///
-/// Delegate for DeviceInfoChangeHandler
-///
-public delegate void DeviceInfoChangeHandler(IKeyed device, DeviceInfoEventArgs args);
-
+ public delegate void DeviceInfoChangeHandler(IKeyed device, DeviceInfoEventArgs args);
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Device Info/NetworkDeviceHelpers.cs b/src/PepperDash.Essentials.Core/Device Info/NetworkDeviceHelpers.cs
index 34557159..79eaa018 100644
--- a/src/PepperDash.Essentials.Core/Device Info/NetworkDeviceHelpers.cs
+++ b/src/PepperDash.Essentials.Core/Device Info/NetworkDeviceHelpers.cs
@@ -3,46 +3,46 @@ using System.Collections.Generic;
using System.Linq;
using PepperDash.Core;
using Crestron.SimplSharp;
+using PepperDash.Essentials.Core;
using Serilog.Events;
-namespace PepperDash.Essentials.Core.DeviceInfo;
-
-///
-/// Helper methods for network devices
-///
-public static class NetworkDeviceHelpers
+namespace PepperDash.Essentials.Core.DeviceInfo
{
///
- /// Event raised when ArpTable changes
+ /// Static class NetworkDeviceHelpers
///
- public static event ArpTableEventHandler ArpTableUpdated;
-
- ///
- /// Delegate called by ArpTableUpdated
- ///
- /// contains the entire ARP table and a bool to note if there was an error in retrieving the data
- public delegate void ArpTableEventHandler(ArpTableEventArgs args);
-
- private static readonly char NewLineSplitter = CrestronEnvironment.NewLine.ToCharArray().First();
- private static readonly string NewLine = CrestronEnvironment.NewLine;
-
- private static readonly object _lock = new();
-
- ///
- /// Last resolved ARP table - it is recommended to refresh the arp before using this.
- ///
- public static List ArpTable { get; private set; }
-
- ///
- /// Force recheck of ARP table
- ///
- public static void RefreshArp()
+ public static class NetworkDeviceHelpers
{
- var error = false;
- lock (_lock)
+ ///
+ /// Event raised when ArpTable changes
+ ///
+ public static event ArpTableEventHandler ArpTableUpdated;
+
+ ///
+ /// Delegate called by ArpTableUpdated
+ ///
+ /// contains the entire ARP table and a bool to note if there was an error in retrieving the data
+ public delegate void ArpTableEventHandler(ArpTableEventArgs args);
+
+ private static readonly char NewLineSplitter = CrestronEnvironment.NewLine.ToCharArray().First();
+ private static readonly string NewLine = CrestronEnvironment.NewLine;
+
+ private static readonly CCriticalSection Lock = new CCriticalSection();
+
+ ///
+ /// Gets or sets the ArpTable
+ ///
+ public static List ArpTable { get; private set; }
+
+ ///
+ /// RefreshArp method
+ ///
+ public static void RefreshArp()
{
+ var error = false;
try
{
+ Lock.Enter();
var consoleResponse = string.Empty;
if (!CrestronConsole.SendControlSystemCommand("showarptable", ref consoleResponse)) return;
if (string.IsNullOrEmpty(consoleResponse))
@@ -75,152 +75,164 @@ public static class NetworkDeviceHelpers
Debug.LogMessage(LogEventLevel.Information, "Exception in \"RefreshArp\" : {0}", ex.Message);
error = true;
}
- } // end lock
-
- OnArpTableUpdated(new ArpTableEventArgs(ArpTable, error));
- }
+ finally
+ {
+ Lock.Leave();
+ OnArpTableUpdated(new ArpTableEventArgs(ArpTable, error));
+ }
+ }
- private static void OnArpTableUpdated(ArpTableEventArgs args)
- {
- if (args == null) return;
- var handler = ArpTableUpdated;
- if (handler == null) return;
- handler.Invoke(args);
- }
+ private static void OnArpTableUpdated(ArpTableEventArgs args)
+ {
+ if (args == null) return;
+ var handler = ArpTableUpdated;
+ if (handler == null) return;
+ handler.Invoke(args);
+ }
+
+ static NetworkDeviceHelpers()
+ {
+ ArpTable = new List();
+ }
+
+ ///
+ /// Removes leading zeros, leading whitespace, and trailing whitespace from an IPAddress string
+ ///
+ /// Ip Address to Santitize
+ /// Sanitized Ip Address
+ ///
+ /// SanitizeIpAddress method
+ ///
+ public static string SanitizeIpAddress(string ipAddressIn)
+ {
+ try
+ {
+ var ipAddress = IPAddress.Parse(ipAddressIn.TrimStart('0'));
+ return ipAddress.ToString();
+ }
+ catch (Exception ex)
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Unable to Santize Ip : {0}", ex.Message);
+ return ipAddressIn;
+ }
+ }
+
+ ///
+ /// Resolves a hostname by IP Address using DNS
+ ///
+ /// IP Address to resolve from
+ /// Resolved Hostname - on failure to determine hostname, will return IP Address
+ ///
+ /// ResolveHostnameFromIp method
+ ///
+ public static string ResolveHostnameFromIp(string ipAddress)
+ {
+ try
+ {
+ var santitizedIp = SanitizeIpAddress(ipAddress);
+ var hostEntry = Dns.GetHostEntry(santitizedIp);
+ return hostEntry == null ? ipAddress : hostEntry.HostName;
+ }
+ catch (Exception ex)
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Exception Resolving Hostname from IP Address : {0}", ex.Message);
+ return ipAddress;
+ }
+ }
+
+ ///
+ /// Resolves an IP Address by hostname using DNS
+ ///
+ /// Hostname to resolve from
+ /// Resolved IP Address - on a failure to determine IP Address, will return hostname
+ ///
+ /// ResolveIpFromHostname method
+ ///
+ public static string ResolveIpFromHostname(string hostName)
+ {
+ try
+ {
+ var hostEntry = Dns.GetHostEntry(hostName);
+ return hostEntry == null ? hostName : hostEntry.AddressList.First().ToString();
+ }
+ catch (Exception ex)
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Exception Resolving IP Address from Hostname : {0}", ex.Message);
+ return hostName;
+ }
+ }
- static NetworkDeviceHelpers()
- {
- ArpTable = new List();
}
///
- /// Removes leading zeros, leading whitespace, and trailing whitespace from an IPAddress string
+ /// Represents a ArpEntry
///
- /// Ip Address to Santitize
- /// Sanitized Ip Address
- public static string SanitizeIpAddress(string ipAddressIn)
+ public class ArpEntry
{
- try
+ ///
+ /// The IP Address of the ARP Entry
+ ///
+ public readonly IPAddress IpAddress;
+
+ ///
+ /// The MAC Address of the ARP Entry
+ ///
+ public readonly string MacAddress;
+
+ ///
+ /// Constructs new ArpEntry object
+ ///
+ /// string formatted as ipv4 address
+ /// mac address string - format is unimportant
+ public ArpEntry(string ipAddress, string macAddress)
{
- var ipAddress = IPAddress.Parse(ipAddressIn.TrimStart('0'));
- return ipAddress.ToString();
- }
- catch (Exception ex)
- {
- Debug.LogMessage(LogEventLevel.Information, "Unable to Santize Ip : {0}", ex.Message);
- return ipAddressIn;
+ if (string.IsNullOrEmpty(ipAddress))
+ {
+ throw new ArgumentException("\"ipAddress\" cannot be null or empty");
+ }
+ if (string.IsNullOrEmpty(macAddress))
+ {
+ throw new ArgumentException("\"macAddress\" cannot be null or empty");
+ }
+ IpAddress = IPAddress.Parse(ipAddress.TrimStart().TrimStart('0').TrimEnd());
+ MacAddress = macAddress;
}
}
///
- /// Resolves a hostname by IP Address using DNS
+ /// Represents a ArpTableEventArgs
///
- /// IP Address to resolve from
- /// Resolved Hostname - on failure to determine hostname, will return IP Address
- public static string ResolveHostnameFromIp(string ipAddress)
+ public class ArpTableEventArgs : EventArgs
{
- try
+ ///
+ /// The retrieved ARP Table
+ ///
+ public readonly List ArpTable;
+ ///
+ /// True if there was a problem retrieving the ARP Table
+ ///
+ public readonly bool Error;
+
+ ///
+ /// Constructor for ArpTableEventArgs
+ ///
+ /// The entirety of the retrieved ARP table
+ /// True of an error was encountered updating the ARP table
+ public ArpTableEventArgs(List arpTable, bool error)
{
- var santitizedIp = SanitizeIpAddress(ipAddress);
- var hostEntry = Dns.GetHostEntry(santitizedIp);
- return hostEntry == null ? ipAddress : hostEntry.HostName;
+ ArpTable = arpTable;
+ Error = error;
}
- catch (Exception ex)
+
+ ///
+ /// Constructor for ArpTableEventArgs - assumes no error encountered in retrieving ARP Table
+ ///
+ /// The entirety of the retrieved ARP table
+ public ArpTableEventArgs(List arpTable)
{
- Debug.LogMessage(LogEventLevel.Information, "Exception Resolving Hostname from IP Address : {0}", ex.Message);
- return ipAddress;
+ ArpTable = arpTable;
+ Error = false;
}
}
-
- ///
- /// Resolves an IP Address by hostname using DNS
- ///
- /// Hostname to resolve from
- /// Resolved IP Address - on a failure to determine IP Address, will return hostname
- public static string ResolveIpFromHostname(string hostName)
- {
- try
- {
- var hostEntry = Dns.GetHostEntry(hostName);
- return hostEntry == null ? hostName : hostEntry.AddressList.First().ToString();
- }
- catch (Exception ex)
- {
- Debug.LogMessage(LogEventLevel.Information, "Exception Resolving IP Address from Hostname : {0}", ex.Message);
- return hostName;
- }
- }
-
-}
-
-///
-/// Object to hold data about an arp entry
-///
-public class ArpEntry
-{
- ///
- /// IP Address of the ARP entry
- ///
- public readonly IPAddress IpAddress;
-
- ///
- /// MAC Address of the ARP entry
- ///
- public readonly string MacAddress;
-
- ///
- /// Constructs new ArpEntry object
- ///
- /// string formatted as ipv4 address
- /// mac address string - format is unimportant
- public ArpEntry(string ipAddress, string macAddress)
- {
- if (string.IsNullOrEmpty(ipAddress))
- {
- throw new ArgumentException("\"ipAddress\" cannot be null or empty");
- }
- if (string.IsNullOrEmpty(macAddress))
- {
- throw new ArgumentException("\"macAddress\" cannot be null or empty");
- }
- IpAddress = IPAddress.Parse(ipAddress.TrimStart().TrimStart('0').TrimEnd());
- MacAddress = macAddress;
- }
-}
-
-///
-/// Arguments passed by the ArpTableUpdated event
-///
-public class ArpTableEventArgs : EventArgs
-{
- ///
- /// The retrieved ARP Table
- ///
- public readonly List ArpTable;
- ///
- /// True if there was a problem retrieving the ARP Table
- ///
- public readonly bool Error;
-
- ///
- /// Constructor for ArpTableEventArgs
- ///
- /// The entirety of the retrieved ARP table
- /// True of an error was encountered updating the ARP table
- public ArpTableEventArgs(List arpTable, bool error)
- {
- ArpTable = arpTable;
- Error = error;
- }
-
- ///
- /// Constructor for ArpTableEventArgs - assumes no error encountered in retrieving ARP Table
- ///
- /// The entirety of the retrieved ARP table
- public ArpTableEventArgs(List arpTable)
- {
- ArpTable = arpTable;
- Error = false;
- }
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IChannel.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IChannel.cs
index 3dda43e4..c8c54006 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IChannel.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IChannel.cs
@@ -4,8 +4,8 @@ using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.SmartObjects;
-namespace PepperDash.Essentials.Core;
-
+namespace PepperDash.Essentials.Core
+{
///
/// Defines the contract for IChannel
///
@@ -79,4 +79,5 @@ namespace PepperDash.Essentials.Core;
triList.ClearBoolSigAction(129);
triList.ClearBoolSigAction(134);
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IColorFunctions.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IColorFunctions.cs
index 51f8e44a..69acc8f6 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IColorFunctions.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IColorFunctions.cs
@@ -4,8 +4,8 @@ using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.SmartObjects;
-namespace PepperDash.Essentials.Core;
-
+namespace PepperDash.Essentials.Core
+{
///
/// IColor interface
///
@@ -66,4 +66,5 @@ namespace PepperDash.Essentials.Core;
triList.ClearBoolSigAction(157);
triList.ClearBoolSigAction(158);
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDPad.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDPad.cs
index 9e26cd5a..ec9362b8 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDPad.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDPad.cs
@@ -4,8 +4,8 @@ using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.SmartObjects;
-namespace PepperDash.Essentials.Core;
-
+namespace PepperDash.Essentials.Core
+{
///
///
///
@@ -86,4 +86,5 @@ namespace PepperDash.Essentials.Core;
triList.ClearBoolSigAction(130);
triList.ClearBoolSigAction(134);
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDiscPlayerControls.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDiscPlayerControls.cs
index b3d944e6..5deee690 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDiscPlayerControls.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDiscPlayerControls.cs
@@ -3,11 +3,14 @@
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.SmartObjects;
-namespace PepperDash.Essentials.Core;
+namespace PepperDash.Essentials.Core
+{
///
/// Defines the contract for IDiscPlayerControls
///
public interface IDiscPlayerControls : IColor, IDPad, INumericKeypad, IHasPowerControl, ITransport, IUiDisplayInfo
{
- }
\ No newline at end of file
+ }
+
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDisplay.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDisplay.cs
index 3f38d4e4..af3428f6 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDisplay.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDisplay.cs
@@ -1,14 +1,15 @@
using PepperDash.Core;
-namespace PepperDash.Essentials.Core.DeviceTypeInterfaces;
-
-///
-/// Interface for display devices that can be controlled and monitored.
-/// This interface combines functionality for feedback, routing, power control,
-/// warming/cooling, usage tracking, and key name management.
-/// It is designed to be implemented by devices that require these capabilities,
-/// such as projectors, displays, and other visual output devices.
-///
-public interface IDisplay : IHasFeedback, IRoutingSinkWithSwitching, IHasPowerControl, IWarmingCooling, IUsageTracking, IKeyName
+namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
+ ///
+ /// Interface for display devices that can be controlled and monitored.
+ /// This interface combines functionality for feedback, routing, power control,
+ /// warming/cooling, usage tracking, and key name management.
+ /// It is designed to be implemented by devices that require these capabilities,
+ /// such as projectors, displays, and other visual output devices.
+ ///
+ public interface IDisplay : IHasFeedback, IRoutingSinkWithSwitching, IHasPowerControl, IWarmingCooling, IUsageTracking, IKeyName
+ {
+ }
}
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDisplayBasic.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDisplayBasic.cs
new file mode 100644
index 00000000..b76d3145
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDisplayBasic.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Essentials.Core.Devices.DeviceTypeInterfaces
+{
+ ///
+ /// Defines the contract for IDisplayBasic
+ ///
+ public interface IDisplayBasic
+ {
+ ///
+ /// Sets the input to HDMI 1
+ ///
+ void InputHdmi1();
+
+ ///
+ /// Sets the input to HDMI 2
+ ///
+ void InputHdmi2();
+
+ ///
+ /// Sets the input to HDMI 3
+ ///
+ void InputHdmi3();
+
+ ///
+ /// Sets the input to HDMI 4
+ ///
+ void InputHdmi4();
+
+ ///
+ /// Sets the input to DisplayPort 1
+ ///
+ void InputDisplayPort1();
+
+ ///
+ /// Sets the input to DVI 1
+ ///
+ void InputDvi1();
+
+ ///
+ /// Sets the input to Video 1
+ ///
+ void InputVideo1();
+
+ ///
+ /// Sets the input to VGA 1
+ ///
+ void InputVga1();
+
+ ///
+ /// Sets the input to VGA 2
+ ///
+ void InputVga2();
+
+ ///
+ /// Sets the input to RGB 1
+ ///
+ void InputRgb1();
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDumbSource.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDumbSource.cs
index ba7f1a43..2f419498 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDumbSource.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDumbSource.cs
@@ -4,11 +4,12 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
-namespace PepperDash.Essentials.Core;
-
-///
-/// Defines the contract for IDumbSource
-///
-public interface IDumbSource
+namespace PepperDash.Essentials.Core
{
+ ///
+ /// Defines the contract for IDumbSource
+ ///
+ public interface IDumbSource
+ {
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDvr.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDvr.cs
index 4397db50..527e8d46 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDvr.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IDvr.cs
@@ -9,8 +9,8 @@ using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.SmartObjects;
-namespace PepperDash.Essentials.Core;
-
+namespace PepperDash.Essentials.Core
+{
///
///
///
@@ -55,4 +55,5 @@ namespace PepperDash.Essentials.Core;
triList.ClearBoolSigAction(136);
triList.ClearBoolSigAction(152);
}
- }
\ No newline at end of file
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IEmergencyOSD.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IEmergencyOSD.cs
index 66d74c3e..efc068b0 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IEmergencyOSD.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IEmergencyOSD.cs
@@ -1,20 +1,25 @@
-namespace PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
-
-///
-/// Defines the contract for IEmergencyOSD
-///
-public interface IEmergencyOSD
+namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
///
- /// Shows an emergency message on the OSD
+ /// Defines the contract for IEmergencyOSD
///
- /// The URL of the emergency message to display
- void ShowEmergencyMessage(string url);
+ public interface IEmergencyOSD
+ {
+ ///
+ /// Shows an emergency message on the OSD
+ ///
+ /// The URL of the emergency message to display
+ void ShowEmergencyMessage(string url);
- ///
- /// Hides the emergency message from the OSD
- ///
- void HideEmergencyMessage();
+ ///
+ /// Hides the emergency message from the OSD
+ ///
+ void HideEmergencyMessage();
+ }
}
-
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasBranding.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasBranding.cs
index 75f04156..c05ef572 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasBranding.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasBranding.cs
@@ -1,18 +1,21 @@
-namespace PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using System;
-///
-/// Defines the contract for IHasBranding
-///
-public interface IHasBranding
+namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
///
- /// Gets whether branding is enabled
+ /// Defines the contract for IHasBranding
///
- bool BrandingEnabled { get; }
+ public interface IHasBranding
+ {
+ ///
+ /// Gets whether branding is enabled
+ ///
+ bool BrandingEnabled { get; }
- ///
- /// Initializes branding for the device
- ///
- /// The key identifying the room for branding purposes
- void InitializeBranding(string roomKey);
-}
+ ///
+ /// Initializes branding for the device
+ ///
+ /// The key identifying the room for branding purposes
+ void InitializeBranding(string roomKey);
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasFarEndContentStatus.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasFarEndContentStatus.cs
index 1595fa6e..5a8f66ee 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasFarEndContentStatus.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasFarEndContentStatus.cs
@@ -1,13 +1,13 @@
-namespace PepperDash.Essentials.Core.DeviceTypeInterfaces;
-
-
-///
-/// Defines the contract for IHasFarEndContentStatus
-///
-public interface IHasFarEndContentStatus
+namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
///
- /// Gets whether far end content is being received
+ /// Defines the contract for IHasFarEndContentStatus
///
- BoolFeedback ReceivingContent { get; }
-}
+ public interface IHasFarEndContentStatus
+ {
+ ///
+ /// Gets whether far end content is being received
+ ///
+ BoolFeedback ReceivingContent { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasInputs.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasInputs.cs
index d6fae39d..42e97b4b 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasInputs.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasInputs.cs
@@ -1,20 +1,19 @@
using PepperDash.Core;
-namespace PepperDash.Essentials.Core.DeviceTypeInterfaces;
-
-
-///
-/// Describes a device that has selectable inputs
-///
-/// the type to use as the key for each input item. Most likely an enum or string\
-///
-/// See MockDisplay for example implemntation
-///
-public interface IHasInputs : IKeyName
+namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
///
- /// Gets the collection of inputs
+ /// Describes a device that has selectable inputs
///
- ISelectableItems Inputs { get; }
+ /// the type to use as the key for each input item. Most likely an enum or string\
+ ///
+ /// See MockDisplay for example implemntation
+ ///
+ public interface IHasInputs : IKeyName
+ {
+ ///
+ /// Gets the collection of inputs
+ ///
+ ISelectableItems Inputs { get; }
+ }
}
-
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasPhoneDialing.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasPhoneDialing.cs
index 170b058d..b884092b 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasPhoneDialing.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasPhoneDialing.cs
@@ -1,40 +1,43 @@
-namespace PepperDash.Essentials.Core.DeviceTypeInterfaces;
+using System;
+using PepperDash.Essentials.Core;
-
-///
-/// Defines the contract for IHasPhoneDialing
-///
-public interface IHasPhoneDialing
+namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
///
- /// Feedback that indicates whether the phone is off-hook
+ /// Defines the contract for IHasPhoneDialing
///
- BoolFeedback PhoneOffHookFeedback { get; }
+ public interface IHasPhoneDialing
+ {
+ ///
+ /// Feedback that indicates whether the phone is off-hook
+ ///
+ BoolFeedback PhoneOffHookFeedback { get; }
- ///
- /// Feedback that provides the caller ID name
- ///
- StringFeedback CallerIdNameFeedback { get; }
+ ///
+ /// Feedback that provides the caller ID name
+ ///
+ StringFeedback CallerIdNameFeedback { get; }
- ///
- /// Feedback that provides the caller ID number
- ///
- StringFeedback CallerIdNumberFeedback { get; }
+ ///
+ /// Feedback that provides the caller ID number
+ ///
+ StringFeedback CallerIdNumberFeedback { get; }
- ///
- /// Dials a phone call to the specified number
- ///
- /// the number to dial
- void DialPhoneCall(string number);
+ ///
+ /// Dials a phone call to the specified number
+ ///
+ /// the number to dial
+ void DialPhoneCall(string number);
- ///
- /// Ends the current phone call
- ///
- void EndPhoneCall();
+ ///
+ /// Ends the current phone call
+ ///
+ void EndPhoneCall();
- ///
- /// Sends a DTMF digit to the phone
- ///
- /// the DTMF digit to send
- void SendDtmfToPhone(string digit);
-}
+ ///
+ /// Sends a DTMF digit to the phone
+ ///
+ /// the DTMF digit to send
+ void SendDtmfToPhone(string digit);
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasScreensWithLayouts.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasScreensWithLayouts.cs
index 2a67e39b..439462c6 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasScreensWithLayouts.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasScreensWithLayouts.cs
@@ -5,102 +5,103 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace PepperDash.Essentials.Core.DeviceTypeInterfaces;
-
-///
-/// This defines a device that has screens with layouts
-/// Simply decorative
-///
-public interface IHasScreensWithLayouts
+namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
///
- /// A dictionary of screens, keyed by screen ID, that contains information about each screen and its layouts.
+ /// This defines a device that has screens with layouts
+ /// Simply decorative
///
- Dictionary Screens { get; }
+ public interface IHasScreensWithLayouts
+ {
+ ///
+ /// A dictionary of screens, keyed by screen ID, that contains information about each screen and its layouts.
+ ///
+ Dictionary Screens { get; }
+
+ ///
+ /// Applies a specific layout to a screen based on the provided screen ID and layout index.
+ ///
+ ///
+ ///
+ void ApplyLayout(uint screenId, uint layoutIndex);
+ }
///
- /// Applies a specific layout to a screen based on the provided screen ID and layout index.
+ /// Represents information about a screen and its layouts.
///
- ///
- ///
- void ApplyLayout(uint screenId, uint layoutIndex);
-}
-
-///
-/// Represents information about a screen and its layouts.
-///
-public class ScreenInfo
-{
-
- ///
- /// Indicates whether the screen is enabled or not.
- ///
- [JsonProperty("enabled")]
- public bool Enabled { get; set; }
-
- ///
- /// The name of the screen.
- ///
- [JsonProperty("name")]
- public string Name { get; set; }
-
- ///
- /// The index of the screen.
- ///
- [JsonProperty("screenIndex")]
- public int ScreenIndex { get; set; }
-
- ///
- /// A dictionary of layout information for the screen, keyed by layout ID.
- ///
- [JsonProperty("layouts")]
- public Dictionary Layouts { get; set; }
-}
-
-///
-/// Represents information about a layout on a screen.
-///
-public class LayoutInfo
-{
- ///
- /// The name of the layout.
- ///
- [JsonProperty("layoutName")]
- public string LayoutName { get; set; }
-
- ///
- /// The index of the layout.
- ///
- [JsonProperty("layoutIndex")]
- public int LayoutIndex { get; set; }
-
- ///
- /// The type of the layout, which can be "single", "double", "triple", or "quad".
- ///
- [JsonProperty("layoutType")]
- public string LayoutType { get; set; }
-
- ///
- /// A dictionary of window configurations for the layout, keyed by window ID.
- ///
- [JsonProperty("windows")]
- public Dictionary Windows { get; set; }
-}
-
-///
-/// Represents the configuration of a window within a layout on a screen.
-///
-public class WindowConfig
-{
- ///
- /// The display label for the window
- ///
- [JsonProperty("label")]
- public string Label { get; set; }
-
- ///
- /// The input for the window
- ///
- [JsonProperty("input")]
- public string Input { get; set; }
+ public class ScreenInfo
+ {
+
+ ///
+ /// Indicates whether the screen is enabled or not.
+ ///
+ [JsonProperty("enabled")]
+ public bool Enabled { get; set; }
+
+ ///
+ /// The name of the screen.
+ ///
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ ///
+ /// The index of the screen.
+ ///
+ [JsonProperty("screenIndex")]
+ public int ScreenIndex { get; set; }
+
+ ///
+ /// A dictionary of layout information for the screen, keyed by layout ID.
+ ///
+ [JsonProperty("layouts")]
+ public Dictionary Layouts { get; set; }
+ }
+
+ ///
+ /// Represents information about a layout on a screen.
+ ///
+ public class LayoutInfo
+ {
+ ///
+ /// The name of the layout.
+ ///
+ [JsonProperty("layoutName")]
+ public string LayoutName { get; set; }
+
+ ///
+ /// The index of the layout.
+ ///
+ [JsonProperty("layoutIndex")]
+ public int LayoutIndex { get; set; }
+
+ ///
+ /// The type of the layout, which can be "single", "double", "triple", or "quad".
+ ///
+ [JsonProperty("layoutType")]
+ public string LayoutType { get; set; }
+
+ ///
+ /// A dictionary of window configurations for the layout, keyed by window ID.
+ ///
+ [JsonProperty("windows")]
+ public Dictionary Windows { get; set; }
+ }
+
+ ///
+ /// Represents the configuration of a window within a layout on a screen.
+ ///
+ public class WindowConfig
+ {
+ ///
+ /// The display label for the window
+ ///
+ [JsonProperty("label")]
+ public string Label { get; set; }
+
+ ///
+ /// The input for the window
+ ///
+ [JsonProperty("input")]
+ public string Input { get; set; }
+ }
}
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasSurroundSoundModes.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasSurroundSoundModes.cs
index 2a50dd0f..45b24f8b 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasSurroundSoundModes.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasSurroundSoundModes.cs
@@ -1,4 +1,9 @@
using PepperDash.Core;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasWebView.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasWebView.cs
index d1ae55bd..ef766efd 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasWebView.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasWebView.cs
@@ -1,84 +1,386 @@
using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
-namespace PepperDash.Essentials.Core.DeviceTypeInterfaces;
-
-
-///
-/// Defines the contract for IHasWebView
-///
-public interface IHasWebView
+namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
- ///
- /// Indicates whether the webview is currently visible
- ///
- bool WebviewIsVisible { get; }
+ ///
+ /// Defines the display mode for a webview event, with expected values of "Fullscreen", "Modal", or "Unknown".
+ ///
+ public enum eWebViewEventMode
+ {
+ ///
+ /// The display mode for the webview event is unknown or not specified. This value can be used as a default or fallback when the display mode is not provided or cannot be parsed into a known value.
+ ///
+ Unknown,
+
+ ///
+ /// The webview event should be displayed in fullscreen mode, covering the entire screen and typically used for immersive experiences or when maximum screen real estate is needed. When a webview event with this display mode is shown, it will typically trigger the WebViewStatusChanged event with a status of "Fullscreen", and when it is cleared/closed, it will trigger the WebViewStatusChanged event with a status of "Cleared".
+ ///
+ Fullscreen,
+
+ ///
+ /// The webview event should be displayed in modal mode, which typically means it will be shown as a dialog or overlay on top of the existing content, allowing the user to interact with it while still being able to see the underlying content. This display mode is often used for alerts, confirmations, or when the webview content is related to the current context but does not require full immersion. When a webview event with this display mode is shown, it will typically trigger the WebViewStatusChanged event with a status of "Modal", and when it is cleared/closed, it will trigger the WebViewStatusChanged event with a status of "Cleared".
+ ///
+ Modal,
+ }
+
+ ///
+ /// Defines the target for a webview event, with expected values of "OSD", "Controller", "PersistentWebApp", or "RoomScheduler".
+ ///
+
+ public enum eWebViewTarget
+ {
+ ///
+ /// The target for the webview event is unknown or not specified. This value can be used as a default or fallback when the target is not provided or cannot be parsed into a known value.
+ ///
+ Unknown,
+
+ ///
+ /// The webview event should be displayed on the On-Screen Display (OSD).
+ ///
+ OSD,
+
+ ///
+ /// The webview event should be displayed on the controller.
+ ///
+ Controller,
+
+ ///
+ /// The webview event should be displayed on the persistent web application.
+ ///
+ PersistentWebApp,
+
+ ///
+ /// The webview event should be displayed on the room scheduler.
+ ///
+ RoomScheduler
+ }
+
+ ///
+ /// Represents the reason for an error in a webview event, which can provide additional information about what went wrong. This class is typically only used in the Status property of a WebViewEvent when the status indicates an error, and may be null otherwise.
+ ///
+ public class Reason
+ {
+ ///
+ /// The reason for an error in a webview event as a string, which can provide additional information about what went wrong. This property is typically only populated in case of an error, and may be null otherwise.
+ ///
+ public string Value { get; set; }
+ }
+
+ ///
+ /// Represents the XPath of a webview event, which can provide information about where an error occurred in the webview. This class is typically only used in the Status property of a WebViewEvent when the status indicates an error, and may be null otherwise.
+ ///
+ public class XPath
+ {
+ ///
+ /// The XPath of a webview event as a string, which can provide information about where an error occurred in the webview. This property is typically only populated in case of an error, and may be null otherwise.
+ ///
+ public string Value { get; set; }
+ }
+
+ ///
+ /// Represents a base class for properties that have a string value and trigger an action when the value changes. This class can be used as a base for properties like DisplayMode and Target in the WebViewEvent, which have string values that can be set directly or parsed into enums for easier handling of expected values. The ValueChangedAction can be set to trigger any desired behavior when the value changes, such as updating the UI or triggering other events.
+ ///
+ public abstract class ValueProperty
+ {
+ ///
+ /// Triggered when Value is set
+ ///
+ public Action ValueChangedAction { get; set; }
+
+ ///
+ /// Triggers the ValueChangedAction if it is set. This method should be called whenever the Value property is set to ensure that any desired behavior associated with a change in value is executed.
+ ///
+ protected void OnValueChanged()
+ {
+ var a = ValueChangedAction;
+ if (a != null)
+ a();
+ }
+
+ }
+
+ ///
+ /// Represents a webview event, which can include information about the status of the webview, the display parameters for the webview, and any error information if applicable. This class can be used to represent both show and clear events for a webview, with the Status property indicating the current status of the webview (e.g., "Fullscreen", "Modal", "Cleared", "Error", or "Unknown"), the Display property providing details about how the webview is being displayed (e.g., mode, URL, target, title), and the Cleared property providing details about a cleared/closed webview event (e.g., target and ID). The Id property can be used to correlate show and clear events for the same webview instance.
+ ///
+ public class WebViewEvent
+ {
+ ///
+ /// The unique identifier for the webview event, which can be used to correlate show and clear events for the same webview instance. This property is typically included in both show and clear events for a webview, allowing you to track the lifecycle of a specific webview instance from when it is shown to when it is cleared/closed. The Id can be any string value, but it should be unique for each webview instance to ensure proper correlation between show and clear events.
+ ///
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ ///
+ /// The status of the webview event, which can indicate the current state of the webview (e.g., "Fullscreen", "Modal", "Cleared", "Error", or "Unknown") as well as any error information if applicable (XPath and Reason). The Value property can be used to get or set the current status of the webview, while the XPath and Reason properties can provide additional information in case of an error. The StatusString property can be used to get or set the raw status string from the event, but it is recommended to use the Value property for easier handling of expected values. Setting the Value property will trigger the ValueChangedAction if it is set, allowing you to respond to changes in the webview status as needed.
+ ///
+ [JsonProperty("status")]
+ public Status Status { get; set; } // /Event/UserInterface/WebView/Status
+
+ ///
+ /// The display parameters for the webview event, which can include the display mode (e.g., "Fullscreen", "Modal", or "Unknown"), the URL to display in the webview, the target for the webview (e.g., "OSD", "Controller", "PersistentWebApp", or "RoomScheduler"), and the title to display on the webview. This property is typically included in show events for a webview, providing details about how the webview is being displayed. When a webview event with these display parameters is shown, it will typically trigger the WebViewStatusChanged event with a status of "Fullscreen" or "Modal" depending on the specified display mode, and when it is cleared/closed, it will trigger the WebViewStatusChanged event with a status of "Cleared".
+ ///
+ [JsonProperty("display")]
+ public WebViewDisplay Display { get; set; } // /Event/UserInterface/WebView/Display
+
+ ///
+ /// The details for a cleared/closed webview event, which can include the target for the webview that was cleared (e.g., "OSD", "Controller", "PersistentWebApp", or "RoomScheduler") and the unique identifier for the webview event that was cleared. This property is typically included in clear events for a webview, providing details about which webview instance was cleared/closed. When a webview event with this property is cleared/closed, it will typically trigger the WebViewStatusChanged event with a status of "Cleared".
+ ///
+ [JsonProperty("cleared")]
+ public WebViewClear Cleared { get; set; } // /Event/UserInterface/WebView/Cleared
+ }
+
+ ///
+ /// Represents the display parameters for a webview event, which can include the display mode (e.g., "Fullscreen", "Modal", or "Unknown"), the URL to display in the webview, the target for the webview (e.g., "OSD", "Controller", "PersistentWebApp", or "RoomScheduler"), and the title to display on the webview. This class is typically used in the Display property of a WebViewEvent to provide details about how the webview is being displayed when a show event occurs. When a webview event with these display parameters is shown, it will typically trigger the WebViewStatusChanged event with a status of "Fullscreen" or "Modal" depending on the specified display mode, and when it is cleared/closed, it will trigger the WebViewStatusChanged event with a status of "Cleared".
+ ///
+ public class WebViewDisplay
+ {
+ ///
+ /// The display mode for the webview event. Expected values are "Fullscreen", "Modal", or "Unknown".
+ ///
+ [JsonProperty("mode")]
+ public DisplayMode Mode { get; set; }
+
+ ///
+ /// The URL to display in the webview.
+ ///
+ [JsonProperty("url")]
+ public string Url { get; set; }
+
+ ///
+ /// The target for the webview. Expected values are "OSD", "Controller", "PersistentWebApp", or "RoomScheduler".
+ ///
+ [JsonProperty("target")]
+ public Target Target { get; set; }
+
+ ///
+ /// The title to display on the webview.
+ ///
+ [JsonProperty("title")]
+ public string Title { get; set; }
+
+ ///
+ /// The unique identifier for the webview event, used to correlate show and clear events for the same webview instance.
+ ///
+ [JsonProperty("id")]
+ public string Id { get; set; }
+ }
+
+ ///
+ /// Represents the data for a webview cleared event, which indicates that a webview with the specified ID and target has been cleared/closed.
+ ///
+ public class WebViewClear
+ {
+ ///
+ /// The target for the webview that was cleared. Expected values are "OSD", "Controller", "PersistentWebApp", or "RoomScheduler".
+ ///
+ [JsonProperty("target")]
+ public Target Target { get; set; }
+
+ ///
+ /// The unique identifier for the webview event that was cleared, used to correlate show and clear events for the same webview instance.
+ ///
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ ///
+ /// The URL that was displayed in the webview that was cleared.
+ ///
+ [JsonProperty("url")]
+ public string Url { get; set; }
+ }
+
+ ///
+ /// Represents the display mode for a webview event, with a string value and a corresponding enum property for easier handling of expected values.
+ ///
+ public class DisplayMode : ValueProperty
+ {
+ private string _value;
+
+ ///
+ /// The id of the webview event.
+ ///
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ ///
+ /// The string value for the display mode, which can be set directly or parsed into the WebViewEventMode enum using the WebViewEventMode property. Setting this property will also trigger the ValueChangedAction if it is set.
+ ///
+ public string Value { get { return _value; } set { _value = value; OnValueChanged(); } }
+
+ ///
+ /// The display mode for the webview event as an enum, which can be used for easier handling of expected values. Expected values are Fullscreen, Modal, or Unknown.
+ ///
+ public eWebViewEventMode WebViewEventMode
+ {
+ get
+ {
+ eWebViewEventMode mode;
+ System.Enum.TryParse(Value, true, out mode);
+ return mode;
+ }
+ }
+ }
+
+ ///
+ /// Represents the target for a webview event, with a string value and a corresponding enum property for easier handling of expected values. Setting the Value property will also trigger the ValueChangedAction if it is set.
+ ///
+ public class Target : ValueProperty
+ {
+ private string _value;
+
+ ///
+ /// The id of the webview event.
+ ///
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ ///
+ /// The string value for the target, which can be set directly or parsed into the eWebViewTarget enum using the WebViewTarget property. Expected values are "OSD", "Controller", "PersistentWebApp", or "RoomScheduler". Setting this property will also trigger the ValueChangedAction if it is set.
+ ///
+ public string Value { get { return _value; } set { _value = value; OnValueChanged(); } }
+
+ ///
+ /// The target for the webview event as an enum, which can be used for easier handling of expected values. Expected values are OSD, Controller, PersistentWebApp, or RoomScheduler.
+ ///
+ public eWebViewTarget WebViewTarget
+ {
+ get
+ {
+ eWebViewTarget target;
+ System.Enum.TryParse(Value, true, out target);
+ return target;
+ }
+ }
+ }
+
+ ///
+ /// Represents the status of a webview event, which can include error information (XPath and Reason) as well as the current status of the webview. The Value property can be used to get or set the current status of the webview, while the XPath and Reason properties can provide additional information in case of an error. The StatusString property can be used to get or set the raw status string from the event.
+ ///
+ public class Status
+ {
+ ///
+ /// The XPath of the webview event, which can provide information about where an error occurred in the webview. This property is typically only populated in case of an error, and may be null otherwise.
+ ///
+ [JsonProperty("XPath", NullValueHandling = NullValueHandling.Ignore)]
+ public XPath XPath { get; set; }
+
+ ///
+ /// The reason for an error in the webview event, which can provide additional information about what went wrong. This property is typically only populated in case of an error, and may be null otherwise.
+ ///
+ [JsonProperty("Reason", NullValueHandling = NullValueHandling.Ignore)]
+ public Reason Reason { get; set; }
+
+ ///
+ /// The raw status string from the webview event, which can provide information about the current status of the webview. This property can be used to get or set the status directly, but it is recommended to use the Value property for easier handling of expected values. Setting this property will not trigger any actions, while setting the Value property will trigger the ValueChangedAction if it is set.
+ ///
+ [JsonProperty("status", NullValueHandling = NullValueHandling.Ignore)]
+ public string StatusString { get; set; }
+
+ ///
+ /// The current status of the webview as a string, which can be set directly or parsed into a WebViewEventMode enum using the WebViewEventMode property. Expected values are "Fullscreen", "Modal", "Cleared", "Error", or "Unknown". Setting this property will trigger the ValueChangedAction if it is set.
+ ///
+ [JsonProperty("Value", NullValueHandling = NullValueHandling.Ignore)]
+ public string Value { get; set; }
+ }
///
- /// Shows the webview with the specified parameters
+ /// Defines the contract for IHasWebView
///
- /// the URL to display in the webview
- /// the display mode for the webview
- /// the title to display on the webview
- /// the target for the webview
- void ShowWebView(string url, string mode, string title, string target);
-
- ///
- /// Hides the webview
- ///
- void HideWebView();
-
- ///
- /// Event raised when the webview status changes
- ///
- event EventHandler WebViewStatusChanged;
-}
-
-
-///
-/// Defines the contract for IHasWebViewWithPwaMode
-///
-public interface IHasWebViewWithPwaMode : IHasWebView
-{
- ///
- /// Indicates whether the webview is currently in PWA mode
- ///
- bool IsInPwaMode { get; }
-
- ///
- /// Gets the BoolFeedback indicating whether the webview is currently in PWA mode
- ///
- BoolFeedback IsInPwaModeFeedback { get; }
-
- ///
- /// Sends navigators to the specified PWA URL. Accepts an absolute URL or a relative URL for a mobile control app
- ///
- /// The URL to navigate to
- void SendNavigatorsToPwaUrl(string url);
-
- ///
- /// Exits navigators from PWA mode
- ///
- void ExitNavigatorsPwaMode();
-}
-
-
-///
-/// Represents a WebViewStatusChangedEventArgs
-///
-public class WebViewStatusChangedEventArgs : EventArgs
-{
- ///
- /// Gets or sets the Status
- ///
- public string Status { get; }
-
- ///
- /// Constructor for WebViewStatusChangedEventArgs
- ///
- /// the new status of the webview
- public WebViewStatusChangedEventArgs(string status)
+ public interface IHasWebView
{
- Status = status;
+ ///
+ /// Indicates whether the webview is currently visible
+ ///
+ bool WebviewIsVisible { get; }
+
+ ///
+ /// Shows the webview with the specified parameters
+ ///
+ /// the URL to display in the webview
+ /// the display mode for the webview
+ /// the title to display on the webview
+ /// the target for the webview
+ void ShowWebView(string url, string mode, string title, string target);
+
+ ///
+ /// Hides the webview
+ ///
+ void HideWebView();
+
+ ///
+ /// Event raised when the webview status changes
+ ///
+ event EventHandler WebViewStatusChanged;
+
+ }
+
+
+ ///
+ /// Defines the contract for IHasWebViewWithPwaMode
+ ///
+ public interface IHasWebViewWithPwaMode : IHasWebView
+ {
+ ///
+ /// Indicates whether the webview is currently in PWA mode
+ ///
+ bool IsInPwaMode { get; }
+
+ ///
+ /// Gets the BoolFeedback indicating whether the webview is currently in PWA mode
+ ///
+ BoolFeedback IsInPwaModeFeedback { get; }
+
+ ///
+ /// Sends navigators to the specified PWA URL. Accepts an absolute URL or a relative URL for a mobile control app
+ ///
+ /// The URL to navigate to
+ void SendNavigatorsToPwaUrl(string url);
+
+ ///
+ /// Exits navigators from PWA mode
+ ///
+ void ExitNavigatorsPwaMode();
+ }
+
+
+ ///
+ /// Represents a WebViewStatusChangedEventArgs
+ ///
+ public class WebViewStatusChangedEventArgs : EventArgs
+ {
+ ///
+ /// Gets or sets the Status
+ ///
+ public string Status { get; }
+
+ ///
+ /// Gets or sets the WebViewEvent associated with the status change, which can provide additional information about the webview event that triggered the status change, such as display parameters or error information. This property allows you to include the full WebViewEvent in the event args, giving you access to all relevant details about the webview event when handling the WebViewStatusChanged event.
+ ///
+ public WebViewEvent WebView { get; }
+
+ ///
+ /// Constructor for WebViewStatusChangedEventArgs
+ ///
+ /// the new status of the webview
+ public WebViewStatusChangedEventArgs(string status)
+ {
+ Status = status;
+ }
+
+ ///
+ /// Constructor for WebViewStatusChangedEventArgs with WebViewEvent parameter, which can provide additional information about the webview event that triggered the status change, such as display parameters or error information. This constructor allows you to include the full WebViewEvent in the event args, giving you access to all relevant details about the webview event when handling the WebViewStatusChanged event.
+ ///
+ /// the new status of the webview
+ /// the WebViewEvent associated with the status change
+ public WebViewStatusChangedEventArgs(string status, WebViewEvent webview)
+ {
+ Status = status;
+ WebView = webview;
+ }
}
}
-
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasWirelessSharing.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasWirelessSharing.cs
new file mode 100644
index 00000000..6cd3835e
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHasWirelessSharing.cs
@@ -0,0 +1,48 @@
+using System;
+
+namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
+{
+ ///
+ /// Defines the contract for a wireless presentation endpoint that reports whether a wireless
+ /// sharing session is currently active. Implemented by platforms such as Crestron AirMedia,
+ /// Mersive Solstice, Barco ClickShare, Miracast/Teams receivers, etc. Allows consumers (e.g.
+ /// room plugins) to react to wireless sharing activity without taking a dependency on any
+ /// concrete device implementation.
+ ///
+ ///
+ /// This refers specifically to wireless screen/device mirroring, as distinct from in-call
+ /// content sharing on a video conference.
+ ///
+ public interface IHasWirelessSharing
+ {
+ ///
+ /// Reports whether a wireless sharing session is currently active (content is being presented).
+ ///
+ BoolFeedback IsSharingFeedback { get; }
+
+ ///
+ /// Raised when wireless sharing starts or stops. The event args carry the new sharing state.
+ ///
+ event EventHandler SharingChanged;
+ }
+
+ ///
+ /// Event arguments describing a change in wireless sharing state.
+ ///
+ public class WirelessSharingEventArgs : EventArgs
+ {
+ ///
+ /// True if a wireless sharing session is active (content is being presented), false otherwise.
+ ///
+ public bool IsSharing { get; private set; }
+
+ ///
+ /// Creates a new .
+ ///
+ /// True if a wireless sharing session is active, false otherwise.
+ public WirelessSharingEventArgs(bool isSharing)
+ {
+ IsSharing = isSharing;
+ }
+ }
+}
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHumiditySensor.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHumiditySensor.cs
index f28059c3..748fb860 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHumiditySensor.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IHumiditySensor.cs
@@ -1,4 +1,9 @@
-
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
///
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IInitializationExceptions.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IInitializationExceptions.cs
deleted file mode 100644
index 55ce51ab..00000000
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/IInitializationExceptions.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-using System.Collections.Generic;
-
-namespace PepperDash.Essentials.Core.DeviceTypeInterfaces;
-
-///
-/// Defines the contract for devices that can report initialization exceptions.
-///
-public interface IInitializationExceptions
-{
- ///
- /// Gets a list of exceptions that occurred during the initialization of the program.
- ///
- List InitializationExceptions { get; }
-}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ILanguageDefinition.cs b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ILanguageDefinition.cs
index 4cdffc83..f02c0e05 100644
--- a/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ILanguageDefinition.cs
+++ b/src/PepperDash.Essentials.Core/DeviceTypeInterfaces/ILanguageDefinition.cs
@@ -1,57 +1,56 @@
using System;
using System.Collections.Generic;
-namespace PepperDash.Essentials.Core.DeviceTypeInterfaces;
-
-
-///
-/// Defines the contract for ILanguageDefinition
-///
-public interface ILanguageDefinition
+namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
///
- /// The locale name for the language definition
+ /// Defines the contract for ILanguageDefinition
///
- string LocaleName { get; set; }
+ public interface ILanguageDefinition
+ {
+ ///
+ /// The locale name for the language definition
+ ///
+ string LocaleName { get; set; }
- ///
- /// The friendly name for the language definition
- ///
- string FriendlyName { get; set; }
+ ///
+ /// The friendly name for the language definition
+ ///
+ string FriendlyName { get; set; }
- ///
- /// Indicates whether the language definition is enabled
- ///
- bool Enable { get; set; }
+ ///
+ /// Indicates whether the language definition is enabled
+ ///
+ bool Enable { get; set; }
- ///
- /// The UI labels for the language definition
- ///
- List UiLabels { get; set; }
+ ///