test: add crestron mocks and Test configuration

This commit is contained in:
Andrew Welker
2025-08-13 21:15:11 -05:00
parent 4ee62088fa
commit bc60106afd
25 changed files with 3728 additions and 563 deletions

View File

@@ -1,97 +1,270 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33213.308
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PepperDash.Essentials.Devices.Common", "src\PepperDash.Essentials.Devices.Common\PepperDash.Essentials.Devices.Common.csproj", "{53E204B7-97DD-441D-A96C-721DF014DF82}"
ProjectSection(ProjectDependencies) = postProject
{E5336563-1194-501E-BC4A-79AD9283EF90} = {E5336563-1194-501E-BC4A-79AD9283EF90}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PepperDash.Essentials", "src\PepperDash.Essentials\PepperDash.Essentials.csproj", "{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}"
ProjectSection(ProjectDependencies) = postProject
{E5336563-1194-501E-BC4A-79AD9283EF90} = {E5336563-1194-501E-BC4A-79AD9283EF90}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PepperDash.Essentials.Core", "src\PepperDash.Essentials.Core\PepperDash.Essentials.Core.csproj", "{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}"
ProjectSection(ProjectDependencies) = postProject
{E5336563-1194-501E-BC4A-79AD9283EF90} = {E5336563-1194-501E-BC4A-79AD9283EF90}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mobile Control", "Mobile Control", "{B24989D7-32B5-48D5-9AE1-5F3B17D25206}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Essentials.MobileControl", "src\PepperDash.Essentials.MobileControl\PepperDash.Essentials.MobileControl.csproj", "{F6D362DE-2256-44B1-927A-8CE4705D839A}"
ProjectSection(ProjectDependencies) = postProject
{E5336563-1194-501E-BC4A-79AD9283EF90} = {E5336563-1194-501E-BC4A-79AD9283EF90}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Essentials.MobileControl.Messengers", "src\PepperDash.Essentials.MobileControl.Messengers\PepperDash.Essentials.MobileControl.Messengers.csproj", "{B438694F-8FF7-464A-9EC8-10427374471F}"
ProjectSection(ProjectDependencies) = postProject
{E5336563-1194-501E-BC4A-79AD9283EF90} = {E5336563-1194-501E-BC4A-79AD9283EF90}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Essentials", "Essentials", "{AD98B742-8D85-481C-A69D-D8D8ABED39EA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Core", "src\PepperDash.Core\PepperDash.Core.csproj", "{E5336563-1194-501E-BC4A-79AD9283EF90}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug 4.7.2|Any CPU = Debug 4.7.2|Any CPU
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
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|Any CPU.ActiveCfg = Debug|Any CPU
{53E204B7-97DD-441D-A96C-721DF014DF82}.Debug|Any CPU.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
{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|Any CPU.ActiveCfg = Debug|Any CPU
{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug|Any CPU.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
{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|Any CPU.ActiveCfg = Debug|Any CPU
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Debug|Any CPU.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
{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|Any CPU.ActiveCfg = Debug|Any CPU
{F6D362DE-2256-44B1-927A-8CE4705D839A}.Debug|Any CPU.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
{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|Any CPU.ActiveCfg = Debug|Any CPU
{B438694F-8FF7-464A-9EC8-10427374471F}.Debug|Any CPU.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
{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|Any CPU.ActiveCfg = Debug|Any CPU
{E5336563-1194-501E-BC4A-79AD9283EF90}.Debug|Any CPU.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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{53E204B7-97DD-441D-A96C-721DF014DF82} = {AD98B742-8D85-481C-A69D-D8D8ABED39EA}
{CB3B11BA-625C-4D35-B663-FDC5BE9A230E} = {AD98B742-8D85-481C-A69D-D8D8ABED39EA}
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B} = {AD98B742-8D85-481C-A69D-D8D8ABED39EA}
{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}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6907A4BF-7201-47CF-AAB1-3597F3B8E1C3}
EndGlobalSection
EndGlobal
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33213.308
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PepperDash.Essentials.Devices.Common", "src\PepperDash.Essentials.Devices.Common\PepperDash.Essentials.Devices.Common.csproj", "{53E204B7-97DD-441D-A96C-721DF014DF82}"
ProjectSection(ProjectDependencies) = postProject
{E5336563-1194-501E-BC4A-79AD9283EF90} = {E5336563-1194-501E-BC4A-79AD9283EF90}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PepperDash.Essentials", "src\PepperDash.Essentials\PepperDash.Essentials.csproj", "{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}"
ProjectSection(ProjectDependencies) = postProject
{E5336563-1194-501E-BC4A-79AD9283EF90} = {E5336563-1194-501E-BC4A-79AD9283EF90}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PepperDash.Essentials.Core", "src\PepperDash.Essentials.Core\PepperDash.Essentials.Core.csproj", "{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}"
ProjectSection(ProjectDependencies) = postProject
{E5336563-1194-501E-BC4A-79AD9283EF90} = {E5336563-1194-501E-BC4A-79AD9283EF90}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mobile Control", "Mobile Control", "{B24989D7-32B5-48D5-9AE1-5F3B17D25206}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Essentials.MobileControl", "src\PepperDash.Essentials.MobileControl\PepperDash.Essentials.MobileControl.csproj", "{F6D362DE-2256-44B1-927A-8CE4705D839A}"
ProjectSection(ProjectDependencies) = postProject
{E5336563-1194-501E-BC4A-79AD9283EF90} = {E5336563-1194-501E-BC4A-79AD9283EF90}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Essentials.MobileControl.Messengers", "src\PepperDash.Essentials.MobileControl.Messengers\PepperDash.Essentials.MobileControl.Messengers.csproj", "{B438694F-8FF7-464A-9EC8-10427374471F}"
ProjectSection(ProjectDependencies) = postProject
{E5336563-1194-501E-BC4A-79AD9283EF90} = {E5336563-1194-501E-BC4A-79AD9283EF90}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Essentials", "Essentials", "{AD98B742-8D85-481C-A69D-D8D8ABED39EA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
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}") = "Tests", "Tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrestronMock", "src\CrestronMock\CrestronMock.csproj", "{01191C7B-606D-4169-81B0-BC8BC1623CE9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EssentialsTests", "Tests\EssentialsTests\EssentialsTests.csproj", "{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}"
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
Test|Any CPU = Test|Any CPU
Test|x64 = Test|x64
Test|x86 = Test|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
{53E204B7-97DD-441D-A96C-721DF014DF82}.Test|Any CPU.ActiveCfg = Test|Any CPU
{53E204B7-97DD-441D-A96C-721DF014DF82}.Test|Any CPU.Build.0 = Test|Any CPU
{53E204B7-97DD-441D-A96C-721DF014DF82}.Test|x64.ActiveCfg = Test|Any CPU
{53E204B7-97DD-441D-A96C-721DF014DF82}.Test|x64.Build.0 = Test|Any CPU
{53E204B7-97DD-441D-A96C-721DF014DF82}.Test|x86.ActiveCfg = Test|Any CPU
{53E204B7-97DD-441D-A96C-721DF014DF82}.Test|x86.Build.0 = Test|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
{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Test|Any CPU.ActiveCfg = Test|Any CPU
{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Test|Any CPU.Build.0 = Test|Any CPU
{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Test|x64.ActiveCfg = Test|Any CPU
{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Test|x64.Build.0 = Test|Any CPU
{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Test|x86.ActiveCfg = Test|Any CPU
{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Test|x86.Build.0 = Test|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
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Test|Any CPU.ActiveCfg = Test|Any CPU
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Test|Any CPU.Build.0 = Test|Any CPU
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Test|x64.ActiveCfg = Test|Any CPU
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Test|x64.Build.0 = Test|Any CPU
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Test|x86.ActiveCfg = Test|Any CPU
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}.Test|x86.Build.0 = Test|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
{F6D362DE-2256-44B1-927A-8CE4705D839A}.Test|Any CPU.ActiveCfg = Test|Any CPU
{F6D362DE-2256-44B1-927A-8CE4705D839A}.Test|Any CPU.Build.0 = Test|Any CPU
{F6D362DE-2256-44B1-927A-8CE4705D839A}.Test|x64.ActiveCfg = Test|Any CPU
{F6D362DE-2256-44B1-927A-8CE4705D839A}.Test|x64.Build.0 = Test|Any CPU
{F6D362DE-2256-44B1-927A-8CE4705D839A}.Test|x86.ActiveCfg = Test|Any CPU
{F6D362DE-2256-44B1-927A-8CE4705D839A}.Test|x86.Build.0 = Test|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
{B438694F-8FF7-464A-9EC8-10427374471F}.Test|Any CPU.ActiveCfg = Test|Any CPU
{B438694F-8FF7-464A-9EC8-10427374471F}.Test|Any CPU.Build.0 = Test|Any CPU
{B438694F-8FF7-464A-9EC8-10427374471F}.Test|x64.ActiveCfg = Test|Any CPU
{B438694F-8FF7-464A-9EC8-10427374471F}.Test|x64.Build.0 = Test|Any CPU
{B438694F-8FF7-464A-9EC8-10427374471F}.Test|x86.ActiveCfg = Test|Any CPU
{B438694F-8FF7-464A-9EC8-10427374471F}.Test|x86.Build.0 = Test|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
{E5336563-1194-501E-BC4A-79AD9283EF90}.Test|Any CPU.ActiveCfg = Test|Any CPU
{E5336563-1194-501E-BC4A-79AD9283EF90}.Test|Any CPU.Build.0 = Test|Any CPU
{E5336563-1194-501E-BC4A-79AD9283EF90}.Test|x64.ActiveCfg = Test|Any CPU
{E5336563-1194-501E-BC4A-79AD9283EF90}.Test|x64.Build.0 = Test|Any CPU
{E5336563-1194-501E-BC4A-79AD9283EF90}.Test|x86.ActiveCfg = Test|Any CPU
{E5336563-1194-501E-BC4A-79AD9283EF90}.Test|x86.Build.0 = Test|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Debug 4.7.2|Any CPU.ActiveCfg = Debug|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Debug 4.7.2|Any CPU.Build.0 = Debug|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Debug 4.7.2|x64.ActiveCfg = Debug|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Debug 4.7.2|x64.Build.0 = Debug|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Debug 4.7.2|x86.ActiveCfg = Debug|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Debug 4.7.2|x86.Build.0 = Debug|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Debug|x64.ActiveCfg = Debug|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Debug|x64.Build.0 = Debug|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Debug|x86.ActiveCfg = Debug|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Debug|x86.Build.0 = Debug|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Release|Any CPU.Build.0 = Release|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Release|x64.ActiveCfg = Release|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Release|x64.Build.0 = Release|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Release|x86.ActiveCfg = Release|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Release|x86.Build.0 = Release|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Test|Any CPU.ActiveCfg = Test|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Test|Any CPU.Build.0 = Test|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Test|x64.ActiveCfg = Test|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Test|x64.Build.0 = Test|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Test|x86.ActiveCfg = Test|Any CPU
{01191C7B-606D-4169-81B0-BC8BC1623CE9}.Test|x86.Build.0 = Test|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Debug 4.7.2|Any CPU.ActiveCfg = Debug|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Debug 4.7.2|Any CPU.Build.0 = Debug|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Debug 4.7.2|x64.ActiveCfg = Debug|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Debug 4.7.2|x64.Build.0 = Debug|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Debug 4.7.2|x86.ActiveCfg = Debug|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Debug 4.7.2|x86.Build.0 = Debug|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Debug|x64.ActiveCfg = Debug|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Debug|x64.Build.0 = Debug|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Debug|x86.ActiveCfg = Debug|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Debug|x86.Build.0 = Debug|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Release|Any CPU.Build.0 = Release|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Release|x64.ActiveCfg = Release|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Release|x64.Build.0 = Release|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Release|x86.ActiveCfg = Release|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Release|x86.Build.0 = Release|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Test|Any CPU.ActiveCfg = Test|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Test|Any CPU.Build.0 = Test|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Test|x64.ActiveCfg = Test|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Test|x64.Build.0 = Test|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Test|x86.ActiveCfg = Test|Any CPU
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB}.Test|x86.Build.0 = Test|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{53E204B7-97DD-441D-A96C-721DF014DF82} = {AD98B742-8D85-481C-A69D-D8D8ABED39EA}
{CB3B11BA-625C-4D35-B663-FDC5BE9A230E} = {AD98B742-8D85-481C-A69D-D8D8ABED39EA}
{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B} = {AD98B742-8D85-481C-A69D-D8D8ABED39EA}
{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}
{01191C7B-606D-4169-81B0-BC8BC1623CE9} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6907A4BF-7201-47CF-AAB1-3597F3B8E1C3}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,183 @@
using System;
using System.Threading;
namespace Crestron.SimplSharp
{
/// <summary>
/// Mock implementation of Crestron CCriticalSection for testing purposes
/// Provides the same public API surface as the real CCriticalSection
/// </summary>
public class CCriticalSection : IDisposable
{
#region Private Fields
private readonly object _lockObject = new object();
private readonly ReaderWriterLockSlim _readerWriterLock = new ReaderWriterLockSlim();
private bool _disposed = false;
#endregion
#region Constructor
/// <summary>Initializes a new instance of the CCriticalSection class</summary>
public CCriticalSection()
{
// Mock implementation - no actual initialization required
}
#endregion
#region Public Methods
/// <summary>Enters the critical section</summary>
public void Enter()
{
if (_disposed) throw new ObjectDisposedException(nameof(CCriticalSection));
Monitor.Enter(_lockObject);
}
/// <summary>Tries to enter the critical section</summary>
/// <returns>True if the critical section was entered successfully</returns>
public bool TryEnter()
{
if (_disposed) return false;
return Monitor.TryEnter(_lockObject);
}
/// <summary>Tries to enter the critical section with a timeout</summary>
/// <param name="timeout">Timeout in milliseconds</param>
/// <returns>True if the critical section was entered successfully</returns>
public bool TryEnter(int timeout)
{
if (_disposed) return false;
return Monitor.TryEnter(_lockObject, timeout);
}
/// <summary>Tries to enter the critical section with a TimeSpan timeout</summary>
/// <param name="timeout">Timeout as TimeSpan</param>
/// <returns>True if the critical section was entered successfully</returns>
public bool TryEnter(TimeSpan timeout)
{
if (_disposed) return false;
return Monitor.TryEnter(_lockObject, timeout);
}
/// <summary>Leaves the critical section</summary>
public void Leave()
{
if (_disposed) return;
try
{
Monitor.Exit(_lockObject);
}
catch (SynchronizationLockException)
{
// Ignore if not held by current thread
}
}
/// <summary>Enters a read lock</summary>
public void EnterReadLock()
{
if (_disposed) throw new ObjectDisposedException(nameof(CCriticalSection));
_readerWriterLock.EnterReadLock();
}
/// <summary>Tries to enter a read lock</summary>
/// <returns>True if the read lock was acquired successfully</returns>
public bool TryEnterReadLock()
{
if (_disposed) return false;
return _readerWriterLock.TryEnterReadLock(0);
}
/// <summary>Tries to enter a read lock with a timeout</summary>
/// <param name="timeout">Timeout in milliseconds</param>
/// <returns>True if the read lock was acquired successfully</returns>
public bool TryEnterReadLock(int timeout)
{
if (_disposed) return false;
return _readerWriterLock.TryEnterReadLock(timeout);
}
/// <summary>Exits the read lock</summary>
public void ExitReadLock()
{
if (_disposed) return;
try
{
_readerWriterLock.ExitReadLock();
}
catch (SynchronizationLockException)
{
// Ignore if not held by current thread
}
}
/// <summary>Enters a write lock</summary>
public void EnterWriteLock()
{
if (_disposed) throw new ObjectDisposedException(nameof(CCriticalSection));
_readerWriterLock.EnterWriteLock();
}
/// <summary>Tries to enter a write lock</summary>
/// <returns>True if the write lock was acquired successfully</returns>
public bool TryEnterWriteLock()
{
if (_disposed) return false;
return _readerWriterLock.TryEnterWriteLock(0);
}
/// <summary>Tries to enter a write lock with a timeout</summary>
/// <param name="timeout">Timeout in milliseconds</param>
/// <returns>True if the write lock was acquired successfully</returns>
public bool TryEnterWriteLock(int timeout)
{
if (_disposed) return false;
return _readerWriterLock.TryEnterWriteLock(timeout);
}
/// <summary>Exits the write lock</summary>
public void ExitWriteLock()
{
if (_disposed) return;
try
{
_readerWriterLock.ExitWriteLock();
}
catch (SynchronizationLockException)
{
// Ignore if not held by current thread
}
}
#endregion
#region IDisposable Implementation
/// <summary>Disposes the critical section and releases resources</summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>Protected dispose method</summary>
/// <param name="disposing">True if disposing managed resources</param>
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_readerWriterLock?.Dispose();
}
_disposed = true;
}
}
#endregion
}
}

191
src/CrestronMock/CTimer.cs Normal file
View File

@@ -0,0 +1,191 @@
using System;
using System.Threading;
namespace Crestron.SimplSharp
{
/// <summary>Mock timer event handler</summary>
/// <param name="userObject">User-defined object</param>
public delegate void CTimerEventHandler(object? userObject);
/// <summary>
/// Mock implementation of Crestron CTimer for testing purposes
/// Provides the same public API surface as the real CTimer
/// </summary>
public class CTimer : IDisposable
{
#region Private Fields
private Timer? _timer;
private readonly object _lockObject = new object();
private bool _disposed = false;
private readonly CTimerEventHandler? _callback;
private readonly object? _userObject;
#endregion
#region Properties
/// <summary>Gets whether the timer is currently running</summary>
public bool Running { get; private set; } = false;
/// <summary>Gets the timer interval in milliseconds</summary>
public long TimeToFire { get; private set; } = 0;
#endregion
#region Constructors
/// <summary>Initializes a new instance of the CTimer class</summary>
/// <param name="callbackFunction">Function to call when timer fires</param>
/// <param name="userObject">User-defined object to pass to callback</param>
/// <param name="dueTime">Time before timer first fires (milliseconds)</param>
/// <param name="period">Interval between timer fires (milliseconds), or -1 for one-shot</param>
public CTimer(CTimerEventHandler callbackFunction, object? userObject, long dueTime, long period)
{
_callback = callbackFunction;
_userObject = userObject;
TimeToFire = period;
var dueTimeInt = dueTime > int.MaxValue ? Timeout.Infinite : (int)dueTime;
var periodInt = period > int.MaxValue || period < 0 ? Timeout.Infinite : (int)period;
_timer = new Timer(TimerCallback, null, dueTimeInt, periodInt);
Running = dueTime != Timeout.Infinite;
}
/// <summary>Initializes a new instance of the CTimer class</summary>
/// <param name="callbackFunction">Function to call when timer fires</param>
/// <param name="userObject">User-defined object to pass to callback</param>
/// <param name="dueTime">Time before timer first fires (milliseconds)</param>
public CTimer(CTimerEventHandler callbackFunction, object? userObject, long dueTime)
: this(callbackFunction, userObject, dueTime, -1)
{
}
/// <summary>Initializes a new instance of the CTimer class</summary>
/// <param name="callbackFunction">Function to call when timer fires</param>
/// <param name="userObject">User-defined object to pass to callback</param>
public CTimer(CTimerEventHandler callbackFunction, object? userObject)
: this(callbackFunction, userObject, Timeout.Infinite, -1)
{
}
/// <summary>Initializes a new instance of the CTimer class</summary>
/// <param name="callbackFunction">Function to call when timer fires</param>
public CTimer(CTimerEventHandler callbackFunction)
: this(callbackFunction, null, Timeout.Infinite, -1)
{
}
#endregion
#region Public Methods
/// <summary>Resets the timer with a new due time</summary>
/// <param name="dueTime">New due time in milliseconds</param>
/// <returns>True if successful</returns>
public bool Reset(long dueTime)
{
return Reset(dueTime, -1);
}
/// <summary>Resets the timer with new due time and period</summary>
/// <param name="dueTime">New due time in milliseconds</param>
/// <param name="period">New period in milliseconds, or -1 for one-shot</param>
/// <returns>True if successful</returns>
public bool Reset(long dueTime, long period)
{
lock (_lockObject)
{
if (_disposed || _timer == null) return false;
TimeToFire = period;
var dueTimeInt = dueTime > int.MaxValue ? Timeout.Infinite : (int)dueTime;
var periodInt = period > int.MaxValue || period < 0 ? Timeout.Infinite : (int)period;
try
{
_timer.Change(dueTimeInt, periodInt);
Running = dueTime != Timeout.Infinite;
return true;
}
catch
{
return false;
}
}
}
/// <summary>Stops the timer</summary>
/// <returns>True if successful</returns>
public bool Stop()
{
lock (_lockObject)
{
if (_disposed || _timer == null) return false;
try
{
_timer.Change(Timeout.Infinite, Timeout.Infinite);
Running = false;
return true;
}
catch
{
return false;
}
}
}
#endregion
#region Private Methods
/// <summary>Internal timer callback</summary>
/// <param name="state">Timer state (unused)</param>
private void TimerCallback(object? state)
{
try
{
_callback?.Invoke(_userObject);
}
catch
{
// Suppress exceptions in callback to prevent timer from stopping
}
}
#endregion
#region IDisposable Implementation
/// <summary>Disposes the timer and releases resources</summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>Protected dispose method</summary>
/// <param name="disposing">True if disposing managed resources</param>
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
lock (_lockObject)
{
_timer?.Dispose();
_timer = null;
Running = false;
}
}
_disposed = true;
}
}
#endregion
}
}

View File

@@ -0,0 +1,266 @@
using System;
using System.Collections.Generic;
namespace CrestronMock;
/// <summary>Mock Crestron signal base class</summary>
public class Sig { }
/// <summary>Mock UShort input signal</summary>
public class UShortInputSig { }
/// <summary>Mock UShort output signal</summary>
public class UShortOutputSig { }
/// <summary>Mock Boolean input signal</summary>
public class BoolInputSig { }
/// <summary>Mock String input signal</summary>
public class StringInputSig { }
/// <summary>Mock Boolean output signal</summary>
public class BoolOutputSig { }
/// <summary>Mock String output signal</summary>
public class StringOutputSig { }
/// <summary>Mock signal group</summary>
public class SigGroup { }
/// <summary>Mock COM port</summary>
public class ComPort { }
/// <summary>Mock relay</summary>
public class Relay { }
/// <summary>Mock IR output port</summary>
public class IROutputPort { }
/// <summary>Mock IO port</summary>
public class IOPort { }
/// <summary>Mock VersiPort</summary>
public class VersiPort { }
/// <summary>Mock IR input port</summary>
public class IRInputPort { }
/// <summary>Signal type enumeration</summary>
public enum eSigType
{
Bool,
UShort,
String
}
/// <summary>Mock read-only collection</summary>
public class ReadOnlyCollection<TKey, TValue> : Dictionary<TKey, TValue> where TKey : notnull
{
}
/// <summary>Mock COM ports interface</summary>
public interface IComPorts
{
ComPort[] ComPorts { get; }
}
/// <summary>Mock relay ports interface</summary>
public interface IRelayPorts
{
Relay[] RelayPorts { get; }
}
/// <summary>Mock IR output ports interface</summary>
public interface IIROutputPorts
{
IROutputPort[] IROutputPorts { get; }
}
/// <summary>Mock IO ports interface</summary>
public interface IIOPorts
{
IOPort[] IOPorts { get; }
}
/// <summary>Mock digital input ports interface</summary>
public interface IDigitalInputPorts
{
VersiPort[] DigitalInputPorts { get; }
}
/// <summary>Mock IR input port interface</summary>
public interface IIRInputPort
{
IRInputPort IRInputPort { get; }
}
/// <summary>
/// Mock implementation of CrestronControlSystem for testing purposes
/// Base class for the CrestronControlSystem The Customer application is derived over this class
/// </summary>
public class CrestronControlSystem : IComPorts, IRelayPorts, IIROutputPorts, IIOPorts, IDigitalInputPorts, IIRInputPort
{
// Static fields
public static Sig NullCue { get; set; } = new Sig();
public static UShortInputSig NullUShortInputSig { get; set; } = new UShortInputSig();
public static UShortOutputSig NullUShortOutputSig { get; set; } = new UShortOutputSig();
public static BoolInputSig NullBoolInputSig { get; set; } = new BoolInputSig();
public static StringInputSig NullStringInputSig { get; set; } = new StringInputSig();
public static BoolOutputSig NullBoolOutputSig { get; set; } = new BoolOutputSig();
public static StringOutputSig NullStringOutputSig { get; set; } = new StringOutputSig();
public static ReadOnlyCollection<int, SigGroup> SigGroups { get; set; } = new ReadOnlyCollection<int, SigGroup>();
public static int MaxNumberOfEventsInQueue { get; set; } = 1000;
// Constructor
public CrestronControlSystem()
{
// Initialize collections and properties
ComPorts = Array.Empty<ComPort>();
RelayPorts = Array.Empty<Relay>();
IROutputPorts = Array.Empty<IROutputPort>();
IOPorts = Array.Empty<IOPort>();
DigitalInputPorts = Array.Empty<VersiPort>();
IRInputPort = new IRInputPort();
}
// Virtual methods that can be overridden
public virtual void InitializeSystem()
{
// Override in derived classes
}
public virtual void SavePreset()
{
// Override in derived classes
}
public virtual void RecallPreset()
{
// Override in derived classes
}
public virtual void BassFlat()
{
// Override in derived classes
}
public virtual void TrebleFlat()
{
// Override in derived classes
}
public virtual void LimiterEnable()
{
// Override in derived classes
}
public virtual void LimiterDisable()
{
// Override in derived classes
}
public virtual void LimiterSoftKneeOn()
{
// Override in derived classes
}
public virtual void LimiterSoftKneeOff()
{
// Override in derived classes
}
public virtual void MasterMuteOn()
{
// Override in derived classes
}
public virtual void MasterMuteOff()
{
// Override in derived classes
}
public virtual void MicMasterMuteOn()
{
// Override in derived classes
}
public virtual void MicMasterMuteOff()
{
// Override in derived classes
}
public virtual void SourceMuteOn()
{
// Override in derived classes
}
public virtual void SourceMuteOff()
{
// Override in derived classes
}
// Non-virtual methods
public void MicMuteOn(uint MicNumber)
{
// Implementation
}
public void MicMuteOff(uint MicNumber)
{
// Implementation
}
public void MonoOutput()
{
// Implementation
}
public void StereoOutput()
{
// Implementation
}
// Static methods for SigGroup management
public static SigGroup CreateSigGroup(int groupID, params BoolInputSig[] boolInputSigs)
{
return new SigGroup();
}
public static SigGroup CreateSigGroup(int groupID, params UShortInputSig[] ushortInputSigs)
{
return new SigGroup();
}
public static SigGroup CreateSigGroup(int groupID, eSigType type)
{
return new SigGroup();
}
public static SigGroup CreateSigGroup(int groupID, params StringInputSig[] stringInputSigs)
{
return new SigGroup();
}
public static void RemoveSigGroup(int groupID)
{
// Implementation
}
public static void RemoveSigGroup(SigGroup sigGroupToRemove)
{
// Implementation
}
public static void ClearSigGroups()
{
// Implementation
}
// Interface implementations
public ComPort[] ComPorts { get; set; }
public Relay[] RelayPorts { get; set; }
public IROutputPort[] IROutputPorts { get; set; }
public IOPort[] IOPorts { get; set; }
public VersiPort[] DigitalInputPorts { get; set; }
public IRInputPort IRInputPort { get; set; }
}

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Configurations>Debug;Release;Test</Configurations>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,238 @@
using System;
using System.Collections.Generic;
using System.Threading;
namespace Crestron.SimplSharp
{
/// <summary>
/// Mock implementation of Crestron CrestronQueue for testing purposes
/// Provides the same public API surface as the real CrestronQueue
/// </summary>
/// <typeparam name="T">Type of items in the queue</typeparam>
public class CrestronQueue<T> : IDisposable
{
#region Private Fields
private readonly Queue<T> _queue = new Queue<T>();
private readonly object _lockObject = new object();
private readonly ManualResetEventSlim _dataAvailableEvent = new ManualResetEventSlim(false);
private bool _disposed = false;
#endregion
#region Properties
/// <summary>Gets the number of items in the queue</summary>
public int Count
{
get
{
lock (_lockObject)
{
return _queue.Count;
}
}
}
/// <summary>Gets whether the queue is empty</summary>
public bool IsEmpty
{
get
{
lock (_lockObject)
{
return _queue.Count == 0;
}
}
}
#endregion
#region Constructor
/// <summary>Initializes a new instance of the CrestronQueue class</summary>
public CrestronQueue()
{
// Mock implementation
}
#endregion
#region Public Methods
/// <summary>Adds an item to the end of the queue</summary>
/// <param name="item">Item to add</param>
public void Enqueue(T item)
{
if (_disposed) throw new ObjectDisposedException(nameof(CrestronQueue<T>));
lock (_lockObject)
{
_queue.Enqueue(item);
_dataAvailableEvent.Set();
}
}
/// <summary>Removes and returns the item at the beginning of the queue</summary>
/// <returns>The item that was removed from the queue</returns>
/// <exception cref="InvalidOperationException">Thrown when the queue is empty</exception>
public T Dequeue()
{
if (_disposed) throw new ObjectDisposedException(nameof(CrestronQueue<T>));
lock (_lockObject)
{
if (_queue.Count == 0)
throw new InvalidOperationException("Queue is empty");
var item = _queue.Dequeue();
if (_queue.Count == 0)
_dataAvailableEvent.Reset();
return item;
}
}
/// <summary>Tries to remove and return the item at the beginning of the queue</summary>
/// <param name="item">When successful, contains the dequeued item</param>
/// <returns>True if an item was successfully dequeued</returns>
public bool TryDequeue(out T item)
{
if (_disposed)
{
item = default(T)!;
return false;
}
lock (_lockObject)
{
if (_queue.Count == 0)
{
item = default(T)!;
return false;
}
item = _queue.Dequeue();
if (_queue.Count == 0)
_dataAvailableEvent.Reset();
return true;
}
}
/// <summary>Returns the item at the beginning of the queue without removing it</summary>
/// <returns>The item at the beginning of the queue</returns>
/// <exception cref="InvalidOperationException">Thrown when the queue is empty</exception>
public T Peek()
{
if (_disposed) throw new ObjectDisposedException(nameof(CrestronQueue<T>));
lock (_lockObject)
{
if (_queue.Count == 0)
throw new InvalidOperationException("Queue is empty");
return _queue.Peek();
}
}
/// <summary>Tries to return the item at the beginning of the queue without removing it</summary>
/// <param name="item">When successful, contains the item at the beginning of the queue</param>
/// <returns>True if an item was found</returns>
public bool TryPeek(out T item)
{
if (_disposed)
{
item = default(T)!;
return false;
}
lock (_lockObject)
{
if (_queue.Count == 0)
{
item = default(T)!;
return false;
}
item = _queue.Peek();
return true;
}
}
/// <summary>Removes all items from the queue</summary>
public void Clear()
{
if (_disposed) return;
lock (_lockObject)
{
_queue.Clear();
_dataAvailableEvent.Reset();
}
}
/// <summary>Waits for data to become available in the queue</summary>
/// <param name="timeout">Timeout in milliseconds</param>
/// <returns>True if data became available within the timeout</returns>
public bool WaitForData(int timeout)
{
if (_disposed) return false;
return _dataAvailableEvent.Wait(timeout);
}
/// <summary>Waits for data to become available in the queue</summary>
/// <returns>True when data becomes available</returns>
public bool WaitForData()
{
if (_disposed) return false;
_dataAvailableEvent.Wait();
return true;
}
/// <summary>Copies the queue elements to an array</summary>
/// <returns>Array containing the queue elements</returns>
public T[] ToArray()
{
if (_disposed) return new T[0];
lock (_lockObject)
{
return _queue.ToArray();
}
}
#endregion
#region IDisposable Implementation
/// <summary>Disposes the queue and releases resources</summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>Protected dispose method</summary>
/// <param name="disposing">True if disposing managed resources</param>
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_dataAvailableEvent?.Dispose();
Clear();
}
_disposed = true;
}
}
#endregion
}
}

View File

@@ -0,0 +1,123 @@
using System;
namespace Crestron.SimplSharp
{
/// <summary>Mock eProgramStatusEventType enumeration</summary>
public enum eProgramStatusEventType
{
/// <summary>Program stopping</summary>
eProgramStopping = 0,
/// <summary>Program started</summary>
eProgramStarted = 1,
/// <summary>Program paused</summary>
eProgramPaused = 2,
/// <summary>Program resumed</summary>
eProgramResumed = 3
}
/// <summary>Mock EthernetEventArgs class</summary>
public class EthernetEventArgs : EventArgs
{
/// <summary>Gets the Ethernet adapter that triggered the event</summary>
public int EthernetAdapter { get; private set; }
/// <summary>Gets the link status</summary>
public bool LinkUp { get; private set; }
/// <summary>Gets the speed in Mbps</summary>
public int Speed { get; private set; }
/// <summary>Gets whether it's full duplex</summary>
public bool FullDuplex { get; private set; }
/// <summary>Initializes a new instance of EthernetEventArgs</summary>
/// <param name="adapter">Ethernet adapter number</param>
/// <param name="linkUp">Link status</param>
/// <param name="speed">Speed in Mbps</param>
/// <param name="fullDuplex">Full duplex status</param>
public EthernetEventArgs(int adapter, bool linkUp, int speed, bool fullDuplex)
{
EthernetAdapter = adapter;
LinkUp = linkUp;
Speed = speed;
FullDuplex = fullDuplex;
}
/// <summary>Default constructor</summary>
public EthernetEventArgs() : this(0, false, 0, false)
{
}
}
}
namespace Crestron.SimplSharp.CrestronIO
{
/// <summary>Mock FileInfo class for basic file operations</summary>
public class FileInfo
{
/// <summary>Gets the full path of the file</summary>
public string FullName { get; private set; }
/// <summary>Gets the name of the file</summary>
public string Name { get; private set; }
/// <summary>Gets the directory name</summary>
public string? DirectoryName { get; private set; }
/// <summary>Gets whether the file exists</summary>
public bool Exists { get; private set; }
/// <summary>Gets the length of the file in bytes</summary>
public long Length { get; private set; }
/// <summary>Gets the creation time</summary>
public DateTime CreationTime { get; private set; }
/// <summary>Gets the last write time</summary>
public DateTime LastWriteTime { get; private set; }
/// <summary>Gets the last access time</summary>
public DateTime LastAccessTime { get; private set; }
/// <summary>Initializes a new instance of FileInfo</summary>
/// <param name="fileName">Path to the file</param>
public FileInfo(string fileName)
{
FullName = fileName ?? string.Empty;
Name = System.IO.Path.GetFileName(fileName) ?? string.Empty;
DirectoryName = System.IO.Path.GetDirectoryName(fileName);
// Mock file properties
Exists = !string.IsNullOrEmpty(fileName);
Length = 0;
CreationTime = DateTime.Now;
LastWriteTime = DateTime.Now;
LastAccessTime = DateTime.Now;
}
/// <summary>Deletes the file</summary>
public void Delete()
{
// Mock implementation - just mark as not existing
Exists = false;
}
/// <summary>Creates a text file or opens an existing one for writing</summary>
/// <returns>A mock StreamWriter</returns>
public System.IO.StreamWriter CreateText()
{
var stream = new System.IO.MemoryStream();
return new System.IO.StreamWriter(stream);
}
/// <summary>Opens an existing file for reading</summary>
/// <returns>A mock FileStream</returns>
public System.IO.FileStream OpenRead()
{
// Mock implementation - return a memory stream wrapped as FileStream
return new System.IO.FileStream(FullName, System.IO.FileMode.Open, System.IO.FileAccess.Read);
}
}
}

View File

@@ -0,0 +1,380 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace Crestron.SimplSharp.CrestronWebSocketServer
{
/// <summary>Mock HttpCwsServer class for HTTP web server functionality</summary>
public class HttpCwsServer : IDisposable
{
private HttpListener? _httpListener;
private bool _listening;
private readonly Dictionary<string, IHttpCwsHandler> _routes = new Dictionary<string, IHttpCwsHandler>();
/// <summary>Gets or sets the port number</summary>
public int Port { get; set; }
/// <summary>Gets whether the server is listening</summary>
public bool Listening => _listening;
/// <summary>Initializes a new instance of HttpCwsServer</summary>
public HttpCwsServer()
{
Port = 80;
}
/// <summary>Initializes a new instance of HttpCwsServer</summary>
/// <param name="port">Port number to listen on</param>
public HttpCwsServer(int port)
{
Port = port;
}
/// <summary>Starts the HTTP server</summary>
/// <returns>True if started successfully</returns>
public bool Start()
{
if (_listening)
return true;
try
{
_httpListener = new HttpListener();
_httpListener.Prefixes.Add($"http://+:{Port}/");
_httpListener.Start();
_listening = true;
_ = Task.Run(ProcessRequestsAsync);
return true;
}
catch (Exception)
{
return false;
}
}
/// <summary>Stops the HTTP server</summary>
/// <returns>True if stopped successfully</returns>
public bool Stop()
{
if (!_listening)
return true;
try
{
_listening = false;
_httpListener?.Stop();
_httpListener?.Close();
return true;
}
catch (Exception)
{
return false;
}
}
/// <summary>Adds a route handler</summary>
/// <param name="route">Route path</param>
/// <param name="handler">Handler for the route</param>
public void AddRoute(string route, IHttpCwsHandler handler)
{
_routes[route.ToLowerInvariant()] = handler;
}
/// <summary>Removes a route handler</summary>
/// <param name="route">Route path to remove</param>
public void RemoveRoute(string route)
{
_routes.Remove(route.ToLowerInvariant());
}
private async Task ProcessRequestsAsync()
{
while (_listening && _httpListener != null)
{
try
{
var context = await _httpListener.GetContextAsync();
_ = Task.Run(() => HandleRequest(context));
}
catch (HttpListenerException)
{
// Listener was stopped
break;
}
catch (ObjectDisposedException)
{
// Listener was disposed
break;
}
catch (Exception)
{
// Handle other exceptions
continue;
}
}
}
private void HandleRequest(HttpListenerContext context)
{
try
{
var request = context.Request;
var response = context.Response;
var path = request.Url?.AbsolutePath?.ToLowerInvariant() ?? "/";
var cwsContext = new HttpCwsContext(context);
if (_routes.TryGetValue(path, out var handler))
{
handler.ProcessRequest(cwsContext);
}
else
{
// Default 404 response
response.StatusCode = 404;
var buffer = Encoding.UTF8.GetBytes("Not Found");
response.ContentLength64 = buffer.Length;
response.OutputStream.Write(buffer, 0, buffer.Length);
}
response.Close();
}
catch (Exception)
{
// Handle request processing errors
try
{
context.Response.StatusCode = 500;
context.Response.Close();
}
catch
{
// Ignore errors when closing response
}
}
}
/// <summary>Disposes the HttpCwsServer</summary>
public void Dispose()
{
Stop();
_httpListener?.Close();
}
}
/// <summary>Mock HttpCwsContext class representing an HTTP request/response context</summary>
public class HttpCwsContext
{
private readonly HttpListenerContext _context;
/// <summary>Gets the HTTP request</summary>
public HttpCwsRequest Request { get; }
/// <summary>Gets the HTTP response</summary>
public HttpCwsResponse Response { get; }
/// <summary>Initializes a new instance of HttpCwsContext</summary>
/// <param name="context">Underlying HttpListenerContext</param>
public HttpCwsContext(HttpListenerContext context)
{
_context = context;
Request = new HttpCwsRequest(context.Request);
Response = new HttpCwsResponse(context.Response);
}
}
/// <summary>Mock HttpCwsRequest class representing an HTTP request</summary>
public class HttpCwsRequest
{
private readonly HttpListenerRequest _request;
/// <summary>Gets the HTTP method</summary>
public string HttpMethod => _request.HttpMethod;
/// <summary>Gets the request URL</summary>
public Uri? Url => _request.Url;
/// <summary>Gets the request headers</summary>
public System.Collections.Specialized.NameValueCollection Headers => _request.Headers;
/// <summary>Gets the query string</summary>
public System.Collections.Specialized.NameValueCollection QueryString => _request.QueryString;
/// <summary>Gets the content type</summary>
public string? ContentType => _request.ContentType;
/// <summary>Gets the content length</summary>
public long ContentLength => _request.ContentLength64;
/// <summary>Gets the input stream</summary>
public Stream InputStream => _request.InputStream;
/// <summary>Initializes a new instance of HttpCwsRequest</summary>
/// <param name="request">Underlying HttpListenerRequest</param>
public HttpCwsRequest(HttpListenerRequest request)
{
_request = request;
}
/// <summary>Gets the request body as a string</summary>
/// <returns>Request body content</returns>
public string GetRequestBodyAsString()
{
using var reader = new StreamReader(InputStream, Encoding.UTF8);
return reader.ReadToEnd();
}
}
/// <summary>Mock HttpCwsResponse class representing an HTTP response</summary>
public class HttpCwsResponse
{
private readonly HttpListenerResponse _response;
/// <summary>Gets or sets the status code</summary>
public int StatusCode
{
get => _response.StatusCode;
set => _response.StatusCode = value;
}
/// <summary>Gets or sets the content type</summary>
public string? ContentType
{
get => _response.ContentType;
set => _response.ContentType = value;
}
/// <summary>Gets or sets the content length</summary>
public long ContentLength
{
get => _response.ContentLength64;
set => _response.ContentLength64 = value;
}
/// <summary>Gets the response headers</summary>
public WebHeaderCollection Headers => _response.Headers;
/// <summary>Gets the output stream</summary>
public Stream OutputStream => _response.OutputStream;
/// <summary>Initializes a new instance of HttpCwsResponse</summary>
/// <param name="response">Underlying HttpListenerResponse</param>
public HttpCwsResponse(HttpListenerResponse response)
{
_response = response;
}
/// <summary>Writes a string to the response</summary>
/// <param name="content">Content to write</param>
public void Write(string content)
{
var buffer = Encoding.UTF8.GetBytes(content);
ContentLength = buffer.Length;
OutputStream.Write(buffer, 0, buffer.Length);
}
/// <summary>Writes bytes to the response</summary>
/// <param name="buffer">Buffer to write</param>
/// <param name="offset">Offset in buffer</param>
/// <param name="count">Number of bytes to write</param>
public void Write(byte[] buffer, int offset, int count)
{
OutputStream.Write(buffer, offset, count);
}
}
/// <summary>Interface for HTTP request handlers</summary>
public interface IHttpCwsHandler
{
/// <summary>Processes an HTTP request</summary>
/// <param name="context">HTTP context</param>
void ProcessRequest(HttpCwsContext context);
}
/// <summary>Mock HttpCwsRoute class for route management</summary>
public class HttpCwsRoute
{
/// <summary>Gets or sets the route path</summary>
public string Path { get; set; } = string.Empty;
/// <summary>Gets or sets the HTTP method</summary>
public string Method { get; set; } = "GET";
/// <summary>Gets or sets the route handler</summary>
public IHttpCwsHandler? Handler { get; set; }
/// <summary>Initializes a new instance of HttpCwsRoute</summary>
public HttpCwsRoute()
{
}
/// <summary>Initializes a new instance of HttpCwsRoute</summary>
/// <param name="path">Route path</param>
/// <param name="handler">Route handler</param>
public HttpCwsRoute(string path, IHttpCwsHandler handler)
{
Path = path;
Handler = handler;
}
/// <summary>Initializes a new instance of HttpCwsRoute</summary>
/// <param name="path">Route path</param>
/// <param name="method">HTTP method</param>
/// <param name="handler">Route handler</param>
public HttpCwsRoute(string path, string method, IHttpCwsHandler handler)
{
Path = path;
Method = method;
Handler = handler;
}
}
/// <summary>Mock HTTP CWS route collection</summary>
public class HttpCwsRouteCollection
{
private readonly List<HttpCwsRoute> _routes = new List<HttpCwsRoute>();
/// <summary>Adds a route</summary>
/// <param name="route">Route to add</param>
public void Add(HttpCwsRoute route)
{
_routes.Add(route);
}
/// <summary>Removes a route</summary>
/// <param name="route">Route to remove</param>
public void Remove(HttpCwsRoute route)
{
_routes.Remove(route);
}
/// <summary>Clears all routes</summary>
public void Clear()
{
_routes.Clear();
}
/// <summary>Gets route count</summary>
public int Count => _routes.Count;
}
/// <summary>Mock HTTP CWS request event args</summary>
public class HttpCwsRequestEventArgs : EventArgs
{
/// <summary>Gets the HTTP context</summary>
public HttpCwsContext Context { get; private set; }
/// <summary>Initializes new instance</summary>
/// <param name="context">HTTP context</param>
public HttpCwsRequestEventArgs(HttpCwsContext context)
{
Context = context;
}
}
}

View File

@@ -0,0 +1,50 @@
namespace Crestron.SimplSharp.CrestronSockets
{
/// <summary>Mock EthernetAdapterType enumeration</summary>
public enum EthernetAdapterType
{
/// <summary>Ethernet adapter 1</summary>
EthernetLANAdapter = 0,
/// <summary>Ethernet adapter 2</summary>
EthernetAdapter2 = 1,
/// <summary>Auto-detect adapter</summary>
EthernetAdapterAuto = 2
}
/// <summary>Mock SocketErrorCodes enumeration</summary>
public enum SocketErrorCodes
{
/// <summary>Operation completed successfully</summary>
SOCKET_OK = 0,
/// <summary>Socket operation pending</summary>
SOCKET_OPERATION_PENDING = 1,
/// <summary>Socket not connected</summary>
SOCKET_NOT_CONNECTED = 2,
/// <summary>Connection failed</summary>
SOCKET_CONNECTION_FAILED = 3,
/// <summary>Invalid client index</summary>
SOCKET_INVALID_CLIENT_INDEX = 4,
/// <summary>DNS lookup failed</summary>
SOCKET_DNS_LOOKUP_FAILED = 5,
/// <summary>Invalid address</summary>
SOCKET_INVALID_ADDRESS = 6,
/// <summary>Connection timed out</summary>
SOCKET_CONNECTION_TIMEOUT = 7,
/// <summary>Send data failed</summary>
SOCKET_SEND_DATA_FAILED = 8,
/// <summary>Receive data failed</summary>
SOCKET_RECEIVE_DATA_FAILED = 9,
/// <summary>Socket closed</summary>
SOCKET_CLOSED = 10,
/// <summary>Socket disconnected</summary>
SOCKET_DISCONNECTED = 11,
/// <summary>Max connections reached</summary>
SOCKET_MAX_CONNECTIONS_REACHED = 12,
/// <summary>Permission denied</summary>
SOCKET_PERMISSION_DENIED = 13,
/// <summary>Address already in use</summary>
SOCKET_ADDRESS_IN_USE = 14,
/// <summary>Invalid parameter</summary>
SOCKET_INVALID_PARAMETER = 15
}
}

View File

@@ -0,0 +1,273 @@
using System;
namespace Crestron.SimplSharp.CrestronSockets
{
/// <summary>Mock SocketStatus enumeration</summary>
public enum SocketStatus
{
/// <summary>Socket is connecting</summary>
SOCKET_STATUS_WAITING = 1,
/// <summary>Socket is connected</summary>
SOCKET_STATUS_CONNECTED = 2,
/// <summary>Socket is not connected</summary>
SOCKET_STATUS_NOT_CONNECTED = 3,
/// <summary>Connection broken</summary>
SOCKET_STATUS_BROKEN_REMOTELY = 4,
/// <summary>Connection broken locally</summary>
SOCKET_STATUS_BROKEN_LOCALLY = 5,
/// <summary>DNS resolution failed</summary>
SOCKET_STATUS_DNS_RESOLUTION_FAILED = 6,
/// <summary>Connection failed</summary>
SOCKET_STATUS_CONNECT_FAILED = 7,
/// <summary>Socket error</summary>
SOCKET_STATUS_SOCKET_ERROR = 8,
/// <summary>Secure connection failed</summary>
SOCKET_STATUS_SSL_FAILED = 9
}
/// <summary>Mock ServerState enumeration</summary>
public enum ServerState
{
/// <summary>Server is not listening</summary>
SERVER_NOT_LISTENING = 0,
/// <summary>Server is listening</summary>
SERVER_LISTENING = 1
}
/// <summary>Mock event handler for TCP client status changes</summary>
/// <param name="client">The TCP client</param>
/// <param name="clientSocketStatus">The socket status</param>
public delegate void TCPClientSocketStatusChangeEventHandler(TCPClient client, SocketStatus clientSocketStatus);
/// <summary>Mock event handler for receiving TCP client data</summary>
/// <param name="client">The TCP client</param>
/// <param name="numberOfBytesReceived">Number of bytes received</param>
public delegate void TCPClientReceiveEventHandler(TCPClient client, int numberOfBytesReceived);
/// <summary>
/// Mock implementation of Crestron TCPClient for testing purposes
/// Provides the same public API surface as the real TCPClient
/// </summary>
public class TCPClient : IDisposable
{
#region Events
/// <summary>Event fired when socket status changes</summary>
public event TCPClientSocketStatusChangeEventHandler? SocketStatusChange;
/// <summary>Event fired when data is received</summary>
public event TCPClientReceiveEventHandler? DataReceived;
#endregion
#region Properties
/// <summary>Gets the client socket status</summary>
public SocketStatus ClientStatus { get; private set; } = SocketStatus.SOCKET_STATUS_NOT_CONNECTED;
/// <summary>Gets or sets the address to connect to</summary>
public string AddressToConnectTo { get; set; } = string.Empty;
/// <summary>Gets or sets the port number to connect to</summary>
public int PortNumber { get; set; } = 0;
/// <summary>Gets the number of bytes received in the incoming data buffer</summary>
public int IncomingDataBufferSize { get; private set; } = 0;
/// <summary>Gets or sets the socket send timeout in milliseconds</summary>
public int SocketSendTimeout { get; set; } = 30000;
/// <summary>Gets or sets the socket receive timeout in milliseconds</summary>
public int SocketReceiveTimeout { get; set; } = 30000;
/// <summary>Gets or sets whether to keep the connection alive</summary>
public bool KeepAlive { get; set; } = false;
/// <summary>Gets or sets whether Nagle algorithm is enabled</summary>
public bool EnableNagle { get; set; } = true;
/// <summary>Gets the number of bytes available to read</summary>
public int BytesAvailable => IncomingDataBufferSize;
#endregion
#region Constructor
/// <summary>Initializes a new instance of the TCPClient class</summary>
/// <param name="addressToConnectTo">IP address to connect to</param>
/// <param name="portNumber">Port number to connect to</param>
/// <param name="bufferSize">Size of the receive buffer</param>
public TCPClient(string addressToConnectTo, int portNumber, int bufferSize)
{
AddressToConnectTo = addressToConnectTo;
PortNumber = portNumber;
_bufferSize = bufferSize;
_receiveBuffer = new byte[bufferSize];
}
/// <summary>Initializes a new instance of the TCPClient class</summary>
/// <param name="addressToConnectTo">IP address to connect to</param>
/// <param name="portNumber">Port number to connect to</param>
/// <param name="bufferSize">Size of the receive buffer</param>
/// <param name="ethernetAdapterToBindTo">Ethernet adapter to bind to</param>
public TCPClient(string addressToConnectTo, int portNumber, int bufferSize, EthernetAdapterType ethernetAdapterToBindTo)
{
AddressToConnectTo = addressToConnectTo;
PortNumber = portNumber;
_bufferSize = bufferSize;
_receiveBuffer = new byte[bufferSize];
// Note: EthernetAdapterType is ignored in mock implementation
}
#endregion
#region Private Fields
private readonly int _bufferSize;
private readonly byte[] _receiveBuffer;
private bool _disposed = false;
#endregion
#region Public Methods
/// <summary>Connects to the remote endpoint asynchronously</summary>
/// <returns>Status of the connection attempt</returns>
public SocketStatus ConnectToServerAsync()
{
if (_disposed) return SocketStatus.SOCKET_STATUS_SOCKET_ERROR;
// Mock connection - simulate successful connection
ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED;
SocketStatusChange?.Invoke(this, ClientStatus);
return ClientStatus;
}
/// <summary>Connects to the remote endpoint</summary>
/// <returns>Status of the connection attempt</returns>
public SocketStatus ConnectToServer()
{
return ConnectToServerAsync();
}
/// <summary>Disconnects from the remote endpoint</summary>
/// <returns>Status of the disconnection</returns>
public SocketStatus DisconnectFromServer()
{
if (_disposed) return SocketStatus.SOCKET_STATUS_SOCKET_ERROR;
ClientStatus = SocketStatus.SOCKET_STATUS_NOT_CONNECTED;
SocketStatusChange?.Invoke(this, ClientStatus);
return ClientStatus;
}
/// <summary>Sends data to the connected server</summary>
/// <param name="dataToSend">Data to send as a string</param>
/// <returns>Number of bytes sent, or -1 on error</returns>
public int SendData(string dataToSend)
{
if (_disposed || string.IsNullOrEmpty(dataToSend)) return -1;
if (ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED) return -1;
// Mock send - return the length of the string as bytes sent
return System.Text.Encoding.UTF8.GetByteCount(dataToSend);
}
/// <summary>Sends data to the connected server</summary>
/// <param name="dataToSend">Data to send as byte array</param>
/// <param name="lengthToSend">Number of bytes to send</param>
/// <returns>Number of bytes sent, or -1 on error</returns>
public int SendData(byte[] dataToSend, int lengthToSend)
{
if (_disposed || dataToSend == null || lengthToSend <= 0) return -1;
if (ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED) return -1;
if (lengthToSend > dataToSend.Length) return -1;
// Mock send - return the requested length
return lengthToSend;
}
/// <summary>Receives data from the server</summary>
/// <param name="buffer">Buffer to receive data into</param>
/// <param name="bufferIndex">Starting index in the buffer</param>
/// <param name="lengthToReceive">Maximum number of bytes to receive</param>
/// <returns>Number of bytes received, or -1 on error</returns>
public int ReceiveData(byte[] buffer, int bufferIndex, int lengthToReceive)
{
if (_disposed || buffer == null || bufferIndex < 0 || lengthToReceive <= 0) return -1;
if (ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED) return -1;
if (bufferIndex + lengthToReceive > buffer.Length) return -1;
// Mock receive - simulate no data available for now
return 0;
}
/// <summary>Receives data from the server as a string</summary>
/// <param name="numberOfBytesToReceive">Maximum number of bytes to receive</param>
/// <returns>Received data as string, or empty string on error</returns>
public string ReceiveData(int numberOfBytesToReceive)
{
if (_disposed || numberOfBytesToReceive <= 0) return string.Empty;
if (ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED) return string.Empty;
// Mock receive - return empty string (no data available)
return string.Empty;
}
/// <summary>Simulates receiving data (for testing purposes)</summary>
/// <param name="data">Data to simulate receiving</param>
public void SimulateDataReceived(string data)
{
if (_disposed || string.IsNullOrEmpty(data)) return;
var bytes = System.Text.Encoding.UTF8.GetBytes(data);
var bytesToCopy = Math.Min(bytes.Length, _receiveBuffer.Length);
Array.Copy(bytes, _receiveBuffer, bytesToCopy);
IncomingDataBufferSize = bytesToCopy;
DataReceived?.Invoke(this, bytesToCopy);
}
/// <summary>Simulates a socket status change (for testing purposes)</summary>
/// <param name="newStatus">New socket status</param>
public void SimulateStatusChange(SocketStatus newStatus)
{
if (_disposed) return;
ClientStatus = newStatus;
SocketStatusChange?.Invoke(this, newStatus);
}
#endregion
#region IDisposable Implementation
/// <summary>Disposes the TCP client and releases resources</summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>Protected dispose method</summary>
/// <param name="disposing">True if disposing managed resources</param>
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// Disconnect if still connected
if (ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
{
DisconnectFromServer();
}
}
_disposed = true;
}
}
#endregion
}
}

View File

@@ -0,0 +1,434 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Crestron.SimplSharp.CrestronSockets
{
/// <summary>Mock TCPServer class for server-side TCP operations</summary>
public class TCPServer : IDisposable
{
private TcpListener? _listener;
private readonly List<TCPClientConnection> _clients = new List<TCPClientConnection>();
private bool _listening;
private readonly object _lockObject = new object();
/// <summary>Event fired when waiting for connections</summary>
public event TCPServerWaitingForConnectionsEventHandler? WaitingForConnections;
/// <summary>Event fired when a client connects</summary>
public event TCPServerClientConnectEventHandler? ClientConnected;
/// <summary>Event fired when a client disconnects</summary>
public event TCPServerClientDisconnectEventHandler? ClientDisconnected;
/// <summary>Event fired when data is received from a client</summary>
public event TCPServerReceiveDataEventHandler? ReceivedData;
/// <summary>Gets the server state</summary>
public SocketServerState State { get; private set; } = SocketServerState.SERVER_NOT_LISTENING;
/// <summary>Gets the port number</summary>
public int PortNumber { get; private set; }
/// <summary>Gets the maximum number of clients</summary>
public int MaxNumberOfClientSupported { get; private set; }
/// <summary>Gets the number of connected clients</summary>
public int NumberOfClientsConnected
{
get
{
lock (_lockObject)
{
return _clients.Count;
}
}
}
/// <summary>Initializes a new instance of TCPServer</summary>
/// <param name="ipAddress">IP address to bind to</param>
/// <param name="portNumber">Port number to listen on</param>
/// <param name="bufferSize">Buffer size for data reception</param>
/// <param name="ethernetAdapterToBindTo">Ethernet adapter to bind to</param>
/// <param name="maxNumberOfClientSupported">Maximum number of clients</param>
public TCPServer(string ipAddress, int portNumber, int bufferSize, EthernetAdapterType ethernetAdapterToBindTo, int maxNumberOfClientSupported)
{
PortNumber = portNumber;
MaxNumberOfClientSupported = maxNumberOfClientSupported;
}
/// <summary>Initializes a new instance of TCPServer</summary>
/// <param name="portNumber">Port number to listen on</param>
/// <param name="bufferSize">Buffer size for data reception</param>
/// <param name="maxNumberOfClientSupported">Maximum number of clients</param>
public TCPServer(int portNumber, int bufferSize, int maxNumberOfClientSupported)
{
PortNumber = portNumber;
MaxNumberOfClientSupported = maxNumberOfClientSupported;
}
/// <summary>Starts listening for client connections</summary>
/// <returns>SocketErrorCodes indicating success or failure</returns>
public SocketErrorCodes WaitForConnectionAsync()
{
if (_listening)
return SocketErrorCodes.SOCKET_OPERATION_PENDING;
try
{
_listener = new TcpListener(IPAddress.Any, PortNumber);
_listener.Start();
_listening = true;
State = SocketServerState.SERVER_LISTENING;
WaitingForConnections?.Invoke(this, new TCPServerWaitingForConnectionsEventArgs(0));
_ = Task.Run(AcceptClientsAsync);
return SocketErrorCodes.SOCKET_OK;
}
catch (Exception)
{
State = SocketServerState.SERVER_NOT_LISTENING;
return SocketErrorCodes.SOCKET_CONNECTION_FAILED;
}
}
/// <summary>Stops listening for connections</summary>
/// <returns>SocketErrorCodes indicating success or failure</returns>
public SocketErrorCodes Stop()
{
if (!_listening)
return SocketErrorCodes.SOCKET_NOT_CONNECTED;
try
{
_listening = false;
_listener?.Stop();
State = SocketServerState.SERVER_NOT_LISTENING;
lock (_lockObject)
{
foreach (var client in _clients)
{
client.Disconnect();
}
_clients.Clear();
}
return SocketErrorCodes.SOCKET_OK;
}
catch (Exception)
{
return SocketErrorCodes.SOCKET_CONNECTION_FAILED;
}
}
/// <summary>Sends data to a specific client</summary>
/// <param name="data">Data to send</param>
/// <param name="dataLength">Length of data</param>
/// <param name="clientIndex">Index of client to send to</param>
/// <returns>SocketErrorCodes indicating success or failure</returns>
public SocketErrorCodes SendData(byte[] data, int dataLength, uint clientIndex)
{
lock (_lockObject)
{
if (clientIndex >= _clients.Count)
return SocketErrorCodes.SOCKET_INVALID_CLIENT_INDEX;
return _clients[(int)clientIndex].SendData(data, dataLength);
}
}
/// <summary>Sends data to all connected clients</summary>
/// <param name="data">Data to send</param>
/// <param name="dataLength">Length of data</param>
/// <returns>SocketErrorCodes indicating success or failure</returns>
public SocketErrorCodes SendDataToAll(byte[] data, int dataLength)
{
lock (_lockObject)
{
var result = SocketErrorCodes.SOCKET_OK;
foreach (var client in _clients)
{
var sendResult = client.SendData(data, dataLength);
if (sendResult != SocketErrorCodes.SOCKET_OK)
result = sendResult;
}
return result;
}
}
/// <summary>Disconnects a specific client</summary>
/// <param name="clientIndex">Index of client to disconnect</param>
/// <returns>SocketErrorCodes indicating success or failure</returns>
public SocketErrorCodes Disconnect(uint clientIndex)
{
lock (_lockObject)
{
if (clientIndex >= _clients.Count)
return SocketErrorCodes.SOCKET_INVALID_CLIENT_INDEX;
var client = _clients[(int)clientIndex];
client.Disconnect();
_clients.RemoveAt((int)clientIndex);
ClientDisconnected?.Invoke(this, new TCPServerClientDisconnectEventArgs((uint)clientIndex));
return SocketErrorCodes.SOCKET_OK;
}
}
/// <summary>Gets the IP address of a connected client</summary>
/// <param name="clientIndex">Index of client</param>
/// <returns>IP address as string</returns>
public string GetAddressServerAcceptedConnectionFromForSpecificClient(uint clientIndex)
{
lock (_lockObject)
{
if (clientIndex >= _clients.Count)
return string.Empty;
return _clients[(int)clientIndex].ClientIPAddress;
}
}
private async Task AcceptClientsAsync()
{
while (_listening && _listener != null)
{
try
{
var tcpClient = await _listener.AcceptTcpClientAsync();
lock (_lockObject)
{
if (_clients.Count >= MaxNumberOfClientSupported)
{
tcpClient.Close();
continue;
}
var clientConnection = new TCPClientConnection(tcpClient, (uint)_clients.Count);
clientConnection.DataReceived += OnClientDataReceived;
clientConnection.Disconnected += OnClientDisconnected;
_clients.Add(clientConnection);
ClientConnected?.Invoke(this, new TCPServerClientConnectEventArgs((uint)(_clients.Count - 1)));
}
}
catch (ObjectDisposedException)
{
// Server was stopped
break;
}
catch (Exception)
{
// Handle other exceptions
continue;
}
}
}
private void OnClientDataReceived(object? sender, TCPClientDataEventArgs e)
{
if (sender is TCPClientConnection client)
{
var args = new TCPServerReceiveDataEventArgs(e.Data, e.DataLength, client.ClientIndex);
ReceivedData?.Invoke(this, args);
}
}
private void OnClientDisconnected(object? sender, EventArgs e)
{
if (sender is TCPClientConnection client)
{
lock (_lockObject)
{
var index = _clients.IndexOf(client);
if (index >= 0)
{
_clients.RemoveAt(index);
ClientDisconnected?.Invoke(this, new TCPServerClientDisconnectEventArgs(client.ClientIndex));
}
}
}
}
/// <summary>Disposes the TCPServer</summary>
public void Dispose()
{
Stop();
_listener?.Stop();
}
}
/// <summary>Mock SecureTCPServer class for secure server-side TCP operations</summary>
public class SecureTCPServer : TCPServer
{
/// <summary>Initializes a new instance of SecureTCPServer</summary>
/// <param name="ipAddress">IP address to bind to</param>
/// <param name="portNumber">Port number to listen on</param>
/// <param name="bufferSize">Buffer size for data reception</param>
/// <param name="ethernetAdapterToBindTo">Ethernet adapter to bind to</param>
/// <param name="maxNumberOfClientSupported">Maximum number of clients</param>
public SecureTCPServer(string ipAddress, int portNumber, int bufferSize, EthernetAdapterType ethernetAdapterToBindTo, int maxNumberOfClientSupported)
: base(ipAddress, portNumber, bufferSize, ethernetAdapterToBindTo, maxNumberOfClientSupported)
{
}
/// <summary>Initializes a new instance of SecureTCPServer</summary>
/// <param name="portNumber">Port number to listen on</param>
/// <param name="bufferSize">Buffer size for data reception</param>
/// <param name="maxNumberOfClientSupported">Maximum number of clients</param>
public SecureTCPServer(int portNumber, int bufferSize, int maxNumberOfClientSupported)
: base(portNumber, bufferSize, maxNumberOfClientSupported)
{
}
}
/// <summary>Internal class representing a client connection</summary>
internal class TCPClientConnection
{
private readonly TcpClient _tcpClient;
private readonly NetworkStream _stream;
private readonly byte[] _buffer = new byte[4096];
private bool _connected = true;
public uint ClientIndex { get; }
public string ClientIPAddress { get; }
public event EventHandler<TCPClientDataEventArgs>? DataReceived;
public event EventHandler? Disconnected;
public TCPClientConnection(TcpClient tcpClient, uint clientIndex)
{
_tcpClient = tcpClient;
ClientIndex = clientIndex;
_stream = tcpClient.GetStream();
var endpoint = tcpClient.Client.RemoteEndPoint as IPEndPoint;
ClientIPAddress = endpoint?.Address.ToString() ?? "Unknown";
_ = Task.Run(ReceiveDataAsync);
}
public SocketErrorCodes SendData(byte[] data, int dataLength)
{
if (!_connected)
return SocketErrorCodes.SOCKET_NOT_CONNECTED;
try
{
_stream.Write(data, 0, dataLength);
return SocketErrorCodes.SOCKET_OK;
}
catch (Exception)
{
Disconnect();
return SocketErrorCodes.SOCKET_CONNECTION_FAILED;
}
}
public void Disconnect()
{
if (!_connected)
return;
_connected = false;
_stream?.Close();
_tcpClient?.Close();
Disconnected?.Invoke(this, EventArgs.Empty);
}
private async Task ReceiveDataAsync()
{
while (_connected)
{
try
{
var bytesRead = await _stream.ReadAsync(_buffer, 0, _buffer.Length);
if (bytesRead == 0)
{
Disconnect();
break;
}
var data = new byte[bytesRead];
Array.Copy(_buffer, data, bytesRead);
DataReceived?.Invoke(this, new TCPClientDataEventArgs(data, bytesRead));
}
catch (Exception)
{
Disconnect();
break;
}
}
}
}
/// <summary>Event args for TCP client data</summary>
internal class TCPClientDataEventArgs : EventArgs
{
public byte[] Data { get; }
public int DataLength { get; }
public TCPClientDataEventArgs(byte[] data, int dataLength)
{
Data = data;
DataLength = dataLength;
}
}
/// <summary>Server state enumeration</summary>
public enum SocketServerState
{
/// <summary>Server is not listening</summary>
SERVER_NOT_LISTENING = 0,
/// <summary>Server is listening for connections</summary>
SERVER_LISTENING = 1
}
// Event handler delegates
public delegate void TCPServerWaitingForConnectionsEventHandler(TCPServer server, TCPServerWaitingForConnectionsEventArgs args);
public delegate void TCPServerClientConnectEventHandler(TCPServer server, TCPServerClientConnectEventArgs args);
public delegate void TCPServerClientDisconnectEventHandler(TCPServer server, TCPServerClientDisconnectEventArgs args);
public delegate void TCPServerReceiveDataEventHandler(TCPServer server, TCPServerReceiveDataEventArgs args);
// Event argument classes
public class TCPServerWaitingForConnectionsEventArgs : EventArgs
{
public int ErrorCode { get; }
public TCPServerWaitingForConnectionsEventArgs(int errorCode) { ErrorCode = errorCode; }
}
public class TCPServerClientConnectEventArgs : EventArgs
{
public uint ClientIndex { get; }
public TCPServerClientConnectEventArgs(uint clientIndex) { ClientIndex = clientIndex; }
}
public class TCPServerClientDisconnectEventArgs : EventArgs
{
public uint ClientIndex { get; }
public TCPServerClientDisconnectEventArgs(uint clientIndex) { ClientIndex = clientIndex; }
}
public class TCPServerReceiveDataEventArgs : EventArgs
{
public byte[] Data { get; }
public int DataLength { get; }
public uint ClientIndex { get; }
public TCPServerReceiveDataEventArgs(byte[] data, int dataLength, uint clientIndex)
{
Data = data;
DataLength = dataLength;
ClientIndex = clientIndex;
}
}
}

View File

@@ -0,0 +1,248 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
namespace Crestron.SimplSharp.CrestronSockets
{
/// <summary>Mock UDPServer class for UDP communication</summary>
public class UDPServer : IDisposable
{
private UdpClient? _udpClient;
private bool _listening;
private readonly object _lockObject = new object();
private CancellationTokenSource? _cancellationTokenSource;
/// <summary>Event fired when data is received</summary>
public event UDPServerReceiveDataEventHandler? ReceivedData;
/// <summary>Gets the server state</summary>
public SocketServerState State { get; private set; } = SocketServerState.SERVER_NOT_LISTENING;
/// <summary>Gets the port number</summary>
public int PortNumber { get; private set; }
/// <summary>Gets the buffer size</summary>
public int BufferSize { get; private set; }
/// <summary>Initializes a new instance of UDPServer</summary>
/// <param name="ipAddress">IP address to bind to</param>
/// <param name="portNumber">Port number to listen on</param>
/// <param name="bufferSize">Buffer size for data reception</param>
/// <param name="ethernetAdapterToBindTo">Ethernet adapter to bind to</param>
public UDPServer(string ipAddress, int portNumber, int bufferSize, EthernetAdapterType ethernetAdapterToBindTo)
{
PortNumber = portNumber;
BufferSize = bufferSize;
}
/// <summary>Initializes a new instance of UDPServer</summary>
/// <param name="portNumber">Port number to listen on</param>
/// <param name="bufferSize">Buffer size for data reception</param>
public UDPServer(int portNumber, int bufferSize)
{
PortNumber = portNumber;
BufferSize = bufferSize;
}
/// <summary>Starts listening for UDP packets</summary>
/// <returns>SocketErrorCodes indicating success or failure</returns>
public SocketErrorCodes EnableUDPServer()
{
if (_listening)
return SocketErrorCodes.SOCKET_OPERATION_PENDING;
try
{
_udpClient = new UdpClient(PortNumber);
_listening = true;
State = SocketServerState.SERVER_LISTENING;
_cancellationTokenSource = new CancellationTokenSource();
_ = Task.Run(() => ReceiveDataAsync(_cancellationTokenSource.Token));
return SocketErrorCodes.SOCKET_OK;
}
catch (Exception)
{
State = SocketServerState.SERVER_NOT_LISTENING;
return SocketErrorCodes.SOCKET_CONNECTION_FAILED;
}
}
/// <summary>Stops listening for UDP packets</summary>
/// <returns>SocketErrorCodes indicating success or failure</returns>
public SocketErrorCodes DisableUDPServer()
{
if (!_listening)
return SocketErrorCodes.SOCKET_NOT_CONNECTED;
try
{
_listening = false;
_cancellationTokenSource?.Cancel();
_udpClient?.Close();
State = SocketServerState.SERVER_NOT_LISTENING;
return SocketErrorCodes.SOCKET_OK;
}
catch (Exception)
{
return SocketErrorCodes.SOCKET_CONNECTION_FAILED;
}
}
/// <summary>Sends data to a specific endpoint</summary>
/// <param name="data">Data to send</param>
/// <param name="dataLength">Length of data</param>
/// <param name="ipAddress">Target IP address</param>
/// <param name="portNumber">Target port number</param>
/// <returns>SocketErrorCodes indicating success or failure</returns>
public SocketErrorCodes SendData(byte[] data, int dataLength, string ipAddress, int portNumber)
{
if (!_listening || _udpClient == null)
return SocketErrorCodes.SOCKET_NOT_CONNECTED;
try
{
var endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), portNumber);
_udpClient.Send(data, dataLength, endpoint);
return SocketErrorCodes.SOCKET_OK;
}
catch (Exception)
{
return SocketErrorCodes.SOCKET_CONNECTION_FAILED;
}
}
/// <summary>Sends data to a specific endpoint</summary>
/// <param name="data">Data to send</param>
/// <param name="dataLength">Length of data</param>
/// <param name="endpoint">Target endpoint</param>
/// <returns>SocketErrorCodes indicating success or failure</returns>
public SocketErrorCodes SendData(byte[] data, int dataLength, IPEndPoint endpoint)
{
if (!_listening || _udpClient == null)
return SocketErrorCodes.SOCKET_NOT_CONNECTED;
try
{
_udpClient.Send(data, dataLength, endpoint);
return SocketErrorCodes.SOCKET_OK;
}
catch (Exception)
{
return SocketErrorCodes.SOCKET_CONNECTION_FAILED;
}
}
private async Task ReceiveDataAsync(CancellationToken cancellationToken)
{
while (_listening && _udpClient != null && !cancellationToken.IsCancellationRequested)
{
try
{
var result = await _udpClient.ReceiveAsync();
var args = new UDPServerReceiveDataEventArgs(
result.Buffer,
result.Buffer.Length,
result.RemoteEndPoint.Address.ToString(),
result.RemoteEndPoint.Port);
ReceivedData?.Invoke(this, args);
}
catch (ObjectDisposedException)
{
// UDP client was disposed
break;
}
catch (Exception)
{
// Handle other exceptions
continue;
}
}
}
/// <summary>Disposes the UDPServer</summary>
public void Dispose()
{
DisableUDPServer();
_cancellationTokenSource?.Dispose();
_udpClient?.Dispose();
}
}
/// <summary>Mock SecureTCPClient class for secure TCP client operations</summary>
public class SecureTCPClient : TCPClient
{
/// <summary>Initializes a new instance of SecureTCPClient</summary>
/// <param name="ipAddress">Server IP address</param>
/// <param name="portNumber">Server port number</param>
/// <param name="bufferSize">Buffer size for data reception</param>
/// <param name="ethernetAdapterToBindTo">Ethernet adapter to bind to</param>
public SecureTCPClient(string ipAddress, int portNumber, int bufferSize, EthernetAdapterType ethernetAdapterToBindTo)
: base(ipAddress, portNumber, bufferSize, ethernetAdapterToBindTo)
{
}
/// <summary>Initializes a new instance of SecureTCPClient</summary>
/// <param name="ipAddress">Server IP address</param>
/// <param name="portNumber">Server port number</param>
/// <param name="bufferSize">Buffer size for data reception</param>
public SecureTCPClient(string ipAddress, int portNumber, int bufferSize)
: base(ipAddress, portNumber, bufferSize)
{
}
/// <summary>Sets the SSL/TLS settings (mock implementation)</summary>
/// <param name="context">SSL context</param>
public void SetSSLContext(object context)
{
// Mock implementation - does nothing in test environment
}
/// <summary>Validates server certificate (mock implementation)</summary>
/// <param name="certificate">Server certificate</param>
/// <returns>Always returns true in mock implementation</returns>
public bool ValidateServerCertificate(object certificate)
{
// Mock implementation - always accept certificate
return true;
}
}
// Event handler delegates for UDP
public delegate void UDPServerReceiveDataEventHandler(UDPServer server, UDPServerReceiveDataEventArgs args);
// Event argument classes for UDP
public class UDPServerReceiveDataEventArgs : EventArgs
{
/// <summary>Gets the received data</summary>
public byte[] Data { get; }
/// <summary>Gets the length of received data</summary>
public int DataLength { get; }
/// <summary>Gets the sender's IP address</summary>
public string IPAddress { get; }
/// <summary>Gets the sender's port number</summary>
public int Port { get; }
/// <summary>Initializes a new instance of UDPServerReceiveDataEventArgs</summary>
/// <param name="data">Received data</param>
/// <param name="dataLength">Length of received data</param>
/// <param name="ipAddress">Sender's IP address</param>
/// <param name="port">Sender's port number</param>
public UDPServerReceiveDataEventArgs(byte[] data, int dataLength, string ipAddress, int port)
{
Data = data;
DataLength = dataLength;
IPAddress = ipAddress;
Port = port;
}
}
}

View File

@@ -0,0 +1,283 @@
using System;
using System.Collections.Generic;
using Crestron.SimplSharp.CrestronWebSocketServer;
namespace Crestron.SimplSharp.Net
{
/// <summary>HTTP request types</summary>
public enum RequestType
{
/// <summary>GET request</summary>
Get = 0,
/// <summary>POST request</summary>
Post = 1,
/// <summary>PUT request</summary>
Put = 2,
/// <summary>DELETE request</summary>
Delete = 3,
/// <summary>HEAD request</summary>
Head = 4,
/// <summary>OPTIONS request</summary>
Options = 5,
/// <summary>PATCH request</summary>
Patch = 6
}
/// <summary>Mock Http namespace for Crestron HTTP client functionality</summary>
public static class Http
{
/// <summary>HTTP request types</summary>
public enum RequestType
{
/// <summary>GET request</summary>
Get = 0,
/// <summary>POST request</summary>
Post = 1,
/// <summary>PUT request</summary>
Put = 2,
/// <summary>DELETE request</summary>
Delete = 3,
/// <summary>HEAD request</summary>
Head = 4,
/// <summary>OPTIONS request</summary>
Options = 5,
/// <summary>PATCH request</summary>
Patch = 6
}
/// <summary>Mock HTTP client</summary>
public static class HttpClient
{
/// <summary>Dispatch HTTP request</summary>
/// <param name="request">HTTP request</param>
/// <param name="callback">Callback for response</param>
public static void Dispatch(HttpClientRequest request, Action<HttpClientResponse> callback)
{
// Mock implementation - invoke callback with empty response
var response = new HttpClientResponse();
callback?.Invoke(response);
}
}
}
/// <summary>Mock HTTP client request</summary>
public class HttpClientRequest
{
/// <summary>Gets or sets the URL</summary>
public string Url { get; set; } = string.Empty;
/// <summary>Gets or sets the HTTP method</summary>
public string RequestType { get; set; } = "GET";
/// <summary>Gets or sets the content data</summary>
public string ContentString { get; set; } = string.Empty;
/// <summary>Gets the headers collection</summary>
public Dictionary<string, string> Header { get; } = new Dictionary<string, string>();
}
/// <summary>Mock HTTP client response</summary>
public class HttpClientResponse
{
/// <summary>Gets the response code</summary>
public int Code { get; set; } = 200;
/// <summary>Gets the response content</summary>
public string ContentString { get; set; } = string.Empty;
/// <summary>Gets the response data as bytes</summary>
public byte[] ContentBytes { get; set; } = Array.Empty<byte>();
/// <summary>Gets the headers collection</summary>
public Dictionary<string, string> Header { get; } = new Dictionary<string, string>();
}
/// <summary>Mock HTTPS namespace</summary>
public static class Https
{
/// <summary>HTTPS request types</summary>
public enum RequestType
{
/// <summary>GET request</summary>
Get = 0,
/// <summary>POST request</summary>
Post = 1,
/// <summary>PUT request</summary>
Put = 2,
/// <summary>DELETE request</summary>
Delete = 3,
/// <summary>HEAD request</summary>
Head = 4,
/// <summary>OPTIONS request</summary>
Options = 5,
/// <summary>PATCH request</summary>
Patch = 6
}
/// <summary>Mock HTTPS client</summary>
public static class HttpsClient
{
/// <summary>Dispatch HTTPS request</summary>
/// <param name="request">HTTPS request</param>
/// <param name="callback">Callback for response</param>
public static void Dispatch(HttpsClientRequest request, Action<HttpsClientResponse> callback)
{
// Mock implementation - invoke callback with empty response
var response = new HttpsClientResponse();
callback?.Invoke(response);
}
}
}
/// <summary>Mock HTTPS client request</summary>
public class HttpsClientRequest
{
/// <summary>Gets or sets the URL</summary>
public string Url { get; set; } = string.Empty;
/// <summary>Gets or sets the HTTP method</summary>
public string RequestType { get; set; } = "GET";
/// <summary>Gets or sets the content data</summary>
public string ContentString { get; set; } = string.Empty;
/// <summary>Gets the headers collection</summary>
public Dictionary<string, string> Header { get; } = new Dictionary<string, string>();
}
/// <summary>Mock HTTPS client response</summary>
public class HttpsClientResponse
{
/// <summary>Gets the response code</summary>
public int Code { get; set; } = 200;
/// <summary>Gets the response content</summary>
public string ContentString { get; set; } = string.Empty;
/// <summary>Gets the response data as bytes</summary>
public byte[] ContentBytes { get; set; } = Array.Empty<byte>();
/// <summary>Gets the headers collection</summary>
public Dictionary<string, string> Header { get; } = new Dictionary<string, string>();
}
}
namespace Crestron.SimplSharp.WebScripting
{
/// <summary>Mock HTTP CWS route collection</summary>
public class HttpCwsRouteCollection
{
private readonly List<HttpCwsRoute> _routes = new List<HttpCwsRoute>();
/// <summary>Adds a route</summary>
/// <param name="route">Route to add</param>
public void Add(HttpCwsRoute route)
{
_routes.Add(route);
}
/// <summary>Removes a route</summary>
/// <param name="route">Route to remove</param>
public void Remove(HttpCwsRoute route)
{
_routes.Remove(route);
}
/// <summary>Clears all routes</summary>
public void Clear()
{
_routes.Clear();
}
/// <summary>Gets route count</summary>
public int Count => _routes.Count;
}
}
namespace Crestron.SimplSharp.CrestronLogger
{
/// <summary>Mock Crestron logger</summary>
public static class CrestronLogger
{
/// <summary>Mock log levels</summary>
public enum LogLevel
{
/// <summary>Debug level</summary>
Debug = 0,
/// <summary>Info level</summary>
Info = 1,
/// <summary>Warning level</summary>
Warning = 2,
/// <summary>Error level</summary>
Error = 3
}
/// <summary>Mock logger interface</summary>
public interface ILogger
{
/// <summary>Logs a message</summary>
/// <param name="level">Log level</param>
/// <param name="message">Message to log</param>
void Log(LogLevel level, string message);
}
/// <summary>Gets a logger by name</summary>
/// <param name="name">Logger name</param>
/// <returns>Mock logger instance</returns>
public static ILogger GetLogger(string name)
{
return new MockLogger();
}
private class MockLogger : ILogger
{
public void Log(LogLevel level, string message)
{
// Mock implementation - do nothing in test environment
}
}
}
}
namespace Crestron.SimplSharp.CrestronDataStore
{
/// <summary>Mock Crestron data store</summary>
public static class CrestronDataStore
{
/// <summary>Mock data store interface</summary>
public interface IDataStore
{
/// <summary>Sets a value</summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
void SetValue(string key, string value);
/// <summary>Gets a value</summary>
/// <param name="key">Key</param>
/// <returns>Value or null if not found</returns>
string? GetValue(string key);
}
/// <summary>Gets the global data store</summary>
/// <returns>Mock data store instance</returns>
public static IDataStore GetGlobalDataStore()
{
return new MockDataStore();
}
private class MockDataStore : IDataStore
{
private readonly Dictionary<string, string> _data = new Dictionary<string, string>();
public void SetValue(string key, string value)
{
_data[key] = value;
}
public string? GetValue(string key)
{
return _data.TryGetValue(key, out var value) ? value : null;
}
}
}
}

View File

@@ -3,253 +3,253 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Net.Http;
using Crestron.SimplSharp.Net.Https;
using static Crestron.SimplSharp.Net.Http;
using static Crestron.SimplSharp.Net.Https;
namespace PepperDash.Core.GenericRESTfulCommunications;
/// <summary>
/// Generic RESTful communication class
/// </summary>
public class GenericRESTfulClient
{
/// <summary>
/// Generic RESTful communication class
/// Boolean event handler
/// </summary>
public class GenericRESTfulClient
public event EventHandler<BoolChangeEventArgs> BoolChange;
/// <summary>
/// Ushort event handler
/// </summary>
public event EventHandler<UshrtChangeEventArgs> UshrtChange;
/// <summary>
/// String event handler
/// </summary>
public event EventHandler<StringChangeEventArgs> StringChange;
/// <summary>
/// Constructor
/// </summary>
public GenericRESTfulClient()
{
/// <summary>
/// Boolean event handler
/// </summary>
public event EventHandler<BoolChangeEventArgs> BoolChange;
/// <summary>
/// Ushort event handler
/// </summary>
public event EventHandler<UshrtChangeEventArgs> UshrtChange;
/// <summary>
/// String event handler
/// </summary>
public event EventHandler<StringChangeEventArgs> StringChange;
/// <summary>
/// Constructor
/// </summary>
public GenericRESTfulClient()
}
/// <summary>
/// Generic RESTful submit request
/// </summary>
/// <param name="url"></param>
/// <param name="port"></param>
/// <param name="requestType"></param>
/// <param name="username"></param>
/// <param name="password"></param>
/// <param name="contentType"></param>
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);
}
/// <summary>
/// Generic RESTful submit request
/// </summary>
/// <param name="url"></param>
/// <param name="port"></param>
/// <param name="requestType"></param>
/// <param name="username"></param>
/// <param name="password"></param>
/// <param name="contentType"></param>
public void SubmitRequest(string url, ushort port, ushort requestType, string contentType, string username, string password)
else if (url.StartsWith("http:", StringComparison.OrdinalIgnoreCase))
{
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);
}
SubmitRequestHttp(url, port, requestType, contentType, username, password);
}
else
{
OnStringChange(string.Format("Invalid URL {0}", url), 0, GenericRESTfulConstants.ErrorStringChange);
}
}
/// <summary>
/// Private HTTP submit request
/// </summary>
/// <param name="url"></param>
/// <param name="port"></param>
/// <param name="requestType"></param>
/// <param name="contentType"></param>
/// <param name="username"></param>
/// <param name="password"></param>
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
{
OnStringChange(string.Format("Invalid URL {0}", url), 0, GenericRESTfulConstants.ErrorStringChange);
}
}
client.Port = 80;
/// <summary>
/// Private HTTP submit request
/// </summary>
/// <param name="url"></param>
/// <param name="port"></param>
/// <param name="requestType"></param>
/// <param name="contentType"></param>
/// <param name="username"></param>
/// <param name="password"></param>
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);
}
}
/// <summary>
/// Private HTTPS submit request
/// </summary>
/// <param name="url"></param>
/// <param name="port"></param>
/// <param name="requestType"></param>
/// <param name="contentType"></param>
/// <param name="username"></param>
/// <param name="password"></param>
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);
}
}
/// <summary>
/// Private method to encode username and password to Base64 string
/// </summary>
/// <param name="username"></param>
/// <param name="password"></param>
/// <returns>authorization</returns>
private string EncodeBase64(string username, string password)
{
var authorization = "";
if (!string.IsNullOrEmpty(username))
authorization = EncodeBase64(username, password);
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 "" ;
}
if (!string.IsNullOrEmpty(authorization))
request.Header.SetHeaderValue("Authorization", authorization);
return 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);
}
/// <summary>
/// Protected method to handle boolean change events
/// </summary>
/// <param name="state"></param>
/// <param name="index"></param>
/// <param name="type"></param>
protected void OnBoolChange(bool state, ushort index, ushort type)
catch (Exception e)
{
var handler = BoolChange;
if (handler != null)
//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);
}
}
/// <summary>
/// Private HTTPS submit request
/// </summary>
/// <param name="url"></param>
/// <param name="port"></param>
/// <param name="requestType"></param>
/// <param name="contentType"></param>
/// <param name="username"></param>
/// <param name="password"></param>
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);
}
}
/// <summary>
/// Private method to encode username and password to Base64 string
/// </summary>
/// <param name="username"></param>
/// <param name="password"></param>
/// <returns>authorization</returns>
private string EncodeBase64(string username, string password)
{
var authorization = "";
try
{
if (!string.IsNullOrEmpty(username))
{
var args = new BoolChangeEventArgs(state, type);
args.Index = index;
BoolChange(this, args);
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 "";
}
/// <summary>
/// Protected mehtod to handle ushort change events
/// </summary>
/// <param name="value"></param>
/// <param name="index"></param>
/// <param name="type"></param>
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);
}
}
return authorization;
}
/// <summary>
/// Protected method to handle string change events
/// </summary>
/// <param name="value"></param>
/// <param name="index"></param>
/// <param name="type"></param>
protected void OnStringChange(string value, ushort index, ushort type)
/// <summary>
/// Protected method to handle boolean change events
/// </summary>
/// <param name="state"></param>
/// <param name="index"></param>
/// <param name="type"></param>
protected void OnBoolChange(bool state, ushort index, ushort type)
{
var handler = BoolChange;
if (handler != null)
{
var handler = StringChange;
if (handler != null)
{
var args = new StringChangeEventArgs(value, type);
args.Index = index;
StringChange(this, args);
}
var args = new BoolChangeEventArgs(state, type);
args.Index = index;
BoolChange(this, args);
}
}
}
/// <summary>
/// Protected mehtod to handle ushort change events
/// </summary>
/// <param name="value"></param>
/// <param name="index"></param>
/// <param name="type"></param>
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);
}
}
/// <summary>
/// Protected method to handle string change events
/// </summary>
/// <param name="value"></param>
/// <param name="index"></param>
/// <param name="type"></param>
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);
}
}
}

View File

@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectType>Library</ProjectType>
<Configurations>Debug;Release;Test</Configurations>
</PropertyGroup>
<PropertyGroup>
<RootNamespace>PepperDash.Core</RootNamespace>
@@ -24,6 +25,10 @@
<DebugType>full</DebugType>
<DefineConstants>TRACE;DEBUG;SERIES4</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Test|AnyCPU'">
<DebugType>full</DebugType>
<DefineConstants>TRACE;DEBUG;SERIES4</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<DocumentationFile>bin\4Series\$(Configuration)\PepperDashCore.xml</DocumentationFile>
@@ -41,9 +46,14 @@
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Test|AnyCPU' ">
<ProjectReference Include="../../src/CrestronMock/CrestronMock.csproj" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(Platform)' != 'Test|AnyCPU' ">
<PackageReference Include="Crestron.SimplSharp.SDK.Library" Version="2.21.128" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.1" />
<PackageReference Include="Crestron.SimplSharp.SDK.Library" Version="2.21.128" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Serilog" Version="4.3.0" />
<PackageReference Include="Serilog.Expressions" Version="5.0.0" />

View File

@@ -1,11 +1,11 @@
using System;
using Crestron.SimplSharp; // For Basic SIMPL# Classes
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;
using static Crestron.SimplSharp.Net.Http;
using static Crestron.SimplSharp.Net.Https;
namespace PepperDash.Core.WebApi.Presets;
@@ -13,260 +13,260 @@ namespace PepperDash.Core.WebApi.Presets;
/// <summary>
/// Passcode client for the WebApi
/// </summary>
public class WebApiPasscodeClient : IKeyed
public class WebApiPasscodeClient : IKeyed
{
/// <summary>
/// Notifies when user received
/// </summary>
public event EventHandler<UserReceivedEventArgs> UserReceived;
/// <summary>
/// Notifies when Preset received
/// </summary>
public event EventHandler<PresetReceivedEventArgs> PresetReceived;
/// <summary>
/// Unique identifier for this instance
/// </summary>
public string Key { get; private set; }
//string JsonMasterKey;
/// <summary>
/// An embedded JsonToSimpl master object.
/// </summary>
JsonToSimplGenericMaster J2SMaster;
string UrlBase;
string DefaultPresetJsonFilePath;
User CurrentUser;
Preset CurrentPreset;
/// <summary>
/// SIMPL+ can only execute the default constructor. If you have variables that require initialization, please
/// use an Initialize method
/// </summary>
public WebApiPasscodeClient()
{
/// <summary>
/// Notifies when user received
/// </summary>
public event EventHandler<UserReceivedEventArgs> UserReceived;
}
/// <summary>
/// Notifies when Preset received
/// </summary>
public event EventHandler<PresetReceivedEventArgs> PresetReceived;
/// <summary>
/// Initializes the instance
/// </summary>
/// <param name="key"></param>
/// <param name="jsonMasterKey"></param>
/// <param name="urlBase"></param>
/// <param name="defaultPresetJsonFilePath"></param>
public void Initialize(string key, string jsonMasterKey, string urlBase, string defaultPresetJsonFilePath)
{
Key = key;
//JsonMasterKey = jsonMasterKey;
UrlBase = urlBase;
DefaultPresetJsonFilePath = defaultPresetJsonFilePath;
/// <summary>
/// Unique identifier for this instance
/// </summary>
public string Key { get; private set; }
J2SMaster = new JsonToSimplGenericMaster();
J2SMaster.SaveCallback = this.SaveCallback;
J2SMaster.Initialize(jsonMasterKey);
}
//string JsonMasterKey;
/// <summary>
/// Gets the user for a passcode
/// </summary>
/// <param name="passcode"></param>
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();
/// <summary>
/// An embedded JsonToSimpl master object.
/// </summary>
JsonToSimplGenericMaster J2SMaster;
string UrlBase;
string DefaultPresetJsonFilePath;
User CurrentUser;
Preset CurrentPreset;
/// <summary>
/// SIMPL+ can only execute the default constructor. If you have variables that require initialization, please
/// use an Initialize method
/// </summary>
public WebApiPasscodeClient()
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<User>(resp.ContentString);
CurrentUser = user;
if (handler != null)
UserReceived(this, new UserReceivedEventArgs(user, true));
}
else
if (handler != null)
UserReceived(this, new UserReceivedEventArgs(null, false));
}
/// <summary>
///
/// </summary>
/// <param name="roomTypeId"></param>
/// <param name="presetNumber"></param>
public void GetPresetForThisUser(int roomTypeId, int presetNumber)
{
if (CurrentUser == null)
{
CrestronConsole.PrintLine("GetPresetForThisUser no user loaded");
return;
}
/// <summary>
/// Initializes the instance
/// </summary>
/// <param name="key"></param>
/// <param name="jsonMasterKey"></param>
/// <param name="urlBase"></param>
/// <param name="defaultPresetJsonFilePath"></param>
public void Initialize(string key, string jsonMasterKey, string urlBase, string defaultPresetJsonFilePath)
var msg = new UserAndRoomMessage
{
Key = key;
//JsonMasterKey = jsonMasterKey;
UrlBase = urlBase;
DefaultPresetJsonFilePath = defaultPresetJsonFilePath;
UserId = CurrentUser.Id,
RoomTypeId = roomTypeId,
PresetNumber = presetNumber
};
J2SMaster = new JsonToSimplGenericMaster();
J2SMaster.SaveCallback = this.SaveCallback;
J2SMaster.Initialize(jsonMasterKey);
}
/// <summary>
/// Gets the user for a passcode
/// </summary>
/// <param name="passcode"></param>
public void GetUserForPasscode(string passcode)
var handler = PresetReceived;
try
{
// 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<User>(resp.ContentString);
CurrentUser = user;
if (handler != null)
UserReceived(this, new UserReceivedEventArgs(user, true));
}
else
if (handler != null)
UserReceived(this, new UserReceivedEventArgs(null, false));
}
/// <summary>
///
/// </summary>
/// <param name="roomTypeId"></param>
/// <param name="presetNumber"></param>
public void GetPresetForThisUser(int roomTypeId, int presetNumber)
{
if (CurrentUser == null)
{
CrestronConsole.PrintLine("GetPresetForThisUser no user loaded");
if (!UrlBase.StartsWith("https"))
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<Preset>(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)
PresetReceived(this, new PresetReceivedEventArgs(preset, true));
}
else // no existing preset
{
CurrentPreset = new Preset();
LoadDefaultPresetData();
if (handler != null)
PresetReceived(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);
}
}
}
/// <summary>
///
/// </summary>
/// <param name="roomTypeId"></param>
/// <param name="presetNumber"></param>
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
}
/// <summary>
/// After save operation on JSON master happens, send it to server
/// </summary>
/// <param name="json"></param>
void SaveCallback(string json)
{
CurrentPreset.Data = json;
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.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);
req.ContentString = JsonConvert.SerializeObject(msg);
var client = new HttpsClient();
client.HostVerification = false;
client.PeerVerification = false;
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<Preset>(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)
PresetReceived(this, new PresetReceivedEventArgs(preset, true));
}
else // no existing preset
{
CurrentPreset = new Preset();
LoadDefaultPresetData();
if (handler != null)
PresetReceived(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 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);
var data = sr.ReadToEnd();
J2SMaster.SetJsonWithoutEvaluating(data);
CurrentPreset = new Preset() { Data = data, UserId = CurrentUser.Id };
}
catch (HttpException e)
catch (Exception e)
{
CrestronConsole.PrintLine("Preset save exception {0}", e.Response.Code);
Debug.Console(0, this, "Error reading default preset JSON: \r{0}", e);
}
}
}
/// <summary>
///
/// </summary>
/// <param name="roomTypeId"></param>
/// <param name="presetNumber"></param>
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
}
/// <summary>
/// After save operation on JSON master happens, send it to server
/// </summary>
/// <param name="json"></param>
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);
}
}
}

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configurations>Debug;Release;Debug 4.7.2</Configurations>
<Configurations>Debug;Release;Debug 4.7.2;Test</Configurations>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net8</TargetFramework>
@@ -17,6 +17,9 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Test|AnyCPU'">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug 4.7.2|AnyCPU'">
<DebugType>full</DebugType>
</PropertyGroup>

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configurations>Debug;Release;Debug 4.7.2</Configurations>
<Configurations>Debug;Release;Debug 4.7.2;Test</Configurations>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net8</TargetFramework>
@@ -17,6 +17,9 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Test|AnyCPU'">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug 4.7.2|AnyCPU'">
<DebugType>full</DebugType>
</PropertyGroup>

View File

@@ -14,11 +14,16 @@
<Authors>PepperDash Technology</Authors>
<PackageId>PepperDash.Essentials.MobileControl.Messengers</PackageId>
<PackageTags>crestron 4series</PackageTags>
<Configurations>Debug;Release;Test</Configurations>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
<DefineConstants>$(DefineConstants);SERIES4</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Test|AnyCPU'">
<DebugType>full</DebugType>
<DefineConstants>$(DefineConstants);SERIES4</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<DefineConstants>$(DefineConstants);SERIES4</DefineConstants>

View File

@@ -18,10 +18,14 @@
<Authors>PepperDash Technologies</Authors>
<PackageId>PepperDash.Essentials.MobileControl</PackageId>
<PackageTags>crestron 4series</PackageTags>
<Configurations>Debug;Release;Test</Configurations>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DefineConstants>TRACE;DEBUG;SERIES4</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Test|AnyCPU'">
<DefineConstants>TRACE;DEBUG;SERIES4</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<DefineConstants>TRACE;SERIES4</DefineConstants>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectType>Program</ProjectType>
<Configurations>Debug;Release;Debug 4.7.2</Configurations>
<Configurations>Debug;Release;Debug 4.7.2;Test</Configurations>
</PropertyGroup>
<PropertyGroup>
<RootNamespace>PepperDash.Essentials</RootNamespace>
@@ -20,14 +20,22 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Test|AnyCPU'">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug 4.7.2|AnyCPU'">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
</PropertyGroup>
<ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(Platform)' != 'Test|AnyCPU' ">
<PackageReference Include="Crestron.SimplSharp.SDK.Program" Version="2.21.128" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Test|AnyCPU' ">
<ProjectReference Include="../../src/CrestronMock/CrestronMock.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>

View File

@@ -0,0 +1,86 @@
using Crestron.SimplSharpPro;
using Xunit;
namespace EssentialsTests
{
public class CrestronMockTests
{
[Fact]
public void CrestronControlSystem_Constructor_ShouldBuildSuccessfully()
{
// Arrange & Act
var exception = Record.Exception(() => new CrestronControlSystem());
// Assert
Assert.Null(exception);
}
[Fact]
public void CrestronControlSystem_Constructor_ShouldSetPropertiesCorrectly()
{
// Arrange & Act
var controlSystem = new CrestronControlSystem();
// Assert
Assert.NotNull(controlSystem);
Assert.NotNull(controlSystem.ComPorts);
Assert.NotNull(controlSystem.RelayPorts);
Assert.NotNull(controlSystem.IROutputPorts);
Assert.NotNull(controlSystem.DigitalInputPorts);
Assert.NotNull(controlSystem.IRInputPort);
}
[Fact]
public void CrestronControlSystem_InitializeSystem_ShouldNotThrow()
{
// Arrange
var controlSystem = new CrestronControlSystem();
// Act & Assert
var exception = Record.Exception(() => controlSystem.InitializeSystem());
Assert.Null(exception);
}
[Fact]
public void MockControlSystem_ShouldHaveRequiredStaticProperties()
{
// Act & Assert
Assert.NotNull(CrestronControlSystem.NullCue);
Assert.NotNull(CrestronControlSystem.NullBoolInputSig);
Assert.NotNull(CrestronControlSystem.NullBoolOutputSig);
Assert.NotNull(CrestronControlSystem.NullUShortInputSig);
Assert.NotNull(CrestronControlSystem.NullUShortOutputSig);
Assert.NotNull(CrestronControlSystem.NullStringInputSig);
Assert.NotNull(CrestronControlSystem.NullStringOutputSig);
Assert.NotNull(CrestronControlSystem.SigGroups);
}
[Fact]
public void MockControlSystem_ShouldCreateSigGroups()
{
// Act & Assert
var exception = Record.Exception(() =>
{
var sigGroup = CrestronControlSystem.CreateSigGroup(1, eSigType.Bool);
Assert.NotNull(sigGroup);
});
Assert.Null(exception);
}
[Fact]
public void MockControlSystem_VirtualMethods_ShouldNotThrow()
{
// Arrange
var controlSystem = new CrestronControlSystem();
// Act & Assert - just test InitializeSystem since it's definitely available
var exception = Record.Exception(() =>
{
controlSystem.InitializeSystem();
});
Assert.Null(exception);
}
}
}

View File

@@ -0,0 +1,79 @@
using CrestronMock;
using Xunit;
namespace EssentialsTests
{
public class DirectMockTests
{
[Fact]
public void CrestronMock_Should_Build_Successfully()
{
// This test verifies that our mock framework compiles and builds
// We've already proven this by the fact that the test project builds successfully
Assert.True(true, "Mock framework builds successfully in Test configuration");
}
[Fact]
public void MockFramework_Should_Provide_Required_Types()
{
// Verify that the essential mock types are available
var mockSig = new Sig();
var mockBoolInputSig = new BoolInputSig();
var mockUShortInputSig = new UShortInputSig();
var mockStringInputSig = new StringInputSig();
Assert.NotNull(mockSig);
Assert.NotNull(mockBoolInputSig);
Assert.NotNull(mockUShortInputSig);
Assert.NotNull(mockStringInputSig);
}
[Fact]
public void MockFramework_Should_Provide_Hardware_Types()
{
// Verify that hardware mock types are available
var mockComPort = new ComPort();
var mockRelay = new Relay();
var mockIROutputPort = new IROutputPort();
var mockIRInputPort = new IRInputPort();
var mockVersiPort = new VersiPort();
Assert.NotNull(mockComPort);
Assert.NotNull(mockRelay);
Assert.NotNull(mockIROutputPort);
Assert.NotNull(mockIRInputPort);
Assert.NotNull(mockVersiPort);
}
[Fact]
public void TestConfiguration_Should_Use_MockFramework()
{
// In the Test configuration, CrestronControlSystem should come from our mock
// Let's verify this by checking we can create it without real Crestron dependencies
// Since we can't reliably test the namespace-conflicted version,
// let's at least verify our mock types exist
var mockControlSystemType = typeof(CrestronMock.CrestronControlSystem);
Assert.NotNull(mockControlSystemType);
Assert.Equal("CrestronMock.CrestronControlSystem", mockControlSystemType.FullName);
}
[Fact]
public void MockControlSystem_DirectTest_Should_Work()
{
// Test our mock directly using the CrestronMock namespace
var mockControlSystem = new CrestronMock.CrestronControlSystem();
Assert.NotNull(mockControlSystem);
Assert.NotNull(mockControlSystem.ComPorts);
Assert.NotNull(mockControlSystem.RelayPorts);
Assert.NotNull(mockControlSystem.IROutputPorts);
Assert.NotNull(mockControlSystem.DigitalInputPorts);
Assert.NotNull(mockControlSystem.IRInputPort);
// Test that virtual methods don't throw
var exception = Record.Exception(() => mockControlSystem.InitializeSystem());
Assert.Null(exception);
}
}
}

View File

@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<Configurations>Debug;Release;Test</Configurations>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../src/PepperDash.Essentials/PepperDash.Essentials.csproj" />
<ProjectReference Include="../../src/CrestronMock/CrestronMock.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,88 @@
using CrestronMock;
using PepperDash.Essentials;
using PepperDash.Essentials.Core;
namespace EssentialsTests;
public class ControlSystemTests
{
[Fact]
public void ControlSystem_Constructor_ShouldBuildSuccessfully()
{
// Arrange & Act
var exception = Record.Exception(() => new ControlSystem());
// Assert
Assert.Null(exception);
}
[Fact]
public void ControlSystem_Constructor_ShouldSetGlobalControlSystem()
{
// Arrange & Act
var controlSystem = new ControlSystem();
// Assert
Assert.NotNull(Global.ControlSystem);
Assert.Same(controlSystem, Global.ControlSystem);
}
[Fact]
public void ControlSystem_InitializeSystem_ShouldNotThrow()
{
// Arrange
var controlSystem = new ControlSystem();
// Act & Assert
var exception = Record.Exception(() => controlSystem.InitializeSystem());
Assert.Null(exception);
}
[Fact]
public void ControlSystem_ShouldImplementILoadConfig()
{
// Arrange & Act
var controlSystem = new ControlSystem();
// Assert
Assert.True(controlSystem is ILoadConfig);
}
[Fact]
public void ControlSystem_ShouldHaveRequiredInterfaces()
{
// Arrange & Act
var controlSystem = new ControlSystem();
// Assert - Check that it inherits from base mock and implements hardware interfaces
Assert.NotNull(controlSystem);
Assert.True(controlSystem is IComPorts, "ControlSystem should implement IComPorts");
Assert.True(controlSystem is IRelayPorts, "ControlSystem should implement IRelayPorts");
Assert.True(controlSystem is IIROutputPorts, "ControlSystem should implement IIROutputPorts");
Assert.True(controlSystem is IIOPorts, "ControlSystem should implement IIOPorts");
Assert.True(controlSystem is IDigitalInputPorts, "ControlSystem should implement IDigitalInputPorts");
Assert.True(controlSystem is IIRInputPort, "ControlSystem should implement IIRInputPort");
}
[Fact]
public void ControlSystem_ShouldHaveRequiredProperties()
{
// Arrange & Act
var controlSystem = new ControlSystem();
// Assert - Test by casting to interfaces to access properties
var comPorts = controlSystem as IComPorts;
var relayPorts = controlSystem as IRelayPorts;
var irOutputPorts = controlSystem as IIROutputPorts;
var ioPorts = controlSystem as IIOPorts;
var digitalInputPorts = controlSystem as IDigitalInputPorts;
var irInputPort = controlSystem as IIRInputPort;
Assert.NotNull(comPorts?.ComPorts);
Assert.NotNull(relayPorts?.RelayPorts);
Assert.NotNull(irOutputPorts?.IROutputPorts);
Assert.NotNull(ioPorts?.IOPorts);
Assert.NotNull(digitalInputPorts?.DigitalInputPorts);
Assert.NotNull(irInputPort?.IRInputPort);
}
}