mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-02-22 08:05:00 +00:00
Compare commits
246 Commits
v3.0.0-net
...
portal-url
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c49fb9321 | ||
|
|
c55de61da9 | ||
|
|
42444ede0a | ||
|
|
3d50f5f5ac | ||
|
|
11d62aebe1 | ||
|
|
edc10a9c2a | ||
|
|
9be5823956 | ||
|
|
35371dde22 | ||
|
|
d3ceb4d7e7 | ||
|
|
a782b57100 | ||
|
|
1360de599f | ||
|
|
fd95f5fed1 | ||
|
|
9426dff5df | ||
|
|
0d083e63c6 | ||
|
|
6ed7c96ec7 | ||
|
|
314570d6c3 | ||
|
|
ff609dfecd | ||
|
|
666be5ae59 | ||
|
|
bfc9b7e7fa | ||
|
|
b02e952765 | ||
|
|
72861a5097 | ||
|
|
44ed067f4d | ||
|
|
faa2169baf | ||
|
|
fd40b0c6d1 | ||
|
|
afcddad1cc | ||
|
|
c4cf8f13e9 | ||
|
|
c852f87a27 | ||
|
|
071174fa7d | ||
|
|
da0f28a10c | ||
|
|
0538a304ed | ||
|
|
705c5db237 | ||
|
|
2e95f5337e | ||
|
|
ba576180a7 | ||
|
|
92c9db8237 | ||
|
|
c4d064965f | ||
|
|
f27965ac29 | ||
|
|
3ce282e2db | ||
|
|
32a332a6e2 | ||
|
|
151a90c9be | ||
|
|
89e893b3b7 | ||
|
|
d01eb03890 | ||
|
|
16c92afabb | ||
|
|
dff5d2d32e | ||
|
|
317bde3814 | ||
|
|
8bab3dc966 | ||
|
|
514ac850ca | ||
|
|
44432f7a41 | ||
|
|
99253b30c2 | ||
|
|
bf248fe33e | ||
|
|
2f44040e4f | ||
|
|
10399a1be8 | ||
|
|
5409db193c | ||
|
|
f1ce54a524 | ||
|
|
7c72a0d905 | ||
|
|
5d5e78629e | ||
|
|
dab5484d6e | ||
|
|
5c35a3be45 | ||
|
|
6cb98e12fa | ||
|
|
608601990b | ||
|
|
3e0f318f7f | ||
|
|
98d0cc8fdc | ||
|
|
c557c6cdd6 | ||
|
|
8525134ae7 | ||
|
|
1197b15a33 | ||
|
|
ea6a7568fc | ||
|
|
3a2a059ce1 | ||
|
|
f9d9df9d5c | ||
|
|
c284c4275f | ||
|
|
0418f8a7cc | ||
|
|
a5d409e93a | ||
|
|
59944c0e2f | ||
|
|
419177ccd5 | ||
|
|
bd01e2bacc | ||
|
|
2928c5cf94 | ||
|
|
82b5dc96c1 | ||
|
|
37cea8a11c | ||
|
|
a3f0901fa0 | ||
|
|
f91f435768 | ||
|
|
5120b2b574 | ||
|
|
7c90027578 | ||
|
|
bb694b4200 | ||
|
|
087d0a1149 | ||
|
|
4747c16b02 | ||
|
|
aa4d241dde | ||
|
|
9fc5586531 | ||
|
|
d33fd56529 | ||
|
|
8fc4d21f02 | ||
|
|
6d93662b31 | ||
|
|
11b190e76f | ||
|
|
df03a71cbc | ||
|
|
72e67a1c4c | ||
|
|
f8728b6825 | ||
|
|
d9ef8a2289 | ||
|
|
278408a3bc | ||
|
|
fd70377c7f | ||
|
|
06341b14f3 | ||
|
|
d7f9c74b2f | ||
|
|
5921b5dbb0 | ||
|
|
ba0de5128f | ||
|
|
b0a090062f | ||
|
|
9c0cab8218 | ||
|
|
c0af637108 | ||
|
|
9c9eaea928 | ||
|
|
9de94bd65f | ||
|
|
ff46fb8f29 | ||
|
|
d9243def30 | ||
|
|
258699fbcd | ||
|
|
738504e9fc | ||
|
|
cae1bbd6e6 | ||
|
|
dea4407e3e | ||
|
|
ab08a546f7 | ||
|
|
ef98d47792 | ||
|
|
c05976ee60 | ||
|
|
4ca1031bef | ||
|
|
6d61c4525e | ||
|
|
3db274ace5 | ||
|
|
f0f708294c | ||
|
|
a00d186c62 | ||
|
|
51da668dfd | ||
|
|
d2b7400039 | ||
|
|
2424838b7f | ||
|
|
9556edc064 | ||
|
|
f9088691fd | ||
|
|
e40b6a8b4c | ||
|
|
c3b39a87da | ||
|
|
06dc0e947e | ||
|
|
147997f460 | ||
|
|
49abec5eea | ||
|
|
6830efe42a | ||
|
|
d013068a0c | ||
|
|
52916d29f4 | ||
|
|
19e8489166 | ||
|
|
fe33443b25 | ||
|
|
8cf195b262 | ||
|
|
40406b797d | ||
|
|
65bc408ebf | ||
|
|
9d49fb8357 | ||
|
|
fb7797dac7 | ||
|
|
574f5f6dc9 | ||
|
|
e49c69a12f | ||
|
|
7889cba196 | ||
|
|
033aa1f3dd | ||
|
|
9b6c2d80ea | ||
|
|
a0fc731701 | ||
|
|
f2d0dca7b8 | ||
|
|
ab4e85d081 | ||
|
|
47017da527 | ||
|
|
4f0d464ba4 | ||
|
|
0107422507 | ||
|
|
1a366790e7 | ||
|
|
97448f4f0f | ||
|
|
cf3ece4237 | ||
|
|
bb4b2f88b6 | ||
|
|
808e8042a7 | ||
|
|
0bc4388bfd | ||
|
|
dbc132c0da | ||
|
|
5bb0ab2626 | ||
|
|
27bf36c58c | ||
|
|
ce886aea63 | ||
|
|
ef920bf54c | ||
|
|
88466818ce | ||
|
|
0871a902e1 | ||
|
|
a031424752 | ||
|
|
fd1ba345aa | ||
|
|
e03874a7a9 | ||
|
|
2efab4f196 | ||
|
|
a41aba1904 | ||
|
|
d0ca6721f5 | ||
|
|
c732eb48f2 | ||
|
|
efe70208d3 | ||
|
|
615f640ebb | ||
|
|
ee6f9416a3 | ||
|
|
4fc6ecbd0b | ||
|
|
58bcc3315d | ||
|
|
08cc84a8e8 | ||
|
|
226014fee0 | ||
|
|
dc7f99e176 | ||
|
|
f0af9f8d19 | ||
|
|
31143f56df | ||
|
|
43989b9588 | ||
|
|
8db559f197 | ||
|
|
86f20da116 | ||
|
|
0674dbda37 | ||
|
|
592607f3c8 | ||
|
|
ea0a779f8b | ||
|
|
86e4d2f7fb | ||
|
|
0069233e13 | ||
|
|
4048efb07e | ||
|
|
b12cdbc75c | ||
|
|
b47f1d6b77 | ||
|
|
1dbac7d1c8 | ||
|
|
799d4c127c | ||
|
|
a6cd9a0571 | ||
|
|
80da4ad98f | ||
|
|
b283ed34b4 | ||
|
|
899f13eadb | ||
|
|
fc1e29565e | ||
|
|
f9a74567d2 | ||
|
|
53b1e5d142 | ||
|
|
78e9ea8070 | ||
|
|
df201558a5 | ||
|
|
130c874684 | ||
|
|
aee40ffe14 | ||
|
|
3ffad13abf | ||
|
|
5ee7aaa991 | ||
|
|
4fa8433e73 | ||
|
|
5fe99518a0 | ||
|
|
5a2a2129e6 | ||
|
|
4fbfda62d6 | ||
|
|
b8ab54cbe0 | ||
|
|
f6f1619bc2 | ||
|
|
41fd4d6adc | ||
|
|
5b73f8fbd2 | ||
|
|
c70a8edc24 | ||
|
|
7987eb8f9b | ||
|
|
260677a37f | ||
|
|
eeb0e84dc7 | ||
|
|
d282487da6 | ||
|
|
da30424657 | ||
|
|
311452beac | ||
|
|
789113008e | ||
|
|
660836bd5a | ||
|
|
97b2ffed9c | ||
|
|
2bbefa062d | ||
|
|
3421b2f28c | ||
|
|
82889e9794 | ||
|
|
1dcd4e328c | ||
|
|
e76369726d | ||
|
|
2bf0f2092b | ||
|
|
f8455d4110 | ||
|
|
c1eccfd790 | ||
|
|
f1a89161bc | ||
|
|
e59c50d0aa | ||
|
|
9d313d8c7c | ||
|
|
9813673b66 | ||
|
|
f006ed0076 | ||
|
|
ddbcc13c50 | ||
|
|
2a70fc678e | ||
|
|
056614cba1 | ||
|
|
5ff587a8c9 | ||
|
|
26c1baa1f8 | ||
|
|
2b15c2a56f | ||
|
|
a076d531bc | ||
|
|
5e880f0111 | ||
|
|
8f1fb86d37 | ||
|
|
2fa297a204 |
56
.github/workflows/ci.yml
vendored
56
.github/workflows/ci.yml
vendored
@@ -1,56 +0,0 @@
|
||||
name: CI Build and Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, develop, net8-updates ]
|
||||
pull_request:
|
||||
branches: [ main, develop, net8-updates ]
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: '8.0.x'
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build
|
||||
run: dotnet build --configuration Release --no-restore
|
||||
|
||||
- name: Run tests
|
||||
run: dotnet test --no-restore --verbosity normal --collect:"XPlat Code Coverage" --results-directory ./coverage
|
||||
|
||||
- name: Generate coverage report
|
||||
uses: danielpalme/ReportGenerator-GitHub-Action@5.2.0
|
||||
with:
|
||||
reports: coverage/**/coverage.cobertura.xml
|
||||
targetdir: coverage-report
|
||||
reporttypes: Html;Cobertura;MarkdownSummary
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
file: ./coverage-report/Cobertura.xml
|
||||
flags: unittests
|
||||
name: codecov-umbrella
|
||||
fail_ci_if_error: false
|
||||
|
||||
- name: Write coverage summary
|
||||
run: cat coverage-report/Summary.md >> $GITHUB_STEP_SUMMARY
|
||||
if: always()
|
||||
|
||||
- name: Upload test results
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: test-results
|
||||
path: |
|
||||
coverage/
|
||||
coverage-report/
|
||||
247
.github/workflows/essentials-3-dev-build.yml
vendored
247
.github/workflows/essentials-3-dev-build.yml
vendored
@@ -1,247 +0,0 @@
|
||||
name: Essentials v3 Development Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- feature-3.0.0/*
|
||||
- hotfix-3.0.0/*
|
||||
- release-3.0.0/*
|
||||
- development-3.0.0
|
||||
|
||||
env:
|
||||
SOLUTION_PATH: .
|
||||
SOLUTION_FILE: PepperDash.Essentials
|
||||
VERSION: 0.0.0-buildtype-buildnumber
|
||||
BUILD_TYPE: Debug
|
||||
RELEASE_BRANCH: main
|
||||
jobs:
|
||||
Build_Project_4-Series:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# Detect environment (Act vs GitHub)
|
||||
- name: Detect environment
|
||||
id: detect_env
|
||||
run: |
|
||||
if [ -n "$ACT" ]; then
|
||||
echo "is_local=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "is_local=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Install prerequisites
|
||||
run: |
|
||||
if [ "${{ steps.detect_env.outputs.is_local }}" == "true" ]; then
|
||||
# For Act - no sudo needed
|
||||
apt-get update
|
||||
apt-get install -y curl wget libicu-dev git unzip
|
||||
else
|
||||
# For GitHub runners - sudo required
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y curl wget libicu-dev git unzip
|
||||
fi
|
||||
|
||||
- name: Set Version Number
|
||||
id: setVersion
|
||||
shell: bash
|
||||
run: |
|
||||
latestVersion="3.0.0"
|
||||
newVersion=$latestVersion
|
||||
phase=""
|
||||
newVersionString=""
|
||||
|
||||
if [[ $GITHUB_REF =~ ^refs/pull/.* ]]; then
|
||||
phase="beta"
|
||||
newVersionString="${newVersion}-${phase}-${GITHUB_RUN_NUMBER}"
|
||||
elif [[ $GITHUB_REF =~ ^refs/heads/hotfix-3.0.0/.* ]]; then
|
||||
phase="hotfix"
|
||||
newVersionString="${newVersion}-${phase}-${GITHUB_RUN_NUMBER}"
|
||||
elif [[ $GITHUB_REF =~ ^refs/heads/feature-3.0.0/.* ]]; then
|
||||
phase="alpha"
|
||||
newVersionString="${newVersion}-${phase}-${GITHUB_RUN_NUMBER}"
|
||||
elif [[ $GITHUB_REF == "refs/heads/development-3.0.0" ]]; then
|
||||
phase="beta"
|
||||
newVersionString="${newVersion}-${phase}-${GITHUB_RUN_NUMBER}"
|
||||
elif [[ $GITHUB_REF =~ ^refs/heads/release-3.0.0/.* ]]; then
|
||||
version=$(echo $GITHUB_REF | awk -F '/' '{print $NF}' | sed 's/v//')
|
||||
phase="rc"
|
||||
newVersionString="${version}-${phase}-${GITHUB_RUN_NUMBER}"
|
||||
else
|
||||
# For local builds or unrecognized branches
|
||||
newVersionString="${newVersion}-local"
|
||||
fi
|
||||
|
||||
echo "version=$newVersionString" >> $GITHUB_OUTPUT
|
||||
|
||||
# Create Build Properties file
|
||||
- name: Create Build Properties
|
||||
run: |
|
||||
cat > Directory.Build.props << EOF
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>${{ steps.setVersion.outputs.version }}</Version>
|
||||
<AssemblyVersion>${{ steps.setVersion.outputs.version }}</AssemblyVersion>
|
||||
<FileVersion>${{ steps.setVersion.outputs.version }}</FileVersion>
|
||||
<InformationalVersion>${{ steps.setVersion.outputs.version }}</InformationalVersion>
|
||||
<PackageVersion>${{ steps.setVersion.outputs.version }}</PackageVersion>
|
||||
<NuGetVersion>${{ steps.setVersion.outputs.version }}</NuGetVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
EOF
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: '8.0.x'
|
||||
|
||||
- name: Restore NuGet Packages
|
||||
run: dotnet restore ${SOLUTION_FILE}.sln
|
||||
|
||||
- name: Build Solution
|
||||
run: dotnet build ${SOLUTION_FILE}.sln --configuration ${BUILD_TYPE} --no-restore
|
||||
|
||||
# Copy the CPZ file to the output directory with version in the filename
|
||||
- name: Copy and Rename CPZ Files
|
||||
run: |
|
||||
mkdir -p ./output/cpz
|
||||
|
||||
# Find the main CPZ file in the build output
|
||||
if [ -f "./src/PepperDash.Essentials/bin/${BUILD_TYPE}/net8/PepperDashEssentials.cpz" ]; then
|
||||
cp "./src/PepperDash.Essentials/bin/${BUILD_TYPE}/net8/PepperDashEssentials.cpz" "./output/cpz/PepperDashEssentials.${{ steps.setVersion.outputs.version }}.cpz"
|
||||
echo "Main CPZ file copied and renamed successfully."
|
||||
else
|
||||
echo "Warning: Main CPZ file not found at expected location."
|
||||
find ./src -name "*.cpz" | xargs -I {} cp {} ./output/cpz/
|
||||
fi
|
||||
|
||||
- name: Pack Solution
|
||||
run: dotnet pack ${SOLUTION_FILE}.sln --configuration ${BUILD_TYPE} --output ./output/nuget --no-build
|
||||
|
||||
# List build artifacts (runs in both environments)
|
||||
- name: List Build Artifacts
|
||||
run: |
|
||||
echo "=== Build Artifacts ==="
|
||||
echo "NuGet Packages:"
|
||||
find ./output/nuget -type f | sort
|
||||
echo ""
|
||||
echo "CPZ/CPLZ Files:"
|
||||
find ./output -name "*.cpz" -o -name "*.cplz" | sort
|
||||
echo "======================="
|
||||
|
||||
# Enhanced package inspection for local runs
|
||||
- name: Inspect NuGet Packages
|
||||
if: steps.detect_env.outputs.is_local == 'true'
|
||||
run: |
|
||||
echo "=== NuGet Package Details ==="
|
||||
for pkg in $(find ./output/nuget -name "*.nupkg"); do
|
||||
echo "Package: $(basename "$pkg")"
|
||||
echo "Size: $(du -h "$pkg" | cut -f1)"
|
||||
|
||||
# Extract and show package contents
|
||||
echo "Contents:"
|
||||
unzip -l "$pkg" | tail -n +4 | head -n -2
|
||||
echo "--------------------------"
|
||||
|
||||
# Try to extract and show the nuspec file (contains metadata)
|
||||
echo "Metadata:"
|
||||
unzip -p "$pkg" "*.nuspec" 2>/dev/null | grep -E "(<id>|<version>|<description>|<authors>|<dependencies>)" || echo "Metadata extraction failed"
|
||||
echo "--------------------------"
|
||||
done
|
||||
echo "==========================="
|
||||
|
||||
# Tag creation - GitHub version
|
||||
- name: Create tag for non-rc builds (GitHub)
|
||||
if: ${{ !contains(steps.setVersion.outputs.version, 'rc') && steps.detect_env.outputs.is_local == 'false' }}
|
||||
run: |
|
||||
git config --global user.name "GitHub Actions"
|
||||
git config --global user.email "actions@github.com"
|
||||
git tag ${{ steps.setVersion.outputs.version }}
|
||||
git push --tags origin
|
||||
|
||||
# Tag creation - Act mock version
|
||||
- name: Create tag for non-rc builds (Act Mock)
|
||||
if: ${{ !contains(steps.setVersion.outputs.version, 'rc') && steps.detect_env.outputs.is_local == 'true' }}
|
||||
run: |
|
||||
echo "Would create git tag: ${{ steps.setVersion.outputs.version }}"
|
||||
echo "Would push tag to: origin"
|
||||
|
||||
# Release creation - GitHub version
|
||||
- name: Create Release (GitHub)
|
||||
if: steps.detect_env.outputs.is_local == 'false'
|
||||
id: create_release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
artifacts: 'output/cpz/*,output/**/*.cplz'
|
||||
generateReleaseNotes: true
|
||||
prerelease: ${{contains('debug', env.BUILD_TYPE)}}
|
||||
tag: ${{ steps.setVersion.outputs.version }}
|
||||
|
||||
# Release creation - Act mock version with enhanced output
|
||||
- name: Create Release (Act Mock)
|
||||
if: steps.detect_env.outputs.is_local == 'true'
|
||||
run: |
|
||||
echo "=== Mock Release Creation ==="
|
||||
echo "Would create release with:"
|
||||
echo "- Tag: ${{ steps.setVersion.outputs.version }}"
|
||||
echo "- Prerelease: ${{contains('debug', env.BUILD_TYPE)}}"
|
||||
echo "- Artifacts matching pattern: output/cpz/*,output/**/*.cplz"
|
||||
echo ""
|
||||
echo "Matching artifacts:"
|
||||
find ./output/cpz -type f
|
||||
find ./output -name "*.cplz"
|
||||
|
||||
# Detailed info about release artifacts
|
||||
echo ""
|
||||
echo "Artifact Details:"
|
||||
for artifact in $(find ./output/cpz -type f; find ./output -name "*.cplz"); do
|
||||
echo "File: $(basename "$artifact")"
|
||||
echo "Size: $(du -h "$artifact" | cut -f1)"
|
||||
echo "Created: $(stat -c %y "$artifact")"
|
||||
echo "MD5: $(md5sum "$artifact" | cut -d' ' -f1)"
|
||||
echo "--------------------------"
|
||||
done
|
||||
echo "============================"
|
||||
|
||||
# NuGet setup - GitHub version
|
||||
- name: Setup NuGet (GitHub)
|
||||
if: steps.detect_env.outputs.is_local == 'false'
|
||||
run: |
|
||||
dotnet nuget add source https://nuget.pkg.github.com/pepperdash/index.json -n github -u pepperdash -p ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text
|
||||
|
||||
# NuGet setup - Act mock version
|
||||
- name: Setup NuGet (Act Mock)
|
||||
if: steps.detect_env.outputs.is_local == 'true'
|
||||
run: |
|
||||
echo "=== Mock NuGet Setup ==="
|
||||
echo "Would add GitHub NuGet source: https://nuget.pkg.github.com/pepperdash/index.json"
|
||||
echo "======================="
|
||||
|
||||
# Publish to NuGet - GitHub version
|
||||
- name: Publish to Nuget (GitHub)
|
||||
if: steps.detect_env.outputs.is_local == 'false'
|
||||
run: dotnet nuget push ./output/nuget/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }}
|
||||
|
||||
# Publish to NuGet - Act mock version
|
||||
- name: Publish to Nuget (Act Mock)
|
||||
if: steps.detect_env.outputs.is_local == 'true'
|
||||
run: |
|
||||
echo "=== Mock Publish to NuGet ==="
|
||||
echo "Would publish the following packages to https://api.nuget.org/v3/index.json:"
|
||||
find ./output/nuget -name "*.nupkg" | sort
|
||||
echo "============================="
|
||||
|
||||
# Publish to GitHub NuGet - GitHub version
|
||||
- name: Publish to Github Nuget (GitHub)
|
||||
if: steps.detect_env.outputs.is_local == 'false'
|
||||
run: dotnet nuget push ./output/nuget/*.nupkg --source github --api-key ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Publish to GitHub NuGet - Act mock version
|
||||
- name: Publish to Github Nuget (Act Mock)
|
||||
if: steps.detect_env.outputs.is_local == 'true'
|
||||
run: |
|
||||
echo "=== Mock Publish to GitHub NuGet ==="
|
||||
echo "Would publish the following packages to the GitHub NuGet registry:"
|
||||
find ./output/nuget -name "*.nupkg" | sort
|
||||
echo "=================================="
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -394,3 +394,6 @@ essentials-framework/Essentials Interfaces/PepperDash_Essentials_Interfaces/Pepp
|
||||
.vscode/settings.json
|
||||
_site/
|
||||
api/
|
||||
*.DS_Store
|
||||
/._PepperDash.Essentials.4Series.sln
|
||||
dotnet
|
||||
|
||||
9
.vscode/extensions.json
vendored
Normal file
9
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"ms-dotnettools.vscode-dotnet-runtime",
|
||||
"ms-dotnettools.csharp",
|
||||
"ms-dotnettools.csdevkit",
|
||||
"vivaxy.vscode-conventional-commits",
|
||||
"mhutchie.git-graph"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
# Crestron Library Usage Analysis - PepperDash Essentials
|
||||
|
||||
This document provides a comprehensive analysis of Crestron classes and interfaces used throughout the PepperDash Essentials framework, organized by namespace and library component.
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The PepperDash Essentials framework extensively leverages Crestron SDK components across 100+ files, providing abstractions for:
|
||||
- Control system hardware (processors, touchpanels, IO devices)
|
||||
- Communication interfaces (Serial, TCP/IP, SSH, CEC, IR)
|
||||
- Device management and routing
|
||||
- User interface components and smart objects
|
||||
- System monitoring and diagnostics
|
||||
|
||||
## 1. Core Crestron Libraries
|
||||
|
||||
### 1.1 Crestron.SimplSharp
|
||||
|
||||
**Primary Usage**: Foundational framework components, collections, and basic types.
|
||||
|
||||
**Key Files**:
|
||||
- Multiple files across all projects use `Crestron.SimplSharp` namespaces
|
||||
- Provides basic C# runtime support for Crestron processors
|
||||
|
||||
### 1.2 Crestron.SimplSharpPro
|
||||
|
||||
**Primary Usage**: Main hardware abstraction layer for Crestron devices.
|
||||
|
||||
**Key Classes Used**:
|
||||
|
||||
#### CrestronControlSystem
|
||||
- **File**: `/src/PepperDash.Essentials/ControlSystem.cs`
|
||||
- **Usage**: Base class for the main control system implementation
|
||||
- **Implementation**: `public class ControlSystem : CrestronControlSystem, ILoadConfig`
|
||||
|
||||
#### Device (Base Class)
|
||||
- **Files**: 50+ files inherit from or use this class
|
||||
- **Key Implementations**:
|
||||
- `/src/PepperDash.Core/Device.cs` - Core device abstraction
|
||||
- `/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs` - Extended device base
|
||||
- `/src/PepperDash.Essentials.Core/Room/Room.cs` - Room device implementation
|
||||
- `/src/PepperDash.Essentials.Core/Devices/CrestronProcessor.cs` - Processor device wrapper
|
||||
|
||||
#### BasicTriList
|
||||
- **Files**: 30+ files use this class extensively
|
||||
- **Primary Usage**: Touchpanel communication and SIMPL bridging
|
||||
- **Key Files**:
|
||||
- `/src/PepperDash.Essentials.Core/Touchpanels/TriListExtensions.cs` - Extension methods for signal handling
|
||||
- `/src/PepperDash.Essentials.Core/Devices/EssentialsBridgeableDevice.cs` - Bridge interface
|
||||
- `/src/PepperDash.Essentials.Core/Touchpanels/ModalDialog.cs` - UI dialog implementation
|
||||
|
||||
#### BasicTriListWithSmartObject
|
||||
- **Files**: Multiple touchpanel and UI files
|
||||
- **Usage**: Enhanced touchpanel support with smart object integration
|
||||
- **Key Files**:
|
||||
- `/src/PepperDash.Essentials.Core/Touchpanels/Interfaces.cs` - Interface definitions
|
||||
- `/src/PepperDash.Essentials.Core/SmartObjects/SubpageReferenceList/SubpageReferenceList.cs`
|
||||
|
||||
## 2. Communication Hardware
|
||||
|
||||
### 2.1 Serial Communication (ComPort)
|
||||
|
||||
**Primary Class**: `ComPort`
|
||||
**Key Files**:
|
||||
- `/src/PepperDash.Essentials.Core/Comm and IR/ComPortController.cs`
|
||||
- `/src/PepperDash.Essentials.Core/Comm and IR/CommFactory.cs`
|
||||
|
||||
**Usage Pattern**:
|
||||
```csharp
|
||||
public class ComPortController : Device, IBasicCommunicationWithStreamDebugging
|
||||
public static ComPort GetComPort(EssentialsControlPropertiesConfig config)
|
||||
```
|
||||
|
||||
**Interface Support**: `IComPorts` - Used for devices that provide multiple COM ports
|
||||
|
||||
### 2.2 IR Communication (IROutputPort)
|
||||
|
||||
**Primary Class**: `IROutputPort`
|
||||
**Key Files**:
|
||||
- `/src/PepperDash.Essentials.Core/Devices/IrOutputPortController.cs`
|
||||
- `/src/PepperDash.Essentials.Core/Devices/GenericIRController.cs`
|
||||
- `/src/PepperDash.Essentials.Core/Comm and IR/IRPortHelper.cs`
|
||||
|
||||
**Usage Pattern**:
|
||||
```csharp
|
||||
public class IrOutputPortController : Device
|
||||
IROutputPort IrPort;
|
||||
public IrOutputPortController(string key, IROutputPort port, string irDriverFilepath)
|
||||
```
|
||||
|
||||
### 2.3 CEC Communication (ICec)
|
||||
|
||||
**Primary Interface**: `ICec`
|
||||
**Key Files**:
|
||||
- `/src/PepperDash.Essentials.Core/Comm and IR/CecPortController.cs`
|
||||
- `/src/PepperDash.Essentials.Core/Comm and IR/CommFactory.cs`
|
||||
|
||||
**Usage Pattern**:
|
||||
```csharp
|
||||
public class CecPortController : Device, IBasicCommunicationWithStreamDebugging
|
||||
public static ICec GetCecPort(ControlPropertiesConfig config)
|
||||
```
|
||||
|
||||
## 3. Input/Output Hardware
|
||||
|
||||
### 3.1 Digital Input
|
||||
|
||||
**Primary Interface**: `IDigitalInput`
|
||||
**Key Files**:
|
||||
- `/src/PepperDash.Essentials.Core/CrestronIO/GenericDigitalInputDevice.cs`
|
||||
- `/src/PepperDash.Essentials.Core/Microphone Privacy/MicrophonePrivacyController.cs`
|
||||
|
||||
**Usage Pattern**:
|
||||
```csharp
|
||||
public List<IDigitalInput> Inputs { get; private set; }
|
||||
void AddInput(IDigitalInput input)
|
||||
```
|
||||
|
||||
### 3.2 Versiport Support
|
||||
|
||||
**Key Files**:
|
||||
- `/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportInputDevice.cs`
|
||||
- `/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportAnalogInputDevice.cs`
|
||||
- `/src/PepperDash.Essentials.Core/CrestronIO/GenericVersiportOutputDevice.cs`
|
||||
|
||||
**Usage**: Provides flexible I/O port configuration for various signal types
|
||||
|
||||
## 4. Touchpanel Hardware
|
||||
|
||||
### 4.1 MPC3 Touchpanel
|
||||
|
||||
**Primary Class**: `MPC3Basic`
|
||||
**Key File**: `/src/PepperDash.Essentials.Core/Touchpanels/Mpc3Touchpanel.cs`
|
||||
|
||||
**Usage Pattern**:
|
||||
```csharp
|
||||
public class Mpc3TouchpanelController : Device
|
||||
readonly MPC3Basic _touchpanel;
|
||||
_touchpanel = processor.ControllerTouchScreenSlotDevice as MPC3Basic;
|
||||
```
|
||||
|
||||
### 4.2 TSW Series Support
|
||||
|
||||
**Evidence**: References found in messenger files and mobile control components
|
||||
**Usage**: Integrated through mobile control messaging system for TSW touchpanel features
|
||||
|
||||
## 5. Timer and Threading
|
||||
|
||||
### 5.1 CTimer
|
||||
|
||||
**Primary Class**: `CTimer`
|
||||
**Key File**: `/src/PepperDash.Core/PasswordManagement/PasswordManager.cs`
|
||||
|
||||
**Usage Pattern**:
|
||||
```csharp
|
||||
Debug.Console(1, string.Format("PasswordManager.UpdatePassword: CTimer Started"));
|
||||
Debug.Console(1, string.Format("PasswordManager.UpdatePassword: CTimer Reset"));
|
||||
```
|
||||
|
||||
## 6. Networking and Communication
|
||||
|
||||
### 6.1 Ethernet Communication
|
||||
|
||||
**Libraries Used**:
|
||||
- `Crestron.SimplSharpPro.EthernetCommunication`
|
||||
- `Crestron.SimplSharp.Net.Utilities.EthernetHelper`
|
||||
|
||||
**Key Files**:
|
||||
- `/src/PepperDash.Core/Comm/GenericTcpIpClient.cs`
|
||||
- `/src/PepperDash.Core/Comm/GenericTcpIpServer.cs`
|
||||
- `/src/PepperDash.Core/Comm/GenericSecureTcpIpClient.cs`
|
||||
- `/src/PepperDash.Core/Comm/GenericSshClient.cs`
|
||||
- `/src/PepperDash.Core/Comm/GenericUdpServer.cs`
|
||||
|
||||
**Usage Pattern**:
|
||||
```csharp
|
||||
public class GenericTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
|
||||
public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
|
||||
```
|
||||
|
||||
## 7. Device Management Libraries
|
||||
|
||||
### 7.1 DeviceSupport
|
||||
|
||||
**Library**: `Crestron.SimplSharpPro.DeviceSupport`
|
||||
**Usage**: Core device support infrastructure used throughout the framework
|
||||
|
||||
### 7.2 DM (DigitalMedia)
|
||||
|
||||
**Library**: `Crestron.SimplSharpPro.DM`
|
||||
**Usage**: Digital media routing and switching support
|
||||
**Evidence**: Found in routing configuration and DM output card references
|
||||
|
||||
## 8. User Interface Libraries
|
||||
|
||||
### 8.1 UI Components
|
||||
|
||||
**Library**: `Crestron.SimplSharpPro.UI`
|
||||
**Usage**: User interface elements and touchpanel controls
|
||||
|
||||
### 8.2 Smart Objects
|
||||
|
||||
**Key Files**:
|
||||
- `/src/PepperDash.Essentials.Core/SmartObjects/SmartObjectDynamicList.cs`
|
||||
- `/src/PepperDash.Essentials.Core/SmartObjects/SubpageReferenceList/SubpageReferenceList.cs`
|
||||
|
||||
**Usage**: Advanced UI components with dynamic content
|
||||
|
||||
## 9. System Monitoring and Diagnostics
|
||||
|
||||
### 9.1 Diagnostics
|
||||
|
||||
**Library**: `Crestron.SimplSharpPro.Diagnostics`
|
||||
**Usage**: System health monitoring and performance tracking
|
||||
|
||||
### 9.2 System Information
|
||||
|
||||
**Key Files**:
|
||||
- `/src/PepperDash.Essentials.Core/Monitoring/SystemMonitorController.cs`
|
||||
|
||||
**Usage**: Provides system status, Ethernet information, and program details
|
||||
|
||||
## 10. Integration Patterns
|
||||
|
||||
### 10.1 SIMPL Bridging
|
||||
|
||||
**Pattern**: Extensive use of `BasicTriList` for SIMPL integration
|
||||
**Files**: Bridge classes throughout the framework implement `LinkToApi` methods:
|
||||
```csharp
|
||||
public abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge);
|
||||
```
|
||||
|
||||
### 10.2 Device Factory Pattern
|
||||
|
||||
**Implementation**: Factory classes create hardware-specific implementations
|
||||
**Example**: `CommFactory.cs` provides communication device creation
|
||||
|
||||
### 10.3 Extension Methods
|
||||
|
||||
**Pattern**: Extensive use of extension methods for Crestron classes
|
||||
**Example**: `TriListExtensions.cs` adds 30+ extension methods to `BasicTriList`
|
||||
|
||||
## 11. Signal Processing
|
||||
|
||||
### 11.1 Signal Types
|
||||
|
||||
**Bool Signals**: Digital control and feedback
|
||||
**UShort Signals**: Analog values and numeric data
|
||||
**String Signals**: Text and configuration data
|
||||
|
||||
**Implementation**: Comprehensive signal handling in `TriListExtensions.cs`
|
||||
|
||||
## 12. Error Handling and Logging
|
||||
|
||||
**Pattern**: Consistent use of Crestron's Debug logging throughout
|
||||
**Examples**:
|
||||
```csharp
|
||||
Debug.LogMessage(LogEventLevel.Information, "Device {0} is not a valid device", dc.PortDeviceKey);
|
||||
Debug.LogMessage(LogEventLevel.Debug, "Error Waking Panel. Maybe testing with Xpanel?");
|
||||
```
|
||||
|
||||
## 13. Threading and Synchronization
|
||||
|
||||
**Components**:
|
||||
- CTimer for time-based operations
|
||||
- Thread-safe collections and patterns
|
||||
- Event-driven programming models
|
||||
|
||||
## Conclusion
|
||||
|
||||
The PepperDash Essentials framework demonstrates sophisticated integration with the Crestron ecosystem, leveraging:
|
||||
|
||||
- **Core Infrastructure**: CrestronControlSystem, Device base classes
|
||||
- **Communication**: COM, IR, CEC, TCP/IP, SSH protocols
|
||||
- **Hardware Abstraction**: Touchpanels, I/O devices, processors
|
||||
- **User Interface**: Smart objects, signal processing, SIMPL bridging
|
||||
- **System Services**: Monitoring, diagnostics, device management
|
||||
|
||||
This analysis shows that Essentials serves as a comprehensive middleware layer, abstracting Crestron hardware complexities while providing modern software development patterns and practices.
|
||||
|
||||
---
|
||||
*Generated: [Current Date]*
|
||||
*Framework Version: PepperDash Essentials (Based on codebase analysis)*
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
# .NET 8 Upgrade Progress - Crestron Mocking
|
||||
|
||||
## Current Status (August 13, 2025)
|
||||
|
||||
### ✅ Completed Tasks
|
||||
1. **Namespace Migration**: Successfully migrated 26+ files from `Crestron.SimplSharp.CrestronIO` to `System.IO`
|
||||
2. **Main Projects Building**: All main solution projects (PepperDash.Essentials, PepperDash.Essentials.Core, etc.) are building successfully for .NET 8
|
||||
3. **CrestronMock Project Structure**: Established comprehensive mock structure with proper namespace hierarchy
|
||||
4. **Duplicate Definition Resolution**: Resolved 37+ duplicate type definition errors by cleaning up conflicting files
|
||||
5. **HTTP/HTTPS Client Mocks**: Implemented complete HTTP/HTTPS client mocks with proper instance-based architecture
|
||||
6. **Core Networking Mocks**: Basic TCP/UDP client/server mock implementations created
|
||||
|
||||
### 🔄 In Progress - PepperDash.Core Test Configuration
|
||||
Currently working on making PepperDash.Core build successfully with Test configuration using CrestronMock implementations.
|
||||
|
||||
#### Recent Progress:
|
||||
- ✅ Removed duplicate WebAndNetworking_New.cs file (eliminated 37 duplicate errors)
|
||||
- ✅ Cleaned duplicate type definitions from Extensions.cs
|
||||
- ✅ Implemented comprehensive HTTP/HTTPS client mocks with proper method signatures
|
||||
- ✅ Added missing TCP client properties and methods (LocalPortNumberOfClient, callback overloads)
|
||||
- 🔄 **Currently fixing**: TCPServer missing _bufferSize field and additional constructor overloads
|
||||
|
||||
#### Last Action Taken:
|
||||
Working on TCPServer.cs - added 2-parameter constructor but need to add missing `_bufferSize` private field.
|
||||
|
||||
### 🎯 Immediate Next Steps
|
||||
1. **Fix TCPServer.cs**:
|
||||
- Add missing `private int _bufferSize;` field
|
||||
- Add missing event handler properties (SocketStatusChange)
|
||||
- Add missing method overloads for SendDataAsync/ReceiveDataAsync
|
||||
|
||||
2. **Complete Remaining Mock Types**:
|
||||
- UDPServer properties (IPAddressLastMessageReceivedFrom, IPPortLastMessageReceivedFrom, IncomingDataBuffer)
|
||||
- SecureTCPServer/SecureTCPClient missing methods
|
||||
- CrestronQueue.TryToEnqueue method
|
||||
- ProgramStatusEventHandler delegate
|
||||
- Console command response methods
|
||||
|
||||
3. **System Types & Environment**:
|
||||
- InitialParametersClass properties (ApplicationNumber, RoomId, RoomName, etc.)
|
||||
- CrestronEnvironment methods (Sleep, OSVersion, GetTimeZone, etc.)
|
||||
- CrestronDataStoreStatic methods (InitCrestronDataStore, SetLocalIntValue, etc.)
|
||||
- IPAddress type and related networking types
|
||||
|
||||
### 📊 Build Status
|
||||
- **Main Projects**: ✅ All building successfully for .NET 8
|
||||
- **PepperDash.Core Test Config**: ❌ Multiple compilation errors (see below)
|
||||
- **Error Count**: ~150+ compilation errors remaining (down from 200+)
|
||||
|
||||
### 🚨 Key Error Categories Remaining
|
||||
1. **Missing Properties/Methods**: TCPClient.LocalPortNumberOfClient, UDPServer properties, etc.
|
||||
2. **Missing Types**: ProgramStatusEventHandler, SocketException, IPAddress
|
||||
3. **Method Signature Mismatches**: SendDataAsync/ReceiveDataAsync parameter counts
|
||||
4. **Enum Values**: eProgramStatusEventType.Stopping, ETHERNET_PARAMETER_TO_GET constants
|
||||
5. **Constructor Overloads**: TCPServer 2-parameter constructor, CrestronQueue constructor
|
||||
|
||||
### 📁 File Status
|
||||
#### ✅ Complete/Stable:
|
||||
- `WebAndNetworking.cs` - HTTP/HTTPS clients with proper namespace separation
|
||||
- `Extensions.cs` - CrestronInvoke and CrestronEthernetHelper (cleaned of duplicates)
|
||||
- `Console.cs` - ErrorLog, CrestronDataStoreStatic basics
|
||||
- `CrestronLogger.cs` - Proper namespace structure
|
||||
|
||||
#### 🔄 In Progress:
|
||||
- `TCPClient.cs` - Added most properties/methods, needs final validation
|
||||
- `TCPServer.cs` - Missing _bufferSize field, needs event handlers
|
||||
- `UDPServer.cs` - Missing properties and method overloads
|
||||
- `SystemTypes.cs` - Needs InitialParametersClass and CrestronEnvironment extensions
|
||||
|
||||
### 🧪 Test Strategy
|
||||
- **Transparent Mocking**: No modifications to PepperDash.Core source files required
|
||||
- **Test Configuration**: Uses CrestronMock project references instead of real Crestron libraries
|
||||
- **API Compatibility**: Mock implementations maintain identical public API surface
|
||||
|
||||
### 🔄 Command to Continue
|
||||
```bash
|
||||
cd /Users/awelker/source/pepperdash/Essentials
|
||||
dotnet build src/PepperDash.Core/PepperDash.Core.csproj -c Test --verbosity minimal
|
||||
```
|
||||
|
||||
### 📝 Notes
|
||||
- User has been manually editing files, so always check current file contents before making changes
|
||||
- Focus on Test configuration only - don't modify Debug/Release builds
|
||||
- All namespace migration work is complete and should be preserved
|
||||
- HTTP/HTTPS mocking architecture is solid and working well
|
||||
|
||||
### 🎯 Success Criteria
|
||||
Goal: Clean build of PepperDash.Core with Test configuration, enabling .NET 8 unit testing with transparent Crestron API mocking.
|
||||
@@ -36,246 +36,49 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{02EA681E-C
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Core", "src\PepperDash.Core\PepperDash.Core.csproj", "{E5336563-1194-501E-BC4A-79AD9283EF90}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "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
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Essentials.Core.Tests", "src\tests\PepperDash.Essentials.Core.Tests\PepperDash.Essentials.Core.Tests.csproj", "{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}"
|
||||
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
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Debug 4.7.2|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Debug 4.7.2|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Debug 4.7.2|x64.ActiveCfg = Debug|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Debug 4.7.2|x64.Build.0 = Debug|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Debug 4.7.2|x86.ActiveCfg = Debug|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Debug 4.7.2|x86.Build.0 = Debug|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Release|x86.Build.0 = Release|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Test|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Test|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Test|x64.ActiveCfg = Debug|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Test|x64.Build.0 = Debug|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Test|x86.ActiveCfg = Debug|Any CPU
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6}.Test|x86.Build.0 = Debug|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -287,9 +90,6 @@ Global
|
||||
{F6D362DE-2256-44B1-927A-8CE4705D839A} = {B24989D7-32B5-48D5-9AE1-5F3B17D25206}
|
||||
{B438694F-8FF7-464A-9EC8-10427374471F} = {B24989D7-32B5-48D5-9AE1-5F3B17D25206}
|
||||
{E5336563-1194-501E-BC4A-79AD9283EF90} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
||||
{01191C7B-606D-4169-81B0-BC8BC1623CE9} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
|
||||
{3EEC6E2D-ED96-4929-8BBB-C73BE499A4EB} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
|
||||
{E86229FE-9400-4F7E-B4CB-C43637FEE6A6} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {6907A4BF-7201-47CF-AAB1-3597F3B8E1C3}
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
# PepperDash Essentials Unit Testing Strategy
|
||||
|
||||
## Problem Statement
|
||||
The PepperDash Essentials framework is tightly coupled to Crestron hardware libraries that only run on Crestron devices, making it impossible to run unit tests on development machines or in CI/CD pipelines.
|
||||
|
||||
## Solution: Abstraction Layer Pattern
|
||||
|
||||
### 1. Core Abstractions Created
|
||||
We've implemented abstraction interfaces that decouple business logic from Crestron hardware:
|
||||
|
||||
- **`ICrestronControlSystem`** - Abstracts the control system hardware
|
||||
- **`IRelayPort`** - Abstracts relay functionality
|
||||
- **`IDigitalInput`** - Abstracts digital inputs with event handling
|
||||
- **`IVersiPort`** - Abstracts VersiPort I/O
|
||||
|
||||
### 2. Adapter Pattern Implementation
|
||||
Created adapter classes that wrap Crestron objects in production:
|
||||
|
||||
```csharp
|
||||
// Production code uses adapters
|
||||
var controlSystem = new CrestronControlSystemAdapter(Global.ControlSystem);
|
||||
var processor = new CrestronProcessorTestable("key", controlSystem);
|
||||
|
||||
// Test code uses mocks
|
||||
var mockControlSystem = new Mock<ICrestronControlSystem>();
|
||||
var processor = new CrestronProcessorTestable("key", mockControlSystem.Object);
|
||||
```
|
||||
|
||||
### 3. Testable Classes
|
||||
Refactored classes to accept abstractions via dependency injection:
|
||||
|
||||
- **`CrestronProcessorTestable`** - Accepts `ICrestronControlSystem`
|
||||
- **`GenericRelayDeviceTestable`** - Accepts `IRelayPort`
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Step 1: Identify Dependencies
|
||||
```bash
|
||||
# Find Crestron dependencies
|
||||
grep -r "using Crestron" --include="*.cs"
|
||||
```
|
||||
|
||||
### Step 2: Create Abstractions
|
||||
Define interfaces that mirror the Crestron API surface you need:
|
||||
```csharp
|
||||
public interface IRelayPort
|
||||
{
|
||||
void Open();
|
||||
void Close();
|
||||
void Pulse(int delayMs);
|
||||
bool State { get; }
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Implement Adapters
|
||||
Wrap Crestron objects with adapters:
|
||||
```csharp
|
||||
public class RelayPortAdapter : IRelayPort
|
||||
{
|
||||
private readonly Relay _relay;
|
||||
public void Open() => _relay.Open();
|
||||
// ... other methods
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Refactor Classes
|
||||
Accept abstractions in constructors:
|
||||
```csharp
|
||||
public class CrestronProcessorTestable
|
||||
{
|
||||
public CrestronProcessorTestable(string key, ICrestronControlSystem processor)
|
||||
{
|
||||
// Use abstraction instead of concrete type
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Write Tests
|
||||
Use mocking frameworks to test business logic:
|
||||
```csharp
|
||||
[Fact]
|
||||
public void OpenRelay_CallsRelayPortOpen()
|
||||
{
|
||||
var mockRelay = new Mock<IRelayPort>();
|
||||
var device = new GenericRelayDeviceTestable("test", mockRelay.Object);
|
||||
|
||||
device.OpenRelay();
|
||||
|
||||
mockRelay.Verify(r => r.Open(), Times.Once);
|
||||
}
|
||||
```
|
||||
|
||||
## Test Project Structure
|
||||
```
|
||||
tests/
|
||||
├── PepperDash.Essentials.Core.Tests/
|
||||
│ ├── Abstractions/ # Tests for abstraction adapters
|
||||
│ ├── Devices/ # Device-specific tests
|
||||
│ └── *.csproj # Test project file
|
||||
└── README.md # Testing documentation
|
||||
```
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
### GitHub Actions Workflow
|
||||
The `.github/workflows/ci.yml` file runs tests automatically on:
|
||||
- Push to main/develop branches
|
||||
- Pull requests
|
||||
- Generates code coverage reports
|
||||
|
||||
### Running Tests Locally
|
||||
```bash
|
||||
# Run all tests
|
||||
dotnet test
|
||||
|
||||
# Run with coverage
|
||||
dotnet test --collect:"XPlat Code Coverage"
|
||||
|
||||
# Run specific tests
|
||||
dotnet test --filter "FullyQualifiedName~CrestronProcessor"
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **Unit Testing Without Hardware** - Tests run on any machine
|
||||
2. **CI/CD Integration** - Automated testing in pipelines
|
||||
3. **Better Design** - Encourages SOLID principles
|
||||
4. **Faster Development** - No need for hardware to test logic
|
||||
5. **Higher Code Quality** - Catch bugs before deployment
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### For Existing Code
|
||||
1. Identify classes with Crestron dependencies
|
||||
2. Create abstraction interfaces
|
||||
3. Implement adapters
|
||||
4. Create testable versions accepting abstractions
|
||||
5. Write unit tests
|
||||
|
||||
### For New Code
|
||||
1. Always code against abstractions, not Crestron types
|
||||
2. Use dependency injection
|
||||
3. Write tests first (TDD approach)
|
||||
|
||||
## Current Test Coverage
|
||||
- ✅ CrestronProcessor relay management
|
||||
- ✅ GenericRelayDevice operations
|
||||
- ✅ Digital input event handling
|
||||
- ✅ VersiPort analog/digital operations
|
||||
|
||||
## Next Steps
|
||||
1. Expand abstractions for more Crestron components
|
||||
2. Increase test coverage across all modules
|
||||
3. Add integration tests with mock hardware
|
||||
4. Document testing best practices
|
||||
5. Create code generation tools for adapters
|
||||
|
||||
## Tools Used
|
||||
- **xUnit** - Test framework
|
||||
- **Moq** - Mocking library
|
||||
- **FluentAssertions** - Readable assertions
|
||||
- **Coverlet** - Code coverage
|
||||
- **GitHub Actions** - CI/CD
|
||||
|
||||
## Summary
|
||||
By introducing an abstraction layer between the business logic and Crestron hardware dependencies, we've successfully enabled unit testing for the PepperDash Essentials framework. This approach allows development and testing without physical hardware while maintaining full compatibility with Crestron systems in production.
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"runtimeOptions": {
|
||||
"configProperties": {
|
||||
"System.Globalization.Invariant": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Crestron.SimplSharp
|
||||
{
|
||||
public static class ErrorLog
|
||||
{
|
||||
public static void Error(string message, params object[] args)
|
||||
{
|
||||
Console.WriteLine($"[ERROR] {string.Format(message, args)}");
|
||||
}
|
||||
|
||||
public static void Notice(string message, params object[] args)
|
||||
{
|
||||
Console.WriteLine($"[NOTICE] {string.Format(message, args)}");
|
||||
}
|
||||
|
||||
public static void Warn(string message, params object[] args)
|
||||
{
|
||||
Console.WriteLine($"[WARN] {string.Format(message, args)}");
|
||||
}
|
||||
|
||||
public static void Info(string message, params object[] args)
|
||||
{
|
||||
Console.WriteLine($"[INFO] {string.Format(message, args)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Crestron.SimplSharp.CrestronDataStore
|
||||
{
|
||||
public static class CrestronDataStoreStatic
|
||||
{
|
||||
public static CDS_ERROR SetLocalStringValue(string key, string value)
|
||||
{
|
||||
return CDS_ERROR.CDS_SUCCESS;
|
||||
}
|
||||
|
||||
public static CDS_ERROR GetLocalStringValue(string key, out string value)
|
||||
{
|
||||
value = "";
|
||||
return CDS_ERROR.CDS_SUCCESS;
|
||||
}
|
||||
|
||||
/// <summary>Initialize the Crestron data store</summary>
|
||||
/// <returns>0 on success, negative on error</returns>
|
||||
public static int InitCrestronDataStore()
|
||||
{
|
||||
// Mock implementation
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Get a boolean value from local storage</summary>
|
||||
/// <param name="key">The key to retrieve</param>
|
||||
/// <param name="value">The retrieved value</param>
|
||||
/// <returns>0 on success, negative on error</returns>
|
||||
public static int GetLocalBoolValue(string key, out bool value)
|
||||
{
|
||||
// Mock implementation - always return false for now
|
||||
value = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Set a boolean value in local storage</summary>
|
||||
/// <param name="key">The key to set</param>
|
||||
/// <param name="value">The value to set</param>
|
||||
/// <returns>0 on success, negative on error</returns>
|
||||
public static int SetLocalBoolValue(string key, bool value)
|
||||
{
|
||||
// Mock implementation
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Get an integer value from local storage</summary>
|
||||
/// <param name="key">The key to retrieve</param>
|
||||
/// <param name="value">The retrieved value</param>
|
||||
/// <returns>0 on success, negative on error</returns>
|
||||
public static int GetLocalIntValue(string key, out int value)
|
||||
{
|
||||
// Mock implementation - always return 0 for now
|
||||
value = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Set an integer value in local storage</summary>
|
||||
/// <param name="key">The key to set</param>
|
||||
/// <param name="value">The value to set</param>
|
||||
/// <returns>0 on success, negative on error</returns>
|
||||
public static int SetLocalIntValue(string key, int value)
|
||||
{
|
||||
// Mock implementation
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Set an unsigned integer value in local storage</summary>
|
||||
/// <param name="key">The key to set</param>
|
||||
/// <param name="value">The value to set</param>
|
||||
/// <returns>0 on success, negative on error</returns>
|
||||
public static int SetLocalUintValue(string key, uint value)
|
||||
{
|
||||
// Mock implementation
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public enum CDS_ERROR
|
||||
{
|
||||
CDS_SUCCESS = 0,
|
||||
CDS_ERROR = -1
|
||||
}
|
||||
|
||||
/// <summary>Mock CrestronDataStore for local data storage</summary>
|
||||
public static class CrestronDataStore
|
||||
{
|
||||
/// <summary>Error constant for CDS operations</summary>
|
||||
public static readonly int CDS_ERROR = -1;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Crestron.SimplSharp
|
||||
{
|
||||
/// <summary>Mock console command response utility</summary>
|
||||
public static class ConsoleCommandResponseUtility
|
||||
{
|
||||
/// <summary>Send console command response with response code</summary>
|
||||
/// <param name="response">The response text</param>
|
||||
/// <param name="responseCode">The response code</param>
|
||||
public static void ConsoleCommandResponse(string response, int responseCode = 0)
|
||||
{
|
||||
// Mock implementation - just log to console or ignore
|
||||
Console.WriteLine($"Console Response ({responseCode}): {response}");
|
||||
}
|
||||
|
||||
/// <summary>Send console command response with additional parameter</summary>
|
||||
/// <param name="response">The response text</param>
|
||||
/// <param name="param1">First parameter</param>
|
||||
/// <param name="param2">Second parameter</param>
|
||||
public static void ConsoleCommandResponse(string response, object param1, object param2)
|
||||
{
|
||||
// Mock implementation
|
||||
Console.WriteLine($"Console Response: {response} - {param1}, {param2}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@@ -1,277 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Crestron.SimplSharp
|
||||
{
|
||||
// Console and logging types needed by CrestronConsole and CrestronLogger
|
||||
public delegate string ConsoleCommandFunction(string parameters);
|
||||
|
||||
public enum ConsoleAccessLevelEnum
|
||||
{
|
||||
AccessOperator = 0,
|
||||
AccessProgrammer = 1,
|
||||
AccessAdministrator = 2
|
||||
}
|
||||
|
||||
public class ConsoleCommandParameterSpecClass
|
||||
{
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
/// <summary>Ethernet event handler delegate</summary>
|
||||
public delegate void EthernetEventHandler(EthernetEventArgs args);
|
||||
|
||||
/// <summary>Mock CrestronEnvironment for system event handling</summary>
|
||||
public static class CrestronEnvironment
|
||||
{
|
||||
/// <summary>Event fired when program status changes</summary>
|
||||
public static event ProgramStatusEventHandler? ProgramStatusEventHandler;
|
||||
|
||||
/// <summary>Event fired when ethernet status changes</summary>
|
||||
public static event EthernetEventHandler? EthernetEventHandler;
|
||||
|
||||
/// <summary>Gets the device platform</summary>
|
||||
public static eDevicePlatform DevicePlatform => eDevicePlatform.Appliance;
|
||||
|
||||
/// <summary>Gets the runtime environment</summary>
|
||||
public static eRuntimeEnvironment RuntimeEnvironment => eRuntimeEnvironment.SimplSharpPro;
|
||||
|
||||
/// <summary>Gets system information</summary>
|
||||
public static string SystemInfo => "Mock System v1.0";
|
||||
|
||||
/// <summary>Gets OS version</summary>
|
||||
public static string OSVersion => "Mock OS 1.0.0";
|
||||
|
||||
/// <summary>Gets new line character sequence</summary>
|
||||
public static string NewLine => Environment.NewLine;
|
||||
|
||||
/// <summary>Gets program compatibility level</summary>
|
||||
public static eProgramCompatibility ProgramCompatibility => eProgramCompatibility.Series3And4;
|
||||
|
||||
/// <summary>Sleep for specified milliseconds</summary>
|
||||
/// <param name="milliseconds">Sleep duration</param>
|
||||
public static void Sleep(int milliseconds)
|
||||
{
|
||||
System.Threading.Thread.Sleep(milliseconds);
|
||||
}
|
||||
|
||||
/// <summary>Gets the time zone</summary>
|
||||
/// <returns>Time zone string</returns>
|
||||
public static string GetTimeZone()
|
||||
{
|
||||
return TimeZoneInfo.Local.Id;
|
||||
}
|
||||
|
||||
/// <summary>Triggers a program status event (for testing)</summary>
|
||||
/// <param name="eventType">Event type</param>
|
||||
public static void TriggerProgramStatusEvent(eProgramStatusEventType eventType)
|
||||
{
|
||||
ProgramStatusEventHandler?.Invoke(eventType);
|
||||
}
|
||||
|
||||
/// <summary>Triggers an ethernet event (for testing)</summary>
|
||||
/// <param name="args">Event arguments</param>
|
||||
public static void TriggerEthernetEvent(EthernetEventArgs args)
|
||||
{
|
||||
EthernetEventHandler?.Invoke(args);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Mock ethernet event type enumeration</summary>
|
||||
public enum eEthernetEventType
|
||||
{
|
||||
/// <summary>Link down</summary>
|
||||
LinkDown = 0,
|
||||
/// <summary>Link up</summary>
|
||||
LinkUp = 1
|
||||
}
|
||||
|
||||
/// <summary>Mock CrestronConsole for console output</summary>
|
||||
public static class CrestronConsole
|
||||
{
|
||||
/// <summary>Prints a line to the console</summary>
|
||||
/// <param name="message">Message to print</param>
|
||||
public static void PrintLine(string message)
|
||||
{
|
||||
// Mock implementation - could write to System.Console in test environment
|
||||
Console.WriteLine($"[CrestronConsole] {message}");
|
||||
}
|
||||
|
||||
/// <summary>Prints formatted text to the console</summary>
|
||||
/// <param name="format">Format string</param>
|
||||
/// <param name="args">Arguments</param>
|
||||
public static void PrintLine(string format, params object[] args)
|
||||
{
|
||||
Console.WriteLine($"[CrestronConsole] {string.Format(format, args)}");
|
||||
}
|
||||
|
||||
/// <summary>Prints text to the console without a newline</summary>
|
||||
/// <param name="message">Message to print</param>
|
||||
public static void Print(string message)
|
||||
{
|
||||
Console.Write($"[CrestronConsole] {message}");
|
||||
}
|
||||
|
||||
/// <summary>Console command response</summary>
|
||||
/// <param name="command">Command to execute</param>
|
||||
/// <returns>Response string</returns>
|
||||
public static string ConsoleCommandResponse(string command)
|
||||
{
|
||||
return $"Mock response for command: {command}";
|
||||
}
|
||||
|
||||
/// <summary>Add new console command</summary>
|
||||
/// <param name="function">Command function</param>
|
||||
/// <param name="command">Command name</param>
|
||||
/// <param name="help">Help text</param>
|
||||
/// <param name="accessLevel">Access level</param>
|
||||
/// <returns>0 for success</returns>
|
||||
public static int AddNewConsoleCommand(ConsoleCommandFunction function, string command, string help, ConsoleAccessLevelEnum accessLevel)
|
||||
{
|
||||
return 0; // Mock success
|
||||
}
|
||||
|
||||
/// <summary>Add new console command with parameter spec</summary>
|
||||
/// <param name="function">Command function</param>
|
||||
/// <param name="command">Command name</param>
|
||||
/// <param name="help">Help text</param>
|
||||
/// <param name="accessLevel">Access level</param>
|
||||
/// <param name="spec">Parameter specification</param>
|
||||
/// <returns>0 for success</returns>
|
||||
public static int AddNewConsoleCommand(ConsoleCommandFunction function, string command, string help, ConsoleAccessLevelEnum accessLevel, ConsoleCommandParameterSpecClass spec)
|
||||
{
|
||||
return 0; // Mock success
|
||||
}
|
||||
|
||||
/// <summary>Send control system command</summary>
|
||||
/// <param name="command">Command to send</param>
|
||||
/// <param name="programNumber">Program number</param>
|
||||
public static void SendControlSystemCommand(string command, uint programNumber)
|
||||
{
|
||||
// Mock implementation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Crestron.SimplSharp.CrestronIO
|
||||
{
|
||||
/// <summary>Mock File class for basic file operations</summary>
|
||||
public static class File
|
||||
{
|
||||
/// <summary>Checks if a file exists</summary>
|
||||
/// <param name="path">File path</param>
|
||||
/// <returns>True if file exists</returns>
|
||||
public static bool Exists(string path)
|
||||
{
|
||||
// Mock implementation - use System.IO.File for actual file operations
|
||||
return System.IO.File.Exists(path);
|
||||
}
|
||||
|
||||
/// <summary>Reads all text from a file</summary>
|
||||
/// <param name="path">File path</param>
|
||||
/// <returns>File contents</returns>
|
||||
public static string ReadToEnd(string path)
|
||||
{
|
||||
return System.IO.File.ReadAllText(path);
|
||||
}
|
||||
|
||||
/// <summary>Reads all text from a file with specified encoding</summary>
|
||||
/// <param name="path">File path</param>
|
||||
/// <param name="encoding">Text encoding</param>
|
||||
/// <returns>File contents</returns>
|
||||
public static string ReadToEnd(string path, System.Text.Encoding encoding)
|
||||
{
|
||||
return System.IO.File.ReadAllText(path, encoding);
|
||||
}
|
||||
|
||||
/// <summary>Writes text to a file</summary>
|
||||
/// <param name="path">File path</param>
|
||||
/// <param name="contents">Contents to write</param>
|
||||
public static void WriteAllText(string path, string contents)
|
||||
{
|
||||
System.IO.File.WriteAllText(path, contents);
|
||||
}
|
||||
|
||||
/// <summary>Deletes a file</summary>
|
||||
/// <param name="path">File path</param>
|
||||
public static void Delete(string path)
|
||||
{
|
||||
if (System.IO.File.Exists(path))
|
||||
System.IO.File.Delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Mock Directory class for basic directory operations</summary>
|
||||
public static class Directory
|
||||
{
|
||||
/// <summary>Gets the application directory path</summary>
|
||||
/// <returns>Application directory path</returns>
|
||||
public static string GetApplicationDirectory()
|
||||
{
|
||||
// Mock implementation - return current directory or a typical Crestron path
|
||||
return System.IO.Directory.GetCurrentDirectory();
|
||||
}
|
||||
|
||||
/// <summary>Gets the application root directory path</summary>
|
||||
/// <returns>Application root directory path</returns>
|
||||
public static string GetApplicationRootDirectory()
|
||||
{
|
||||
// Mock implementation - return current directory or a typical Crestron path
|
||||
return System.IO.Directory.GetCurrentDirectory();
|
||||
}
|
||||
|
||||
/// <summary>Checks if a directory exists</summary>
|
||||
/// <param name="path">Directory path</param>
|
||||
/// <returns>True if directory exists</returns>
|
||||
public static bool Exists(string path)
|
||||
{
|
||||
return System.IO.Directory.Exists(path);
|
||||
}
|
||||
|
||||
/// <summary>Creates a directory</summary>
|
||||
/// <param name="path">Directory path</param>
|
||||
public static void CreateDirectory(string path)
|
||||
{
|
||||
System.IO.Directory.CreateDirectory(path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Mock Path class for path operations</summary>
|
||||
public static class Path
|
||||
{
|
||||
/// <summary>Directory separator character</summary>
|
||||
public static readonly char DirectorySeparatorChar = System.IO.Path.DirectorySeparatorChar;
|
||||
|
||||
/// <summary>Combines path strings</summary>
|
||||
/// <param name="path1">First path</param>
|
||||
/// <param name="path2">Second path</param>
|
||||
/// <returns>Combined path</returns>
|
||||
public static string Combine(string path1, string path2)
|
||||
{
|
||||
return System.IO.Path.Combine(path1, path2);
|
||||
}
|
||||
|
||||
/// <summary>Gets the file name from a path</summary>
|
||||
/// <param name="path">Full path</param>
|
||||
/// <returns>File name</returns>
|
||||
public static string GetFileName(string path)
|
||||
{
|
||||
return System.IO.Path.GetFileName(path);
|
||||
}
|
||||
|
||||
/// <summary>Gets the directory name from a path</summary>
|
||||
/// <param name="path">Full path</param>
|
||||
/// <returns>Directory name</returns>
|
||||
public static string GetDirectoryName(string path)
|
||||
{
|
||||
return System.IO.Path.GetDirectoryName(path) ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>Gets the file extension from a path</summary>
|
||||
/// <param name="path">Full path</param>
|
||||
/// <returns>File extension</returns>
|
||||
public static string GetExtension(string path)
|
||||
{
|
||||
return System.IO.Path.GetExtension(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Crestron.SimplSharp.CrestronLogger
|
||||
{
|
||||
/// <summary>Mock CrestronLogger for .NET 8 compatibility</summary>
|
||||
public static class CrestronLogger
|
||||
{
|
||||
/// <summary>Write to log</summary>
|
||||
/// <param name="logName">Log name</param>
|
||||
/// <param name="message">Message</param>
|
||||
/// <param name="mode">Logger mode</param>
|
||||
public static void WriteToLog(string logName, string message, LoggerModeEnum mode)
|
||||
{
|
||||
Console.WriteLine($"[{logName}] {message}");
|
||||
}
|
||||
|
||||
/// <summary>Write to log with level</summary>
|
||||
/// <param name="message">Message</param>
|
||||
/// <param name="level">Log level</param>
|
||||
public static void WriteToLog(string message, uint level)
|
||||
{
|
||||
Console.WriteLine($"[Level {level}] {message}");
|
||||
}
|
||||
|
||||
/// <summary>Initialize logger</summary>
|
||||
/// <param name="bufferSize">Buffer size</param>
|
||||
/// <param name="mode">Logger mode</param>
|
||||
public static void Initialize(int bufferSize, LoggerModeEnum mode)
|
||||
{
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
/// <summary>Print the log</summary>
|
||||
/// <param name="includeAll">Include all log entries</param>
|
||||
/// <returns>Log entries as string list</returns>
|
||||
public static List<string> PrintTheLog(bool includeAll = false)
|
||||
{
|
||||
return new List<string> { "Mock log entry" };
|
||||
}
|
||||
|
||||
/// <summary>Clear the log</summary>
|
||||
/// <param name="clearAll">Clear all entries</param>
|
||||
/// <returns>Success message</returns>
|
||||
public static string Clear(bool clearAll)
|
||||
{
|
||||
return "Log cleared (mock)";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Logger mode enumeration</summary>
|
||||
public enum LoggerModeEnum
|
||||
{
|
||||
/// <summary>Append mode</summary>
|
||||
LoggingModeAppend = 0,
|
||||
/// <summary>Overwrite mode</summary>
|
||||
LoggingModeOverwrite = 1,
|
||||
/// <summary>RM mode</summary>
|
||||
RM = 2
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Configurations>Debug;Release;Test</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,245 +0,0 @@
|
||||
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
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the CrestronQueue class with specified capacity</summary>
|
||||
/// <param name="capacity">The initial capacity of the queue</param>
|
||||
public CrestronQueue(int capacity)
|
||||
{
|
||||
// Mock implementation - capacity is ignored in this mock
|
||||
}
|
||||
|
||||
#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
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Crestron.SimplSharp
|
||||
{
|
||||
/// <summary>Mock eProgramStatusEventType enumeration</summary>
|
||||
public enum eProgramStatusEventType
|
||||
{
|
||||
/// <summary>Program stopping</summary>
|
||||
Stopping = 0,
|
||||
/// <summary>Program started</summary>
|
||||
Starting = 1,
|
||||
/// <summary>Program running</summary>
|
||||
Running = 2,
|
||||
/// <summary>Program paused</summary>
|
||||
Paused = 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>Gets the ethernet event type</summary>
|
||||
public eEthernetEventType EthernetEventType { 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;
|
||||
EthernetEventType = linkUp ? eEthernetEventType.LinkUp : eEthernetEventType.LinkDown;
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
|
||||
// Event handler delegates
|
||||
/// <summary>Ethernet event handler delegate</summary>
|
||||
public delegate void EthernetEventHandler(EthernetEventArgs args);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threa case ETHERNET_PARAMETER_TO_GET.ETHERNET_HOSTNAME:
|
||||
return "mock-hostname";
|
||||
case ETHERNET_PARAMETER_TO_GET.ETHERNET_MAC_ADDRESS:
|
||||
return "00:11:22:33:44:55";
|
||||
case ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME:
|
||||
return "mock-domain.local";
|
||||
default:
|
||||
return string.Empty; asks;
|
||||
|
||||
namespace Crestron.SimplSharp
|
||||
{
|
||||
public static class CrestronInvoke
|
||||
{
|
||||
public static void BeginInvoke(Func<object> func, object? state = null)
|
||||
{
|
||||
Task.Run(func);
|
||||
}
|
||||
|
||||
public static void BeginInvoke(Action action)
|
||||
{
|
||||
Task.Run(action);
|
||||
}
|
||||
}
|
||||
|
||||
public static class CrestronEthernetHelper
|
||||
{
|
||||
/// <summary>Ethernet parameter enumeration</summary>
|
||||
public enum ETHERNET_PARAMETER_TO_GET
|
||||
{
|
||||
ETHERNET_HOSTNAME = 0,
|
||||
ETHERNET_DOMAIN_NAME = 1,
|
||||
ETHERNET_IP_ADDRESS = 2,
|
||||
ETHERNET_SUBNET_MASK = 3,
|
||||
ETHERNET_GATEWAY = 4,
|
||||
ETHERNET_DNS_SERVER = 5,
|
||||
ETHERNET_MAC_ADDRESS = 6,
|
||||
ETHERNET_DHCP_STATUS = 7,
|
||||
GET_CURRENT_DHCP_STATE = 8,
|
||||
GET_CURRENT_IP_ADDRESS = 9,
|
||||
GET_CURRENT_IP_MASK = 10,
|
||||
GET_CURRENT_ROUTER = 11,
|
||||
GET_HOSTNAME = 12,
|
||||
GET_LINK_STATUS = 13,
|
||||
GET_DOMAIN_NAME = 14
|
||||
}
|
||||
|
||||
public static List<string> GetEthernetAdaptersInfo()
|
||||
{
|
||||
return new List<string> { "MockAdapter" };
|
||||
}
|
||||
|
||||
public static string GetEthernetParameter(string adapter, string parameter)
|
||||
{
|
||||
return "MockValue";
|
||||
}
|
||||
|
||||
/// <summary>Get ethernet parameter as string</summary>
|
||||
/// <param name="parameter">The parameter to get</param>
|
||||
/// <param name="adapterType">The adapter type</param>
|
||||
/// <returns>The parameter value as string</returns>
|
||||
public static string GetEthernetParameter(ETHERNET_PARAMETER_TO_GET parameter, EthernetAdapterType adapterType)
|
||||
{
|
||||
// Mock implementation
|
||||
switch (parameter)
|
||||
{
|
||||
case ETHERNET_PARAMETER_TO_GET.ETHERNET_IP_ADDRESS:
|
||||
return "192.168.1.100";
|
||||
case ETHERNET_PARAMETER_TO_GET.ETHERNET_SUBNET_MASK:
|
||||
return "255.255.255.0";
|
||||
case ETHERNET_PARAMETER_TO_GET.ETHERNET_GATEWAY:
|
||||
return "192.168.1.1";
|
||||
case ETHERNET_PARAMETER_TO_GET.ETHERNET_HOSTNAME:
|
||||
return "MockHost";
|
||||
case ETHERNET_PARAMETER_TO_GET.ETHERNET_MAC_ADDRESS:
|
||||
return "00:11:22:33:44:55";
|
||||
default:
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Get adapter ID for specified adapter type</summary>
|
||||
/// <param name="adapterType">The adapter type</param>
|
||||
/// <returns>The adapter ID</returns>
|
||||
public static int GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType adapterType)
|
||||
{
|
||||
// Mock implementation
|
||||
return (int)adapterType;
|
||||
}
|
||||
|
||||
/// <summary>Check if control subnet is in automatic mode</summary>
|
||||
/// <param name="adapterId">The adapter ID</param>
|
||||
/// <returns>True if in automatic mode</returns>
|
||||
public static bool IsControlSubnetInAutomaticMode(int adapterId)
|
||||
{
|
||||
// Mock implementation
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Mock EthernetAdapterType enumeration</summary>
|
||||
public enum EthernetAdapterType
|
||||
{
|
||||
/// <summary>Ethernet LAN adapter</summary>
|
||||
EthernetLANAdapter = 0,
|
||||
/// <summary>Control subnet adapter</summary>
|
||||
ControlSubnet = 1,
|
||||
/// <summary>Auto-detect adapter</summary>
|
||||
EthernetAdapterAuto = 2
|
||||
}
|
||||
}
|
||||
@@ -1,407 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Crestron.SimplSharp.WebScripting
|
||||
{
|
||||
/// <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());
|
||||
}
|
||||
|
||||
/// <summary>Unregisters a route handler</summary>
|
||||
/// <param name="route">Route path to unregister</param>
|
||||
public void Unregister(string route)
|
||||
{
|
||||
RemoveRoute(route);
|
||||
}
|
||||
|
||||
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 status description</summary>
|
||||
public string StatusDescription
|
||||
{
|
||||
get => _response.StatusDescription;
|
||||
set => _response.StatusDescription = 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>Ends the response</summary>
|
||||
public void End()
|
||||
{
|
||||
try
|
||||
{
|
||||
_response.Close();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignore exceptions during close
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace Crestron.SimplSharp.CrestronSockets
|
||||
{
|
||||
// Additional types needed for networking compatibility
|
||||
|
||||
/// <summary>IP address extensions and utilities</summary>
|
||||
public static class IPAddress
|
||||
{
|
||||
/// <summary>Parse IP address string</summary>
|
||||
public static System.Net.IPAddress Parse(string ipString)
|
||||
{
|
||||
return System.Net.IPAddress.Parse(ipString);
|
||||
}
|
||||
|
||||
/// <summary>Any IP address</summary>
|
||||
public static System.Net.IPAddress Any => System.Net.IPAddress.Any;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Crestron.SimplSharp
|
||||
{
|
||||
/// <summary>Extensions for CrestronQueue</summary>
|
||||
public static class CrestronQueueExtensions
|
||||
{
|
||||
/// <summary>Try to enqueue item</summary>
|
||||
public static bool TryToEnqueue<T>(this CrestronQueue<T> queue, T item)
|
||||
{
|
||||
try
|
||||
{
|
||||
queue.Enqueue(item);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Crestron.SimplSharp.CrestronSockets
|
||||
{
|
||||
/// <summary>Mock implementation of Crestron SecureTCPClient for testing purposes</summary>
|
||||
public class SecureTCPClient : TCPClient
|
||||
{
|
||||
/// <summary>Initializes a new instance of the SecureTCPClient 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 SecureTCPClient(string addressToConnectTo, int portNumber, int bufferSize)
|
||||
: base(addressToConnectTo, portNumber, bufferSize)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets whether to verify the host certificate</summary>
|
||||
public bool HostVerification { get; set; } = true;
|
||||
|
||||
/// <summary>Gets or sets whether to verify the peer certificate</summary>
|
||||
public bool PeerVerification { get; set; } = true;
|
||||
|
||||
/// <summary>Resets the client connection</summary>
|
||||
/// <param name="connectionFlag">Connection flag</param>
|
||||
public void Reset(int connectionFlag)
|
||||
{
|
||||
// Mock implementation
|
||||
DisconnectFromServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
|
||||
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,
|
||||
/// <summary>Connection in progress</summary>
|
||||
SOCKET_CONNECTION_IN_PROGRESS = 16
|
||||
}
|
||||
|
||||
/// <summary>Mock socket exception</summary>
|
||||
public class SocketException : Exception
|
||||
{
|
||||
/// <summary>Error code</summary>
|
||||
public int ErrorCode { get; }
|
||||
|
||||
/// <summary>Constructor with error code</summary>
|
||||
public SocketException(int errorCode, string message) : base(message)
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
}
|
||||
|
||||
/// <summary>Constructor with message only</summary>
|
||||
public SocketException(string message) : base(message)
|
||||
{
|
||||
ErrorCode = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Crestron.SimplSharp
|
||||
{
|
||||
public delegate void ProgramStatusEventHandler(eProgramStatusEventType eventType);
|
||||
|
||||
public class InitialParametersClass
|
||||
{
|
||||
public static string ApplicationDirectory { get; set; } = "/User/";
|
||||
public static string ProgramIDTag { get; set; } = "MockProgram";
|
||||
public static string ApplicationName { get; set; } = "MockApplication";
|
||||
public static string FirmwareVersion { get; set; } = "1.0.0.0";
|
||||
public static uint ProgramNumber { get; set; } = 1;
|
||||
public static eDevicePlatform DevicePlatform { get; set; } = eDevicePlatform.Appliance;
|
||||
public static eCrestronSeries ControllerSeries { get; set; } = eCrestronSeries.FourSeries;
|
||||
|
||||
// Additional properties needed by PepperDash.Core
|
||||
public static string RoomId { get; set; } = "Room001";
|
||||
public static string RoomName { get; set; } = "Conference Room";
|
||||
public static uint ApplicationNumber { get; set; } = 1;
|
||||
public static string ControllerPromptName { get; set; } = "TestController";
|
||||
public static string ProgramDirectory { get; set; } = "/User/";
|
||||
}
|
||||
|
||||
public enum eDevicePlatform
|
||||
{
|
||||
Appliance = 0,
|
||||
Server = 1,
|
||||
ControlSystem = 2
|
||||
}
|
||||
|
||||
public enum eCrestronSeries
|
||||
{
|
||||
TwoSeries = 2,
|
||||
ThreeSeries = 3,
|
||||
FourSeries = 4,
|
||||
// Alias names used in some contexts
|
||||
Series2 = 2,
|
||||
Series3 = 3,
|
||||
Series4 = 4
|
||||
}
|
||||
|
||||
public enum eRuntimeEnvironment
|
||||
{
|
||||
SimplSharpPro = 0,
|
||||
SimplSharp = 1
|
||||
}
|
||||
|
||||
public enum eProgramCompatibility
|
||||
{
|
||||
Series3And4 = 0,
|
||||
Series3Only = 1,
|
||||
Series4Only = 2
|
||||
}
|
||||
|
||||
public static class Timeout
|
||||
{
|
||||
public const int Infinite = -1;
|
||||
}
|
||||
}
|
||||
@@ -1,360 +0,0 @@
|
||||
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>No connection available</summary>
|
||||
SOCKET_STATUS_NO_CONNECT = 10
|
||||
}
|
||||
|
||||
/// <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>Server is connected</summary>
|
||||
SERVER_CONNECTED = 2
|
||||
}
|
||||
|
||||
/// <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>Delegate for TCP client connect callback</summary>
|
||||
/// <param name="client">TCP client instance</param>
|
||||
public delegate void TCPClientConnectCallback(TCPClient client);
|
||||
|
||||
/// <summary>Delegate for TCP client send callback</summary>
|
||||
/// <param name="client">TCP client instance</param>
|
||||
/// <param name="numberOfBytesSent">Number of bytes sent</param>
|
||||
public delegate void TCPClientSendCallback(TCPClient client, int numberOfBytesSent);
|
||||
|
||||
/// <summary>Delegate for TCP client receive callback</summary>
|
||||
/// <param name="client">TCP client instance</param>
|
||||
/// <param name="numberOfBytesReceived">Number of bytes received</param>
|
||||
public delegate void TCPClientReceiveCallback(TCPClient client, int numberOfBytesReceived);
|
||||
|
||||
/// <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;
|
||||
|
||||
/// <summary>Gets or sets the socket send or receive timeout in milliseconds</summary>
|
||||
public int SocketSendOrReceiveTimeOutInMs
|
||||
{
|
||||
get => SocketSendTimeout;
|
||||
set => SocketSendTimeout = SocketReceiveTimeout = value;
|
||||
}
|
||||
|
||||
/// <summary>Gets the address the client is connected to</summary>
|
||||
public string AddressClientConnectedTo { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Gets the local port number of the client</summary>
|
||||
public uint LocalPortNumberOfClient { get; private set; } = 0;
|
||||
|
||||
/// <summary>Gets the incoming data buffer</summary>
|
||||
public byte[] IncomingDataBuffer { get; private set; } = new byte[0];
|
||||
|
||||
#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;
|
||||
AddressClientConnectedTo = AddressToConnectTo;
|
||||
SocketStatusChange?.Invoke(this, ClientStatus);
|
||||
return ClientStatus;
|
||||
}
|
||||
|
||||
/// <summary>Connects to the remote endpoint asynchronously with callback</summary>
|
||||
/// <param name="callback">Callback to invoke when connection completes</param>
|
||||
/// <returns>Status of the connection attempt</returns>
|
||||
public SocketStatus ConnectToServerAsync(TCPClientConnectCallback callback)
|
||||
{
|
||||
var status = ConnectToServerAsync();
|
||||
callback?.Invoke(this);
|
||||
return status;
|
||||
}
|
||||
|
||||
/// <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>Sends data to the connected server asynchronously</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 SendDataAsync(byte[] dataToSend, int lengthToSend)
|
||||
{
|
||||
return SendData(dataToSend, lengthToSend);
|
||||
}
|
||||
|
||||
/// <summary>Sends data to the connected server asynchronously with callback</summary>
|
||||
/// <param name="dataToSend">Data to send as byte array</param>
|
||||
/// <param name="lengthToSend">Number of bytes to send</param>
|
||||
/// <param name="callback">Callback to invoke when send completes</param>
|
||||
/// <returns>Number of bytes sent, or -1 on error</returns>
|
||||
public int SendDataAsync(byte[] dataToSend, int lengthToSend, TCPClientSendCallback callback)
|
||||
{
|
||||
var result = SendData(dataToSend, lengthToSend);
|
||||
callback?.Invoke(this, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>Receives data from the server asynchronously</summary>
|
||||
/// <returns>Number of bytes received, or -1 on error</returns>
|
||||
public int ReceiveDataAsync()
|
||||
{
|
||||
if (_disposed) return -1;
|
||||
if (ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED) return -1;
|
||||
|
||||
// Mock receive - simulate no data available
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Receives data from the server asynchronously with callback</summary>
|
||||
/// <param name="callback">Callback to invoke when data is received</param>
|
||||
/// <returns>Number of bytes received, or -1 on error</returns>
|
||||
public int ReceiveDataAsync(TCPClientReceiveCallback callback)
|
||||
{
|
||||
var result = ReceiveDataAsync();
|
||||
callback?.Invoke(this, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <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
|
||||
}
|
||||
}
|
||||
@@ -1,552 +0,0 @@
|
||||
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();
|
||||
private int _bufferSize = 4096;
|
||||
private CancellationTokenSource? _cancellationTokenSource;
|
||||
|
||||
/// <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>Event fired when socket status changes</summary>
|
||||
public event TCPServerWaitingForConnectionsEventHandler? SocketStatusChange;
|
||||
|
||||
/// <summary>Gets the server state</summary>
|
||||
public ServerState State { get; private set; } = ServerState.SERVER_NOT_LISTENING;
|
||||
|
||||
/// <summary>Gets or sets the port number</summary>
|
||||
public int PortNumber { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the socket send or receive timeout in milliseconds</summary>
|
||||
public int SocketSendOrReceiveTimeOutInMs { get; set; } = 30000;
|
||||
|
||||
/// <summary>Gets the server socket status based on current state</summary>
|
||||
public SocketStatus ServerSocketStatus => State == ServerState.SERVER_LISTENING ? SocketStatus.SOCKET_STATUS_CONNECTED : SocketStatus.SOCKET_STATUS_NOT_CONNECTED; /// <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>Creates a TCP server with IP address binding</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 incoming data</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;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
/// <summary>Creates a TCP server</summary>
|
||||
/// <param name="portNumber">Port number to listen on</param>
|
||||
/// <param name="bufferSize">Buffer size for incoming data</param>
|
||||
/// <param name="maxNumberOfClientSupported">Maximum number of clients</param>
|
||||
public TCPServer(int portNumber, int bufferSize, int maxNumberOfClientSupported)
|
||||
{
|
||||
PortNumber = portNumber;
|
||||
MaxNumberOfClientSupported = maxNumberOfClientSupported;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
/// <summary>Creates a TCP server with just port and max clients</summary>
|
||||
/// <param name="portNumber">Port number to listen on</param>
|
||||
/// <param name="maxNumberOfClientSupported">Maximum number of clients</param>
|
||||
public TCPServer(int portNumber, int maxNumberOfClientSupported)
|
||||
{
|
||||
PortNumber = portNumber;
|
||||
MaxNumberOfClientSupported = maxNumberOfClientSupported;
|
||||
_bufferSize = 4096; // Default buffer size
|
||||
}
|
||||
|
||||
/// <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 = ServerState.SERVER_LISTENING;
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
// Start accepting clients in background
|
||||
_ = Task.Run(() => AcceptClientsAsync());
|
||||
|
||||
return SocketErrorCodes.SOCKET_OK;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
State = ServerState.SERVER_NOT_LISTENING;
|
||||
return SocketErrorCodes.SOCKET_CONNECTION_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Starts listening for connections asynchronously with callback</summary>
|
||||
/// <param name="ipAddress">IP address to listen on</param>
|
||||
/// <param name="callback">Callback for connection events</param>
|
||||
/// <returns>SocketErrorCodes indicating success or failure</returns>
|
||||
public SocketErrorCodes WaitForConnectionAsync(string ipAddress, TCPServerWaitingForConnectionsEventHandler callback)
|
||||
{
|
||||
SocketStatusChange += callback;
|
||||
return WaitForConnectionAsync();
|
||||
}
|
||||
|
||||
/// <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 = ServerState.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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets the socket status for a specific client</summary>
|
||||
/// <param name="clientIndex">Index of client</param>
|
||||
/// <returns>Socket status</returns>
|
||||
public SocketStatus GetServerSocketStatusForSpecificClient(uint clientIndex)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (clientIndex >= _clients.Count)
|
||||
return SocketStatus.SOCKET_STATUS_NOT_CONNECTED;
|
||||
|
||||
return _clients[(int)clientIndex].IsConnected ?
|
||||
SocketStatus.SOCKET_STATUS_CONNECTED :
|
||||
SocketStatus.SOCKET_STATUS_NOT_CONNECTED;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets the port number the server accepted connection from for a specific client</summary>
|
||||
/// <param name="clientIndex">Index of client</param>
|
||||
/// <returns>Port number</returns>
|
||||
public int GetPortNumberServerAcceptedConnectionFromForSpecificClient(uint clientIndex)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (clientIndex >= _clients.Count)
|
||||
return 0;
|
||||
|
||||
return _clients[(int)clientIndex].ClientPort;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets the local address the server accepted connection from for a specific client</summary>
|
||||
/// <param name="clientIndex">Index of client</param>
|
||||
/// <returns>Local address</returns>
|
||||
public string GetLocalAddressServerAcceptedConnectionFromForSpecificClient(uint clientIndex)
|
||||
{
|
||||
return "127.0.0.1"; // Mock local address
|
||||
}
|
||||
|
||||
/// <summary>Gets the incoming data buffer for a specific client</summary>
|
||||
/// <param name="clientIndex">Index of client</param>
|
||||
/// <returns>Incoming data buffer</returns>
|
||||
public byte[] GetIncomingDataBufferForSpecificClient(uint clientIndex)
|
||||
{
|
||||
return new byte[0]; // Mock empty buffer
|
||||
}
|
||||
|
||||
/// <summary>Sends data to a specific client asynchronously</summary>
|
||||
/// <param name="data">Data to send</param>
|
||||
/// <param name="dataLength">Length of data to send</param>
|
||||
/// <param name="clientIndex">Index of client</param>
|
||||
/// <returns>SocketErrorCodes indicating success or failure</returns>
|
||||
public SocketErrorCodes SendDataAsync(byte[] data, int dataLength, uint clientIndex)
|
||||
{
|
||||
return SendData(data, dataLength, clientIndex);
|
||||
}
|
||||
|
||||
/// <summary>Receives data from a specific client asynchronously</summary>
|
||||
/// <param name="clientIndex">Index of client</param>
|
||||
/// <returns>SocketErrorCodes indicating success or failure</returns>
|
||||
public SocketErrorCodes ReceiveDataAsync(uint clientIndex)
|
||||
{
|
||||
// Mock implementation - no data to receive
|
||||
return SocketErrorCodes.SOCKET_OK;
|
||||
}
|
||||
|
||||
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>Initializes a new instance of SecureTCPServer</summary>
|
||||
/// <param name="portNumber">Port number to listen on</param>
|
||||
/// <param name="maxNumberOfClientSupported">Maximum number of clients</param>
|
||||
public SecureTCPServer(int portNumber, int maxNumberOfClientSupported)
|
||||
: base(portNumber, 4096, maxNumberOfClientSupported) // Default buffer size
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets the handshake timeout in seconds</summary>
|
||||
public int HandshakeTimeout { get; set; } = 30;
|
||||
|
||||
/// <summary>Event raised when socket status changes with client details</summary>
|
||||
public event SecureTCPServerSocketStatusEventHandler? SocketStatusChangeWithClientDetails;
|
||||
}
|
||||
|
||||
/// <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 bool IsConnected => _connected;
|
||||
public int ClientPort { 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";
|
||||
ClientPort = endpoint?.Port ?? 0;
|
||||
|
||||
_ = 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,
|
||||
/// <summary>Server is connected</summary>
|
||||
SERVER_CONNECTED = 2
|
||||
}
|
||||
|
||||
// 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);
|
||||
public delegate void SecureTCPServerSocketStatusChangeEventHandler(SecureTCPServer server, TCPServerWaitingForConnectionsEventArgs args);
|
||||
/// <summary>Delegate for secure TCP server socket status changes with client details</summary>
|
||||
public delegate void SecureTCPServerSocketStatusEventHandler(SecureTCPServer server, uint clientIndex, SocketStatus serverSocketStatus);
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
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 server status (alias for State)</summary>
|
||||
public SocketServerState ServerStatus => State;
|
||||
|
||||
/// <summary>Gets the client status as SocketStatus</summary>
|
||||
public SocketStatus ClientStatus => State == SocketServerState.SERVER_LISTENING ? SocketStatus.SOCKET_STATUS_CONNECTED : SocketStatus.SOCKET_STATUS_NOT_CONNECTED;
|
||||
|
||||
/// <summary>Gets or sets the port number</summary>
|
||||
public int PortNumber { get; set; }
|
||||
|
||||
/// <summary>Gets the buffer size</summary>
|
||||
public int BufferSize { get; private set; }
|
||||
|
||||
/// <summary>Gets the IP address of the last message received from</summary>
|
||||
public string IPAddressLastMessageReceivedFrom { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>Gets the IP port of the last message received from</summary>
|
||||
public int IPPortLastMessageReceivedFrom { get; private set; }
|
||||
|
||||
/// <summary>Gets the incoming data buffer</summary>
|
||||
public byte[] IncomingDataBuffer { get; private set; } = new byte[0];
|
||||
|
||||
/// <summary>Initializes a new instance of UDPServer</summary>
|
||||
public UDPServer()
|
||||
{
|
||||
PortNumber = 0;
|
||||
BufferSize = 1024;
|
||||
}
|
||||
|
||||
/// <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>Starts listening for UDP packets on specified hostname and port</summary>
|
||||
/// <param name="hostname">Hostname to bind to</param>
|
||||
/// <param name="port">Port number to listen on</param>
|
||||
/// <returns>SocketErrorCodes indicating success or failure</returns>
|
||||
public SocketErrorCodes EnableUDPServer(string hostname, int port)
|
||||
{
|
||||
PortNumber = port;
|
||||
return EnableUDPServer();
|
||||
}
|
||||
|
||||
/// <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 the last received endpoint</summary>
|
||||
/// <param name="data">Data to send</param>
|
||||
/// <param name="dataLength">Length of data</param>
|
||||
/// <returns>SocketErrorCodes indicating success or failure</returns>
|
||||
public SocketErrorCodes SendData(byte[] data, int dataLength)
|
||||
{
|
||||
return SendData(data, dataLength, IPAddressLastMessageReceivedFrom, IPPortLastMessageReceivedFrom);
|
||||
}
|
||||
|
||||
/// <summary>Receives data asynchronously</summary>
|
||||
/// <param name="callback">Callback to invoke when data is received</param>
|
||||
/// <returns>SocketErrorCodes indicating success or failure</returns>
|
||||
public SocketErrorCodes ReceiveDataAsync(UDPServerReceiveDataEventHandler callback)
|
||||
{
|
||||
ReceivedData += callback;
|
||||
return SocketErrorCodes.SOCKET_OK;
|
||||
}
|
||||
|
||||
/// <summary>Receives data asynchronously with simple callback</summary>
|
||||
/// <param name="callback">Simple callback to invoke when data is received</param>
|
||||
/// <returns>SocketErrorCodes indicating success or failure</returns>
|
||||
public SocketErrorCodes ReceiveDataAsync(UDPServerReceiveDataSimpleEventHandler callback)
|
||||
{
|
||||
// Convert simple callback to full event handler and subscribe
|
||||
ReceivedData += (server, args) => callback(server, args.DataLength);
|
||||
return SocketErrorCodes.SOCKET_OK;
|
||||
}
|
||||
|
||||
/// <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();
|
||||
}
|
||||
}
|
||||
|
||||
// Event handler delegates for UDP
|
||||
public delegate void UDPServerReceiveDataEventHandler(UDPServer server, UDPServerReceiveDataEventArgs args);
|
||||
public delegate void UDPServerReceiveDataSimpleEventHandler(UDPServer server, int numBytes);
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Crestron.SimplSharp.Net.Http
|
||||
{
|
||||
/// <summary>Mock UrlParser for HTTP</summary>
|
||||
public static class UrlParser
|
||||
{
|
||||
/// <summary>Parse a URL string</summary>
|
||||
/// <param name="url">URL to parse</param>
|
||||
/// <returns>Parsed URL components</returns>
|
||||
public static UrlParserResult Parse(string url)
|
||||
{
|
||||
return new UrlParserResult { Url = url };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>URL parser result</summary>
|
||||
public class UrlParserResult
|
||||
{
|
||||
/// <summary>Original URL</summary>
|
||||
public string Url { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Crestron.SimplSharp.Net.Https
|
||||
{
|
||||
/// <summary>Mock UrlParser for HTTPS - different from HTTP version</summary>
|
||||
public static class UrlParser
|
||||
{
|
||||
/// <summary>Parse a URL string</summary>
|
||||
/// <param name="url">URL to parse</param>
|
||||
/// <returns>Parsed URL components</returns>
|
||||
public static UrlParserResult Parse(string url)
|
||||
{
|
||||
return new UrlParserResult { Url = url };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>HTTPS URL parser result</summary>
|
||||
public class UrlParserResult
|
||||
{
|
||||
/// <summary>Original URL</summary>
|
||||
public string Url { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Crestron.SimplSharp.Net.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 class HttpClient
|
||||
{
|
||||
/// <summary>Gets or sets the keep-alive setting</summary>
|
||||
public bool KeepAlive { get; set; } = false;
|
||||
|
||||
/// <summary>Gets or sets the port number</summary>
|
||||
public int Port { get; set; } = 80;
|
||||
|
||||
/// <summary>Dispatch HTTP request</summary>
|
||||
/// <param name="request">HTTP request</param>
|
||||
/// <param name="callback">Callback for response</param>
|
||||
public void Dispatch(HttpClientRequest request, Action<HttpClientResponse> callback)
|
||||
{
|
||||
// Mock implementation - invoke callback with empty response
|
||||
var response = new HttpClientResponse();
|
||||
callback?.Invoke(response);
|
||||
}
|
||||
|
||||
/// <summary>Dispatches HTTP request synchronously</summary>
|
||||
/// <param name="request">HTTP request</param>
|
||||
/// <returns>HTTP response</returns>
|
||||
public HttpClientResponse Dispatch(HttpClientRequest request)
|
||||
{
|
||||
// Mock implementation - return empty response
|
||||
return new HttpClientResponse();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Mock HTTP client request</summary>
|
||||
public class HttpClientRequest
|
||||
{
|
||||
/// <summary>Gets or sets the URL parser</summary>
|
||||
public Crestron.SimplSharp.Net.Http.UrlParserResult Url { get; set; } = new Crestron.SimplSharp.Net.Http.UrlParserResult();
|
||||
|
||||
/// <summary>Gets or sets the HTTP method</summary>
|
||||
public RequestType RequestType { get; set; } = RequestType.Get;
|
||||
|
||||
/// <summary>Gets or sets the content data</summary>
|
||||
public string ContentString { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Gets the headers collection</summary>
|
||||
public HttpHeaderCollection Header { get; } = new HttpHeaderCollection();
|
||||
}
|
||||
|
||||
/// <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 HttpHeaderCollection Header { get; } = new HttpHeaderCollection();
|
||||
}
|
||||
|
||||
/// <summary>Mock HTTP header collection</summary>
|
||||
public class HttpHeaderCollection
|
||||
{
|
||||
private readonly Dictionary<string, string> _headers = new Dictionary<string, string>();
|
||||
|
||||
/// <summary>Gets or sets the content type</summary>
|
||||
public string ContentType
|
||||
{
|
||||
get => _headers.TryGetValue("Content-Type", out var value) ? value : string.Empty;
|
||||
set => _headers["Content-Type"] = value;
|
||||
}
|
||||
|
||||
/// <summary>Sets a header value</summary>
|
||||
/// <param name="name">Header name</param>
|
||||
/// <param name="value">Header value</param>
|
||||
public void SetHeaderValue(string name, string value)
|
||||
{
|
||||
_headers[name] = value;
|
||||
}
|
||||
|
||||
/// <summary>Gets a header value</summary>
|
||||
/// <param name="name">Header name</param>
|
||||
/// <returns>Header value or empty string if not found</returns>
|
||||
public string GetHeaderValue(string name)
|
||||
{
|
||||
return _headers.TryGetValue(name, out var value) ? value : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
namespace Crestron.SimplSharp.Net.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 class HttpsClient
|
||||
{
|
||||
/// <summary>Gets or sets the keep-alive setting</summary>
|
||||
public bool KeepAlive { get; set; } = false;
|
||||
|
||||
/// <summary>Gets or sets the host verification setting</summary>
|
||||
public bool HostVerification { get; set; } = false;
|
||||
|
||||
/// <summary>Gets or sets the peer verification setting</summary>
|
||||
public bool PeerVerification { get; set; } = false;
|
||||
|
||||
/// <summary>Dispatch HTTPS request</summary>
|
||||
/// <param name="request">HTTPS request</param>
|
||||
/// <param name="callback">Callback for response</param>
|
||||
public void Dispatch(HttpsClientRequest request, Action<HttpsClientResponse> callback)
|
||||
{
|
||||
// Mock implementation - invoke callback with empty response
|
||||
var response = new HttpsClientResponse();
|
||||
callback?.Invoke(response);
|
||||
}
|
||||
|
||||
/// <summary>Dispatches HTTPS request synchronously</summary>
|
||||
/// <param name="request">HTTPS request</param>
|
||||
/// <returns>HTTPS response</returns>
|
||||
public HttpsClientResponse Dispatch(HttpsClientRequest request)
|
||||
{
|
||||
// Mock implementation - return empty response
|
||||
return new HttpsClientResponse();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Mock HTTPS client request</summary>
|
||||
public class HttpsClientRequest
|
||||
{
|
||||
/// <summary>Gets or sets the URL parser</summary>
|
||||
public Crestron.SimplSharp.Net.Https.UrlParserResult Url { get; set; } = new Crestron.SimplSharp.Net.Https.UrlParserResult();
|
||||
|
||||
/// <summary>Gets or sets the HTTP method</summary>
|
||||
public RequestType RequestType { get; set; } = RequestType.Get;
|
||||
|
||||
/// <summary>Gets or sets the content data</summary>
|
||||
public string ContentString { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Gets the headers collection</summary>
|
||||
public HttpsHeaderCollection Header { get; } = new HttpsHeaderCollection();
|
||||
}
|
||||
|
||||
/// <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 HttpsHeaderCollection Header { get; } = new HttpsHeaderCollection();
|
||||
}
|
||||
|
||||
/// <summary>Mock HTTPS header collection</summary>
|
||||
public class HttpsHeaderCollection
|
||||
{
|
||||
private readonly Dictionary<string, string> _headers = new Dictionary<string, string>();
|
||||
|
||||
/// <summary>Gets or sets the content type</summary>
|
||||
public string ContentType
|
||||
{
|
||||
get => _headers.TryGetValue("Content-Type", out var value) ? value : string.Empty;
|
||||
set => _headers["Content-Type"] = value;
|
||||
}
|
||||
|
||||
/// <summary>Sets a header value</summary>
|
||||
/// <param name="name">Header name</param>
|
||||
/// <param name="value">Header value</param>
|
||||
public void SetHeaderValue(string name, string value)
|
||||
{
|
||||
_headers[name] = value;
|
||||
}
|
||||
|
||||
/// <summary>Adds a header</summary>
|
||||
/// <param name="header">Header to add</param>
|
||||
public void AddHeader(HttpsHeader header)
|
||||
{
|
||||
_headers[header.Name] = header.Value;
|
||||
}
|
||||
|
||||
/// <summary>Gets a header value</summary>
|
||||
/// <param name="name">Header name</param>
|
||||
/// <returns>Header value or empty string if not found</returns>
|
||||
public string GetHeaderValue(string name)
|
||||
{
|
||||
return _headers.TryGetValue(name, out var value) ? value : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Mock HTTPS header</summary>
|
||||
public class HttpsHeader
|
||||
{
|
||||
/// <summary>Gets the header name</summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary>Gets the header value</summary>
|
||||
public string Value { get; private set; }
|
||||
|
||||
/// <summary>Initializes a new instance of HttpsHeader</summary>
|
||||
/// <param name="name">Header name</param>
|
||||
/// <param name="value">Header value</param>
|
||||
public HttpsHeader(string name, string value)
|
||||
{
|
||||
Name = name ?? string.Empty;
|
||||
Value = value ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Mock HTTP exception</summary>
|
||||
public class HttpException : Exception
|
||||
{
|
||||
/// <summary>Gets the HTTP response</summary>
|
||||
public HttpsClientResponse Response { get; }
|
||||
|
||||
/// <summary>Initializes a new instance of HttpException</summary>
|
||||
public HttpException() : base()
|
||||
{
|
||||
Response = new HttpsClientResponse();
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of HttpException</summary>
|
||||
/// <param name="message">Exception message</param>
|
||||
public HttpException(string message) : base(message)
|
||||
{
|
||||
Response = new HttpsClientResponse();
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of HttpException</summary>
|
||||
/// <param name="message">Exception message</param>
|
||||
/// <param name="innerException">Inner exception</param>
|
||||
public HttpException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
Response = new HttpsClientResponse();
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of HttpException</summary>
|
||||
/// <param name="message">Exception message</param>
|
||||
/// <param name="response">HTTP response</param>
|
||||
public HttpException(string message, HttpsClientResponse response) : base(message)
|
||||
{
|
||||
Response = response ?? new HttpsClientResponse();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>3.0.0-local</Version>
|
||||
<Version>2.19.4-local</Version>
|
||||
<InformationalVersion>$(Version)</InformationalVersion>
|
||||
<Authors>PepperDash Technology</Authors>
|
||||
<Company>PepperDash Technology</Company>
|
||||
|
||||
@@ -23,23 +23,32 @@
|
||||
<FileName>$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz</FileName>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="DeleteCLZ" BeforeTargets="PreBuildEvent" Condition="$(ProjectType) == 'Library' And $(TargetDir) != '' And Exists($(FileName))">
|
||||
<Delete Files="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).clz">
|
||||
<Target Name="DeleteCLZ" BeforeTargets="CoreBuild" Condition="$(ProjectType) == 'Library' And $(TargetDir) != ''">
|
||||
<ItemGroup>
|
||||
<OldCLZFiles Include="$(TargetDir)$(TargetName).*.$(TargetFramework).clz" />
|
||||
</ItemGroup>
|
||||
<Delete Files="@(OldCLZFiles)" Condition="@(OldCLZFiles) != ''">
|
||||
<Output TaskParameter="DeletedFiles" ItemName="DeletedList"/>
|
||||
</Delete>
|
||||
<Message Text="Deleted files: '@(DeletedList)'" />
|
||||
<Message Text="Deleted old CLZ files: '@(DeletedList)'" Condition="@(DeletedList) != ''" />
|
||||
</Target>
|
||||
<Target Name="DeleteCPZ" BeforeTargets="PreBuildEvent" Condition="$(ProjectType) == 'Program' And $(TargetDir) != '' And Exists($(FileName))">
|
||||
<Delete Files="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz">
|
||||
<Target Name="DeleteCPZ" BeforeTargets="CoreBuild" Condition="$(ProjectType) == 'Program' And $(TargetDir) != ''">
|
||||
<ItemGroup>
|
||||
<OldCPZFiles Include="$(TargetDir)$(TargetName).*.$(TargetFramework).cpz" />
|
||||
</ItemGroup>
|
||||
<Delete Files="@(OldCPZFiles)" Condition="@(OldCPZFiles) != ''">
|
||||
<Output TaskParameter="DeletedFiles" ItemName="DeletedList"/>
|
||||
</Delete>
|
||||
<Message Text="Deleted files: '@(DeletedList)'" />
|
||||
<Message Text="Deleted old CPZ files: '@(DeletedList)'" Condition="@(DeletedList) != ''" />
|
||||
</Target>
|
||||
<Target Name="DeleteCPLZ" BeforeTargets="PreBuildEvent" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != '' And Exists($(FileName))">
|
||||
<Delete Files="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cplz">
|
||||
<Target Name="DeleteCPLZ" BeforeTargets="CoreBuild" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''">
|
||||
<ItemGroup>
|
||||
<OldCPLZFiles Include="$(TargetDir)$(TargetName).*.$(TargetFramework).cplz" />
|
||||
</ItemGroup>
|
||||
<Delete Files="@(OldCPLZFiles)" Condition="@(OldCPLZFiles) != ''">
|
||||
<Output TaskParameter="DeletedFiles" ItemName="DeletedList"/>
|
||||
</Delete>
|
||||
<Message Text="Deleted files: '@(DeletedList)'" />
|
||||
<Message Text="Deleted old CPLZ files: '@(DeletedList)'" Condition="@(DeletedList) != ''" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CreateCPLZ" AfterTargets="Build" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''" DependsOnTargets="DeleteCPLZ">
|
||||
|
||||
43
src/PepperDash.Core/ComTextHelper.cs
Normal file
43
src/PepperDash.Core/ComTextHelper.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class for formatting communication text and byte data for debugging purposes.
|
||||
/// </summary>
|
||||
public class ComTextHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets escaped text for a byte array
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <returns>string with all bytes escaped</returns>
|
||||
public static string GetEscapedText(byte[] bytes)
|
||||
{
|
||||
return string.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets escaped text for a string
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns>string with all bytes escaped</returns>
|
||||
public static string GetEscapedText(string text)
|
||||
{
|
||||
var bytes = Encoding.GetEncoding(28591).GetBytes(text);
|
||||
return string.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets debug text for a string
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns>string with all non-printable characters escaped</returns>
|
||||
public static string GetDebugText(string text)
|
||||
{
|
||||
return Regex.Replace(text, @"[^\u0020-\u007E]", a => GetEscapedText(a.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ using Crestron.SimplSharp;
|
||||
using PepperDash.Core;
|
||||
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the string event handler for line events on the gather
|
||||
/// </summary>
|
||||
@@ -85,8 +85,7 @@ namespace PepperDash.Core;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnects this gather from the Port's TextReceived event. This will not fire LineReceived
|
||||
/// after the this call.
|
||||
/// Stop method
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
@@ -176,3 +175,4 @@ namespace PepperDash.Core;
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using PepperDash.Core;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Controls the ability to disable/enable debugging of TX/RX data sent to/from a device with a built in timer to disable
|
||||
/// </summary>
|
||||
public class CommunicationStreamDebugging
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Controls the ability to disable/enable debugging of TX/RX data sent to/from a device with a built in timer to disable
|
||||
/// </summary>
|
||||
public class CommunicationStreamDebugging
|
||||
{
|
||||
/// <summary>
|
||||
/// Device Key that this instance configures
|
||||
/// </summary>
|
||||
@@ -23,7 +22,7 @@ public class CommunicationStreamDebugging
|
||||
private CTimer DebugExpiryPeriod;
|
||||
|
||||
/// <summary>
|
||||
/// The current debug setting
|
||||
/// Gets or sets the DebugSetting
|
||||
/// </summary>
|
||||
public eStreamDebuggingSetting DebugSetting { get; private set; }
|
||||
|
||||
@@ -37,14 +36,14 @@ public class CommunicationStreamDebugging
|
||||
{
|
||||
get
|
||||
{
|
||||
return _DebugTimeoutInMs/60000;
|
||||
return _DebugTimeoutInMs / 60000;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that receive stream debugging is enabled
|
||||
/// Gets or sets the RxStreamDebuggingIsEnabled
|
||||
/// </summary>
|
||||
public bool RxStreamDebuggingIsEnabled{ get; private set; }
|
||||
public bool RxStreamDebuggingIsEnabled { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that transmit stream debugging is enabled
|
||||
@@ -65,6 +64,9 @@ public class CommunicationStreamDebugging
|
||||
/// Sets the debugging setting and if not setting to off, assumes the default of 30 mintues
|
||||
/// </summary>
|
||||
/// <param name="setting"></param>
|
||||
/// <summary>
|
||||
/// SetDebuggingWithDefaultTimeout method
|
||||
/// </summary>
|
||||
public void SetDebuggingWithDefaultTimeout(eStreamDebuggingSetting setting)
|
||||
{
|
||||
if (setting == eStreamDebuggingSetting.Off)
|
||||
@@ -81,6 +83,9 @@ public class CommunicationStreamDebugging
|
||||
/// </summary>
|
||||
/// <param name="setting"></param>
|
||||
/// <param name="minutes"></param>
|
||||
/// <summary>
|
||||
/// SetDebuggingWithSpecificTimeout method
|
||||
/// </summary>
|
||||
public void SetDebuggingWithSpecificTimeout(eStreamDebuggingSetting setting, uint minutes)
|
||||
{
|
||||
if (setting == eStreamDebuggingSetting.Off)
|
||||
@@ -129,48 +134,5 @@ public class CommunicationStreamDebugging
|
||||
DebugExpiryPeriod.Dispose();
|
||||
DebugExpiryPeriod = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The available settings for stream debugging
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum eStreamDebuggingSetting
|
||||
{
|
||||
/// <summary>
|
||||
/// Debug off
|
||||
/// </summary>
|
||||
Off = 0,
|
||||
/// <summary>
|
||||
/// Debug received data
|
||||
/// </summary>
|
||||
Rx = 1,
|
||||
/// <summary>
|
||||
/// Debug transmitted data
|
||||
/// </summary>
|
||||
Tx = 2,
|
||||
/// <summary>
|
||||
/// Debug both received and transmitted data
|
||||
/// </summary>
|
||||
Both = Rx | Tx
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The available settings for stream debugging response types
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum eStreamDebuggingDataTypeSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Debug data in byte format
|
||||
/// </summary>
|
||||
Bytes = 0,
|
||||
/// <summary>
|
||||
/// Debug data in text format
|
||||
/// </summary>
|
||||
Text = 1,
|
||||
/// <summary>
|
||||
/// Debug data in both byte and text formats
|
||||
/// </summary>
|
||||
Both = Bytes | Text,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ using Crestron.SimplSharp;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Config properties that indicate how to communicate with a device for control
|
||||
/// </summary>
|
||||
public class ControlPropertiesConfig
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a ControlPropertiesConfig
|
||||
/// </summary>
|
||||
public class ControlPropertiesConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// The method of control
|
||||
/// </summary>
|
||||
@@ -89,4 +89,5 @@ public class ControlPropertiesConfig
|
||||
public ControlPropertiesConfig()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,21 +16,21 @@ using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
|
||||
|
||||
namespace PepperDash.Core;
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate for notifying of socket status changes
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
public delegate void GenericSocketStatusChangeEventDelegate(ISocketStatus client);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for notifying of socket status changes
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
public delegate void GenericSocketStatusChangeEventDelegate(ISocketStatus client);
|
||||
|
||||
/// <summary>
|
||||
/// EventArgs class for socket status changes
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// EventArgs class for socket status changes
|
||||
/// </summary>
|
||||
public class GenericSocketStatusChageEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the Client
|
||||
/// </summary>
|
||||
public ISocketStatus Client { get; private set; }
|
||||
|
||||
@@ -46,21 +46,21 @@ public delegate void GenericSocketStatusChangeEventDelegate(ISocketStatus client
|
||||
/// S+ Constructor
|
||||
/// </summary>
|
||||
public GenericSocketStatusChageEventArgs() { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for notifying of TCP Server state changes
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
public delegate void GenericTcpServerStateChangedEventDelegate(ServerState state);
|
||||
|
||||
/// <summary>
|
||||
/// EventArgs class for TCP Server state changes
|
||||
/// </summary>
|
||||
public class GenericTcpServerStateChangedEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// Delegate for notifying of TCP Server state changes
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
public delegate void GenericTcpServerStateChangedEventDelegate(ServerState state);
|
||||
|
||||
/// <summary>
|
||||
/// EventArgs class for TCP Server state changes
|
||||
/// </summary>
|
||||
public class GenericTcpServerStateChangedEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the State
|
||||
/// </summary>
|
||||
public ServerState State { get; private set; }
|
||||
|
||||
@@ -76,20 +76,20 @@ public class GenericTcpServerStateChangedEventArgs : EventArgs
|
||||
/// S+ Constructor
|
||||
/// </summary>
|
||||
public GenericTcpServerStateChangedEventArgs() { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for TCP Server socket status changes
|
||||
/// </summary>
|
||||
/// <param name="socket"></param>
|
||||
/// <param name="clientIndex"></param>
|
||||
/// <param name="clientStatus"></param>
|
||||
public delegate void GenericTcpServerSocketStatusChangeEventDelegate(object socket, uint clientIndex, SocketStatus clientStatus);
|
||||
/// <summary>
|
||||
/// EventArgs for TCP server socket status changes
|
||||
/// </summary>
|
||||
public class GenericTcpServerSocketStatusChangeEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate for TCP Server socket status changes
|
||||
/// </summary>
|
||||
/// <param name="socket"></param>
|
||||
/// <param name="clientIndex"></param>
|
||||
/// <param name="clientStatus"></param>
|
||||
public delegate void GenericTcpServerSocketStatusChangeEventDelegate(object socket, uint clientIndex, SocketStatus clientStatus);
|
||||
/// <summary>
|
||||
/// EventArgs for TCP server socket status changes
|
||||
/// </summary>
|
||||
public class GenericTcpServerSocketStatusChangeEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@@ -130,13 +130,13 @@ public class GenericTcpServerSocketStatusChangeEventArgs : EventArgs
|
||||
/// S+ Constructor
|
||||
/// </summary>
|
||||
public GenericTcpServerSocketStatusChangeEventArgs() { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EventArgs for TCP server com method receive text
|
||||
/// </summary>
|
||||
public class GenericTcpServerCommMethodReceiveTextArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// EventArgs for TCP server com method receive text
|
||||
/// </summary>
|
||||
public class GenericTcpServerCommMethodReceiveTextArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@@ -154,7 +154,7 @@ public class GenericTcpServerCommMethodReceiveTextArgs : EventArgs
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the Text
|
||||
/// </summary>
|
||||
public string Text { get; private set; }
|
||||
|
||||
@@ -181,13 +181,13 @@ public class GenericTcpServerCommMethodReceiveTextArgs : EventArgs
|
||||
/// S+ Constructor
|
||||
/// </summary>
|
||||
public GenericTcpServerCommMethodReceiveTextArgs() { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EventArgs for TCP server client ready for communication
|
||||
/// </summary>
|
||||
public class GenericTcpServerClientReadyForcommunicationsEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// EventArgs for TCP server client ready for communication
|
||||
/// </summary>
|
||||
public class GenericTcpServerClientReadyForcommunicationsEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@@ -205,13 +205,13 @@ public class GenericTcpServerClientReadyForcommunicationsEventArgs : EventArgs
|
||||
/// S+ Constructor
|
||||
/// </summary>
|
||||
public GenericTcpServerClientReadyForcommunicationsEventArgs() { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EventArgs for UDP connected
|
||||
/// </summary>
|
||||
public class GenericUdpConnectedEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// EventArgs for UDP connected
|
||||
/// </summary>
|
||||
public class GenericUdpConnectedEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@@ -244,5 +244,8 @@ public class GenericUdpConnectedEventArgs : EventArgs
|
||||
Connected = connected;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -7,13 +7,13 @@ using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using PepperDash.Core.Logging;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// A class to handle secure TCP/IP communications with a server
|
||||
/// </summary>
|
||||
public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A class to handle secure TCP/IP communications with a server
|
||||
/// </summary>
|
||||
public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
|
||||
{
|
||||
private const string SplusKey = "Uninitialized Secure Tcp _client";
|
||||
/// <summary>
|
||||
/// Stream debugging
|
||||
@@ -79,7 +79,7 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Port on server
|
||||
/// Gets or sets the Port
|
||||
/// </summary>
|
||||
public int Port { get; set; }
|
||||
|
||||
@@ -149,7 +149,7 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
||||
public string ConnectionFailure { get { return ClientStatus.ToString(); } }
|
||||
|
||||
/// <summary>
|
||||
/// bool to track if auto reconnect should be set on the socket
|
||||
/// Gets or sets the AutoReconnect
|
||||
/// </summary>
|
||||
public bool AutoReconnect { get; set; }
|
||||
|
||||
@@ -188,7 +188,7 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
||||
#region GenericSecureTcpIpClient properties
|
||||
|
||||
/// <summary>
|
||||
/// Bool to show whether the server requires a preshared key. This is used in the DynamicTCPServer class
|
||||
/// Gets or sets the SharedKeyRequired
|
||||
/// </summary>
|
||||
public bool SharedKeyRequired { get; set; }
|
||||
|
||||
@@ -207,7 +207,7 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SharedKey is sent for varification to the server. Shared key can be any text (255 char limit in SIMPL+ Module), but must match the Shared Key on the Server module
|
||||
/// Gets or sets the SharedKey
|
||||
/// </summary>
|
||||
public string SharedKey { get; set; }
|
||||
|
||||
@@ -222,7 +222,7 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
||||
bool IsTryingToConnect;
|
||||
|
||||
/// <summary>
|
||||
/// Bool showing if socket is ready for communication after shared key exchange
|
||||
/// Gets or sets the IsReadyForCommunication
|
||||
/// </summary>
|
||||
public bool IsReadyForCommunication { get; set; }
|
||||
|
||||
@@ -342,7 +342,7 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Just to help S+ set the key
|
||||
/// Initialize method
|
||||
/// </summary>
|
||||
public void Initialize(string key)
|
||||
{
|
||||
@@ -421,6 +421,9 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
||||
/// Deactivate the client
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <summary>
|
||||
/// Deactivate method
|
||||
/// </summary>
|
||||
public override bool Deactivate()
|
||||
{
|
||||
if (_client != null)
|
||||
@@ -432,7 +435,7 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect Method. Will return if already connected. Will write errors if missing address, port, or unique key/name.
|
||||
/// Connect method
|
||||
/// </summary>
|
||||
public void Connect()
|
||||
{
|
||||
@@ -563,7 +566,7 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Disconnect method
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
@@ -586,7 +589,7 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the actual disconnect business
|
||||
/// DisconnectClient method
|
||||
/// </summary>
|
||||
public void DisconnectClient()
|
||||
{
|
||||
@@ -846,7 +849,7 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// General send method
|
||||
/// SendText method
|
||||
/// </summary>
|
||||
public void SendText(string text)
|
||||
{
|
||||
@@ -875,7 +878,7 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// SendBytes method
|
||||
/// </summary>
|
||||
public void SendBytes(byte[] bytes)
|
||||
{
|
||||
@@ -898,7 +901,7 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="clientSocketStatus"></param>
|
||||
void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)
|
||||
void Client_SocketStatusChange(SecureTCPClient client, SocketStatus clientSocketStatus)
|
||||
{
|
||||
if (ProgramIsStopping)
|
||||
{
|
||||
@@ -950,4 +953,6 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
||||
handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,13 +19,13 @@ using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using PepperDash.Core.Logging;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Generic secure TCP/IP client for server
|
||||
/// </summary>
|
||||
public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic secure TCP/IP client for server
|
||||
/// </summary>
|
||||
public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
{
|
||||
/// <summary>
|
||||
/// Band aid delegate for choked server
|
||||
/// </summary>
|
||||
@@ -80,7 +80,7 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
public string Hostname { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Port on server
|
||||
/// Gets or sets the Port
|
||||
/// </summary>
|
||||
public int Port { get; set; }
|
||||
|
||||
@@ -113,7 +113,7 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SharedKey is sent for varification to the server. Shared key can be any text (255 char limit in SIMPL+ Module), but must match the Shared Key on the Server module
|
||||
/// Gets or sets the SharedKey
|
||||
/// </summary>
|
||||
public string SharedKey { get; set; }
|
||||
|
||||
@@ -123,7 +123,7 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
private bool WaitingForSharedKeyResponse { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defaults to 2000
|
||||
/// Gets or sets the BufferSize
|
||||
/// </summary>
|
||||
public int BufferSize { get; set; }
|
||||
|
||||
@@ -336,7 +336,7 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Just to help S+ set the key
|
||||
/// Initialize method
|
||||
/// </summary>
|
||||
public void Initialize(string key)
|
||||
{
|
||||
@@ -395,7 +395,7 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect Method. Will return if already connected. Will write errors if missing address, port, or unique key/name.
|
||||
/// Connect method
|
||||
/// </summary>
|
||||
public void Connect()
|
||||
{
|
||||
@@ -526,7 +526,7 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Disconnect method
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
@@ -804,7 +804,7 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// General send method
|
||||
/// SendText method
|
||||
/// </summary>
|
||||
public void SendText(string text)
|
||||
{
|
||||
@@ -833,7 +833,7 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// SendBytes method
|
||||
/// </summary>
|
||||
public void SendBytes(byte[] bytes)
|
||||
{
|
||||
@@ -904,4 +904,6 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,13 +17,13 @@ using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using PepperDash.Core.Logging;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Generic secure TCP/IP server
|
||||
/// </summary>
|
||||
public class GenericSecureTcpIpServer : Device
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic secure TCP/IP server
|
||||
/// </summary>
|
||||
public class GenericSecureTcpIpServer : Device
|
||||
{
|
||||
#region Events
|
||||
/// <summary>
|
||||
/// Event for Receiving text
|
||||
@@ -58,7 +58,7 @@ public class GenericSecureTcpIpServer : Device
|
||||
public ServerHasChokedCallbackDelegate ServerHasChoked { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Delegate for ServerHasChokedCallbackDelegate
|
||||
/// </summary>
|
||||
public delegate void ServerHasChokedCallbackDelegate();
|
||||
|
||||
@@ -104,7 +104,7 @@ public class GenericSecureTcpIpServer : Device
|
||||
int MonitorClientFailureCount;
|
||||
|
||||
/// <summary>
|
||||
/// 3 by default
|
||||
/// Gets or sets the MonitorClientMaxFailureCount
|
||||
/// </summary>
|
||||
public int MonitorClientMaxFailureCount { get; set; }
|
||||
|
||||
@@ -190,7 +190,7 @@ public class GenericSecureTcpIpServer : Device
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Port Server should listen on
|
||||
/// Gets or sets the Port
|
||||
/// </summary>
|
||||
public int Port { get; set; }
|
||||
|
||||
@@ -223,8 +223,7 @@ public class GenericSecureTcpIpServer : Device
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SharedKey is sent for varification to the server. Shared key can be any text (255 char limit in SIMPL+ Module), but must match the Shared Key on the Server module.
|
||||
/// If SharedKey changes while server is listening or clients are connected, disconnect and stop listening will be called
|
||||
/// Gets or sets the SharedKey
|
||||
/// </summary>
|
||||
public string SharedKey { get; set; }
|
||||
|
||||
@@ -248,7 +247,7 @@ public class GenericSecureTcpIpServer : Device
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Milliseconds before server expects another heartbeat. Set by property HeartbeatRequiredIntervalInSeconds which is driven from S+
|
||||
/// Gets or sets the HeartbeatRequiredIntervalMs
|
||||
/// </summary>
|
||||
public int HeartbeatRequiredIntervalMs { get; set; }
|
||||
|
||||
@@ -258,7 +257,7 @@ public class GenericSecureTcpIpServer : Device
|
||||
public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatRequiredIntervalMs = (value * 1000); } }
|
||||
|
||||
/// <summary>
|
||||
/// String to Match for heartbeat. If null or empty any string will reset heartbeat timer
|
||||
/// Gets or sets the HeartbeatStringToMatch
|
||||
/// </summary>
|
||||
public string HeartbeatStringToMatch { get; set; }
|
||||
|
||||
@@ -276,7 +275,7 @@ public class GenericSecureTcpIpServer : Device
|
||||
public List<uint> ConnectedClientsIndexes = new List<uint>();
|
||||
|
||||
/// <summary>
|
||||
/// Defaults to 2000
|
||||
/// Gets or sets the BufferSize
|
||||
/// </summary>
|
||||
public int BufferSize { get; set; }
|
||||
|
||||
@@ -339,7 +338,7 @@ public class GenericSecureTcpIpServer : Device
|
||||
|
||||
#region Methods - Server Actions
|
||||
/// <summary>
|
||||
/// Disconnects all clients and stops the server
|
||||
/// KillServer method
|
||||
/// </summary>
|
||||
public void KillServer()
|
||||
{
|
||||
@@ -356,6 +355,9 @@ public class GenericSecureTcpIpServer : Device
|
||||
/// Initialize Key for device using client name from SIMPL+. Called on Listen from SIMPL+
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <summary>
|
||||
/// Initialize method
|
||||
/// </summary>
|
||||
public void Initialize(string key)
|
||||
{
|
||||
Key = key;
|
||||
@@ -395,7 +397,7 @@ public class GenericSecureTcpIpServer : Device
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start listening on the specified port
|
||||
/// Listen method
|
||||
/// </summary>
|
||||
public void Listen()
|
||||
{
|
||||
@@ -431,7 +433,7 @@ public class GenericSecureTcpIpServer : Device
|
||||
ServerStopped = false;
|
||||
|
||||
// Start the listner
|
||||
SocketErrorCodes status = SecureServer.WaitForConnectionAsync("0.0.0.0", SecureConnectCallback);
|
||||
SocketErrorCodes status = SecureServer.WaitForConnectionAsync(IPAddress.Any, SecureConnectCallback);
|
||||
if (status != SocketErrorCodes.SOCKET_OPERATION_PENDING)
|
||||
{
|
||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error starting WaitForConnectionAsync {0}", status);
|
||||
@@ -453,7 +455,7 @@ public class GenericSecureTcpIpServer : Device
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop Listeneing
|
||||
/// StopListening method
|
||||
/// </summary>
|
||||
public void StopListening()
|
||||
{
|
||||
@@ -478,6 +480,9 @@ public class GenericSecureTcpIpServer : Device
|
||||
/// Disconnects Client
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <summary>
|
||||
/// DisconnectClient method
|
||||
/// </summary>
|
||||
public void DisconnectClient(uint client)
|
||||
{
|
||||
try
|
||||
@@ -491,7 +496,7 @@ public class GenericSecureTcpIpServer : Device
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Disconnect All Clients
|
||||
/// DisconnectAllClientsForShutdown method
|
||||
/// </summary>
|
||||
public void DisconnectAllClientsForShutdown()
|
||||
{
|
||||
@@ -533,6 +538,9 @@ public class GenericSecureTcpIpServer : Device
|
||||
/// Broadcast text from server to all connected clients
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <summary>
|
||||
/// BroadcastText method
|
||||
/// </summary>
|
||||
public void BroadcastText(string text)
|
||||
{
|
||||
CCriticalSection CCBroadcast = new CCriticalSection();
|
||||
@@ -566,6 +574,9 @@ public class GenericSecureTcpIpServer : Device
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="clientIndex"></param>
|
||||
/// <summary>
|
||||
/// SendTextToClient method
|
||||
/// </summary>
|
||||
public void SendTextToClient(string text, uint clientIndex)
|
||||
{
|
||||
try
|
||||
@@ -634,6 +645,9 @@ public class GenericSecureTcpIpServer : Device
|
||||
/// </summary>
|
||||
/// <param name="clientIndex"></param>
|
||||
/// <returns></returns>
|
||||
/// <summary>
|
||||
/// GetClientIPAddress method
|
||||
/// </summary>
|
||||
public string GetClientIPAddress(uint clientIndex)
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress Index: {0}", clientIndex);
|
||||
@@ -692,20 +706,37 @@ public class GenericSecureTcpIpServer : Device
|
||||
/// Secure Server Socket Status Changed Callback
|
||||
/// </summary>
|
||||
/// <param name="server"></param>
|
||||
/// <param name="args">Event arguments</param>
|
||||
void SecureServer_SocketStatusChange(SecureTCPServer server, TCPServerWaitingForConnectionsEventArgs args)
|
||||
/// <param name="clientIndex"></param>
|
||||
/// <param name="serverSocketStatus"></param>
|
||||
void SecureServer_SocketStatusChange(SecureTCPServer server, uint clientIndex, SocketStatus serverSocketStatus)
|
||||
{
|
||||
try
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SecureServerSocketStatusChange ConnectedClients: {0} ServerState: {1} Port: {2}",
|
||||
SecureServer.NumberOfClientsConnected, SecureServer.State, SecureServer.PortNumber);
|
||||
|
||||
// Handle connection limit and listening state
|
||||
|
||||
// Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SecureServerSocketStatusChange Index:{0} status:{1} Port:{2} IP:{3}", clientIndex, serverSocketStatus, this.SecureServer.GetPortNumberServerAcceptedConnectionFromForSpecificClient(clientIndex), this.SecureServer.GetLocalAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
|
||||
if (serverSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SecureServerSocketStatusChange ConnectedCLients: {0} ServerState: {1} Port: {2}", SecureServer.NumberOfClientsConnected, SecureServer.State, SecureServer.PortNumber);
|
||||
|
||||
if (ConnectedClientsIndexes.Contains(clientIndex))
|
||||
ConnectedClientsIndexes.Remove(clientIndex);
|
||||
if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))
|
||||
{
|
||||
HeartbeatTimerDictionary[clientIndex].Stop();
|
||||
HeartbeatTimerDictionary[clientIndex].Dispose();
|
||||
HeartbeatTimerDictionary.Remove(clientIndex);
|
||||
}
|
||||
if (ClientReadyAfterKeyExchange.Contains(clientIndex))
|
||||
ClientReadyAfterKeyExchange.Remove(clientIndex);
|
||||
if (WaitingForSharedKey.Contains(clientIndex))
|
||||
WaitingForSharedKey.Remove(clientIndex);
|
||||
if (SecureServer.MaxNumberOfClientSupported > SecureServer.NumberOfClientsConnected)
|
||||
{
|
||||
Listen();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Change Callback. Error: {0}", ex);
|
||||
@@ -776,7 +807,7 @@ public class GenericSecureTcpIpServer : Device
|
||||
}
|
||||
|
||||
// Rearm the listner
|
||||
SocketErrorCodes status = server.WaitForConnectionAsync("0.0.0.0", SecureConnectCallback);
|
||||
SocketErrorCodes status = server.WaitForConnectionAsync(IPAddress.Any, SecureConnectCallback);
|
||||
if (status != SocketErrorCodes.SOCKET_OPERATION_PENDING)
|
||||
{
|
||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Socket status connect callback status {0}", status);
|
||||
@@ -1063,4 +1094,5 @@ public class GenericSecureTcpIpServer : Device
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Crestron.SimplSharp;
|
||||
@@ -8,14 +9,15 @@ using PepperDash.Core.Logging;
|
||||
using Renci.SshNet;
|
||||
using Renci.SshNet.Common;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// SSH Client
|
||||
/// </summary>
|
||||
public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
|
||||
{
|
||||
private const string SPlusKey = "Uninitialized SshClient";
|
||||
|
||||
/// <summary>
|
||||
/// Object to enable stream debugging
|
||||
/// </summary>
|
||||
@@ -36,13 +38,8 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
/// </summary>
|
||||
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
|
||||
|
||||
///// <summary>
|
||||
/////
|
||||
///// </summary>
|
||||
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
|
||||
|
||||
/// <summary>
|
||||
/// Address of server
|
||||
/// Gets or sets the Hostname
|
||||
/// </summary>
|
||||
public string Hostname { get; set; }
|
||||
|
||||
@@ -52,12 +49,12 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
public int Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Username for server
|
||||
/// Gets or sets the Username
|
||||
/// </summary>
|
||||
public string Username { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// And... Password for server. That was worth documenting!
|
||||
/// Gets or sets the Password
|
||||
/// </summary>
|
||||
public string Password { get; set; }
|
||||
|
||||
@@ -67,7 +64,7 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
public bool IsConnected
|
||||
{
|
||||
// returns false if no client or not connected
|
||||
get { return Client != null && ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
|
||||
get { return client != null && ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -79,20 +76,30 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Socket status change event
|
||||
/// </summary>
|
||||
public SocketStatus ClientStatus
|
||||
{
|
||||
get { return _ClientStatus; }
|
||||
get { lock (_stateLock) { return _ClientStatus; } }
|
||||
private set
|
||||
{
|
||||
if (_ClientStatus == value)
|
||||
return;
|
||||
bool shouldFireEvent = false;
|
||||
lock (_stateLock)
|
||||
{
|
||||
if (_ClientStatus != value)
|
||||
{
|
||||
_ClientStatus = value;
|
||||
shouldFireEvent = true;
|
||||
}
|
||||
}
|
||||
// Fire event outside lock to avoid deadlock
|
||||
if (shouldFireEvent)
|
||||
OnConnectionChange();
|
||||
}
|
||||
}
|
||||
SocketStatus _ClientStatus;
|
||||
|
||||
private SocketStatus _ClientStatus;
|
||||
private bool _ConnectEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
|
||||
@@ -100,7 +107,7 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
/// </summary>
|
||||
public ushort UStatus
|
||||
{
|
||||
get { return (ushort)_ClientStatus; }
|
||||
get { lock (_stateLock) { return (ushort)_ClientStatus; } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -111,7 +118,11 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
/// <summary>
|
||||
/// Will be set and unset by connect and disconnect only
|
||||
/// </summary>
|
||||
public bool ConnectEnabled { get; private set; }
|
||||
public bool ConnectEnabled
|
||||
{
|
||||
get { lock (_stateLock) { return _ConnectEnabled; } }
|
||||
private set { lock (_stateLock) { _ConnectEnabled = value; } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// S+ helper for AutoReconnect
|
||||
@@ -123,22 +134,29 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Millisecond value, determines the timeout period in between reconnect attempts.
|
||||
/// Set to 5000 by default
|
||||
/// Gets or sets the AutoReconnectIntervalMs
|
||||
/// </summary>
|
||||
public int AutoReconnectIntervalMs { get; set; }
|
||||
|
||||
SshClient Client;
|
||||
private SshClient client;
|
||||
|
||||
ShellStream TheStream;
|
||||
private ShellStream shellStream;
|
||||
|
||||
CTimer ReconnectTimer;
|
||||
private readonly Timer reconnectTimer;
|
||||
|
||||
//Lock object to prevent simulatneous connect/disconnect operations
|
||||
//private CCriticalSection connectLock = new CCriticalSection();
|
||||
private SemaphoreSlim connectLock = new SemaphoreSlim(1);
|
||||
private readonly SemaphoreSlim connectLock = new SemaphoreSlim(1);
|
||||
|
||||
private bool DisconnectLogged = false;
|
||||
// Thread-safety lock for state changes
|
||||
private readonly object _stateLock = new object();
|
||||
|
||||
private bool disconnectLogged = false;
|
||||
|
||||
/// <summary>
|
||||
/// When true, turns off echo for the SSH session
|
||||
/// </summary>
|
||||
public bool DisableEcho { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Typical constructor.
|
||||
@@ -155,13 +173,13 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
Password = password;
|
||||
AutoReconnectIntervalMs = 5000;
|
||||
|
||||
ReconnectTimer = new CTimer(o =>
|
||||
reconnectTimer = new Timer(o =>
|
||||
{
|
||||
if (ConnectEnabled)
|
||||
if (ConnectEnabled) // Now thread-safe property access
|
||||
{
|
||||
Connect();
|
||||
}
|
||||
}, System.Threading.Timeout.Infinite);
|
||||
}, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -173,23 +191,23 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
||||
AutoReconnectIntervalMs = 5000;
|
||||
|
||||
ReconnectTimer = new CTimer(o =>
|
||||
reconnectTimer = new Timer(o =>
|
||||
{
|
||||
if (ConnectEnabled)
|
||||
if (ConnectEnabled) // Now thread-safe property access
|
||||
{
|
||||
Connect();
|
||||
}
|
||||
}, System.Threading.Timeout.Infinite);
|
||||
}, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles closing this up when the program shuts down
|
||||
/// </summary>
|
||||
void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
|
||||
private void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
|
||||
{
|
||||
if (programEventType == eProgramStatusEventType.Stopping)
|
||||
{
|
||||
if (Client != null)
|
||||
if (client != null)
|
||||
{
|
||||
this.LogDebug("Program stopping. Closing connection");
|
||||
Disconnect();
|
||||
@@ -198,7 +216,7 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect to the server, using the provided properties.
|
||||
/// Connect method
|
||||
/// </summary>
|
||||
public void Connect()
|
||||
{
|
||||
@@ -224,13 +242,10 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
this.LogDebug("Attempting connect");
|
||||
|
||||
// Cancel reconnect if running.
|
||||
if (ReconnectTimer != null)
|
||||
{
|
||||
ReconnectTimer.Stop();
|
||||
}
|
||||
StopReconnectTimer();
|
||||
|
||||
// Cleanup the old client if it already exists
|
||||
if (Client != null)
|
||||
if (client != null)
|
||||
{
|
||||
this.LogDebug("Cleaning up disconnected client");
|
||||
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
|
||||
@@ -243,77 +258,90 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
|
||||
this.LogDebug("Creating new SshClient");
|
||||
ConnectionInfo connectionInfo = new ConnectionInfo(Hostname, Port, Username, pauth, kauth);
|
||||
Client = new SshClient(connectionInfo);
|
||||
Client.ErrorOccurred += Client_ErrorOccurred;
|
||||
client = new SshClient(connectionInfo);
|
||||
client.ErrorOccurred += Client_ErrorOccurred;
|
||||
|
||||
//Attempt to connect
|
||||
ClientStatus = SocketStatus.SOCKET_STATUS_WAITING;
|
||||
try
|
||||
{
|
||||
Client.Connect();
|
||||
TheStream = Client.CreateShellStream("PDTShell", 0, 0, 0, 0, 65534);
|
||||
if (TheStream.DataAvailable)
|
||||
client.Connect();
|
||||
|
||||
var modes = new Dictionary<TerminalModes, uint>();
|
||||
|
||||
if (DisableEcho)
|
||||
{
|
||||
modes.Add(TerminalModes.ECHO, 0);
|
||||
}
|
||||
|
||||
shellStream = client.CreateShellStream("PDTShell", 0, 0, 0, 0, 65534, modes);
|
||||
if (shellStream.DataAvailable)
|
||||
{
|
||||
// empty the buffer if there is data
|
||||
string str = TheStream.Read();
|
||||
shellStream.Read();
|
||||
}
|
||||
TheStream.DataReceived += Stream_DataReceived;
|
||||
shellStream.DataReceived += Stream_DataReceived;
|
||||
this.LogInformation("Connected");
|
||||
ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED;
|
||||
DisconnectLogged = false;
|
||||
disconnectLogged = false;
|
||||
}
|
||||
catch (SshConnectionException e)
|
||||
{
|
||||
var ie = e.InnerException; // The details are inside!!
|
||||
var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
|
||||
|
||||
if (ie is SocketException)
|
||||
{
|
||||
this.LogException(ie, "CONNECTION failure: Cannot reach host");
|
||||
this.LogError("CONNECTION failure: Cannot reach host");
|
||||
this.LogVerbose(ie, "Exception details: ");
|
||||
}
|
||||
|
||||
if (ie is System.Net.Sockets.SocketException socketException)
|
||||
{
|
||||
this.LogException(ie, "Connection failure: Cannot reach {host} on {port}",
|
||||
this.LogError("Connection failure: Cannot reach {host} on {port}",
|
||||
Hostname, Port);
|
||||
this.LogVerbose(socketException, "SocketException details: ");
|
||||
}
|
||||
if (ie is SshAuthenticationException)
|
||||
{
|
||||
this.LogException(ie, "Authentication failure for username {userName}", Username);
|
||||
this.LogError("Authentication failure for username {userName}", Username);
|
||||
this.LogVerbose(ie, "AuthenticationException details: ");
|
||||
}
|
||||
else
|
||||
this.LogException(ie, "Error on connect");
|
||||
{
|
||||
this.LogError("Error on connect: {error}", ie.Message);
|
||||
this.LogVerbose(ie, "Exception details: ");
|
||||
}
|
||||
|
||||
DisconnectLogged = true;
|
||||
disconnectLogged = true;
|
||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||
if (AutoReconnect)
|
||||
{
|
||||
this.LogDebug("Checking autoreconnect: {autoReconnect}, {autoReconnectInterval}ms", AutoReconnect, AutoReconnectIntervalMs);
|
||||
ReconnectTimer.Reset(AutoReconnectIntervalMs);
|
||||
StartReconnectTimer();
|
||||
}
|
||||
}
|
||||
catch(SshOperationTimeoutException ex)
|
||||
catch (SshOperationTimeoutException ex)
|
||||
{
|
||||
this.LogWarning("Connection attempt timed out: {message}", ex.Message);
|
||||
|
||||
DisconnectLogged = true;
|
||||
disconnectLogged = true;
|
||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||
if (AutoReconnect)
|
||||
{
|
||||
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
|
||||
ReconnectTimer.Reset(AutoReconnectIntervalMs);
|
||||
StartReconnectTimer();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
|
||||
this.LogException(e, "Unhandled exception on connect");
|
||||
DisconnectLogged = true;
|
||||
this.LogError("Unhandled exception on connect: {error}", e.Message);
|
||||
this.LogVerbose(e, "Exception details: ");
|
||||
disconnectLogged = true;
|
||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||
if (AutoReconnect)
|
||||
{
|
||||
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
|
||||
ReconnectTimer.Reset(AutoReconnectIntervalMs);
|
||||
StartReconnectTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -325,17 +353,13 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect the clients and put away it's resources.
|
||||
/// Disconnect method
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
ConnectEnabled = false;
|
||||
// Stop trying reconnects, if we are
|
||||
if (ReconnectTimer != null)
|
||||
{
|
||||
ReconnectTimer.Stop();
|
||||
// ReconnectTimer = null;
|
||||
}
|
||||
StopReconnectTimer();
|
||||
|
||||
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
|
||||
}
|
||||
@@ -349,35 +373,35 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
|
||||
try
|
||||
{
|
||||
if (Client != null)
|
||||
if (client != null)
|
||||
{
|
||||
Client.ErrorOccurred -= Client_ErrorOccurred;
|
||||
Client.Disconnect();
|
||||
Client.Dispose();
|
||||
Client = null;
|
||||
client.ErrorOccurred -= Client_ErrorOccurred;
|
||||
client.Disconnect();
|
||||
client.Dispose();
|
||||
client = null;
|
||||
ClientStatus = status;
|
||||
this.LogDebug("Disconnected");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.LogException(ex,"Exception in Kill Client");
|
||||
this.LogException(ex, "Exception in Kill Client");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kills the stream
|
||||
/// </summary>
|
||||
void KillStream()
|
||||
private void KillStream()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (TheStream != null)
|
||||
if (shellStream != null)
|
||||
{
|
||||
TheStream.DataReceived -= Stream_DataReceived;
|
||||
TheStream.Close();
|
||||
TheStream.Dispose();
|
||||
TheStream = null;
|
||||
shellStream.DataReceived -= Stream_DataReceived;
|
||||
shellStream.Close();
|
||||
shellStream.Dispose();
|
||||
shellStream = null;
|
||||
this.LogDebug("Disconnected stream");
|
||||
}
|
||||
}
|
||||
@@ -390,7 +414,7 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
/// <summary>
|
||||
/// Handles the keyboard interactive authentication, should it be required.
|
||||
/// </summary>
|
||||
void kauth_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)
|
||||
private void kauth_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)
|
||||
{
|
||||
foreach (AuthenticationPrompt prompt in e.Prompts)
|
||||
if (prompt.Request.IndexOf("Password:", StringComparison.InvariantCultureIgnoreCase) != -1)
|
||||
@@ -400,7 +424,7 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
/// <summary>
|
||||
/// Handler for data receive on ShellStream. Passes data across to queue for line parsing.
|
||||
/// </summary>
|
||||
void Stream_DataReceived(object sender, ShellDataEventArgs e)
|
||||
private void Stream_DataReceived(object sender, ShellDataEventArgs e)
|
||||
{
|
||||
if (((ShellStream)sender).Length <= 0L)
|
||||
{
|
||||
@@ -413,18 +437,14 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
if (bytesHandler != null)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(response);
|
||||
if (StreamDebugging.RxStreamDebuggingIsEnabled)
|
||||
{
|
||||
this.LogInformation("Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
|
||||
}
|
||||
this.PrintReceivedBytes(bytes);
|
||||
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
|
||||
}
|
||||
|
||||
var textHandler = TextReceived;
|
||||
if (textHandler != null)
|
||||
{
|
||||
if (StreamDebugging.RxStreamDebuggingIsEnabled)
|
||||
this.LogInformation("Received: '{0}'", ComTextHelper.GetDebugText(response));
|
||||
this.PrintReceivedText(response);
|
||||
|
||||
textHandler(this, new GenericCommMethodReceiveTextArgs(response));
|
||||
}
|
||||
@@ -436,7 +456,7 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
/// Error event handler for client events - disconnect, etc. Will forward those events via ConnectionChange
|
||||
/// event
|
||||
/// </summary>
|
||||
void Client_ErrorOccurred(object sender, ExceptionEventArgs e)
|
||||
private void Client_ErrorOccurred(object sender, ExceptionEventArgs e)
|
||||
{
|
||||
CrestronInvoke.BeginInvoke(o =>
|
||||
{
|
||||
@@ -456,7 +476,7 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
if (AutoReconnect && ConnectEnabled)
|
||||
{
|
||||
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
|
||||
ReconnectTimer.Reset(AutoReconnectIntervalMs);
|
||||
StartReconnectTimer();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -464,10 +484,9 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
/// <summary>
|
||||
/// Helper for ConnectionChange event
|
||||
/// </summary>
|
||||
void OnConnectionChange()
|
||||
private void OnConnectionChange()
|
||||
{
|
||||
if (ConnectionChange != null)
|
||||
ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
|
||||
ConnectionChange?.Invoke(this, new GenericSocketStatusChageEventArgs(this));
|
||||
}
|
||||
|
||||
#region IBasicCommunication Members
|
||||
@@ -475,21 +494,17 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
/// <summary>
|
||||
/// Sends text to the server
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="text">The text to send</param>
|
||||
public void SendText(string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Client != null && TheStream != null && IsConnected)
|
||||
if (client != null && shellStream != null && IsConnected)
|
||||
{
|
||||
if (StreamDebugging.TxStreamDebuggingIsEnabled)
|
||||
this.LogInformation(
|
||||
"Sending {length} characters of text: '{text}'",
|
||||
text.Length,
|
||||
ComTextHelper.GetDebugText(text));
|
||||
this.PrintSentText(text);
|
||||
|
||||
TheStream.Write(text);
|
||||
TheStream.Flush();
|
||||
shellStream.Write(text);
|
||||
shellStream.Flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -501,7 +516,7 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
this.LogError("ObjectDisposedException sending '{message}'. Restarting connection...", text.Trim());
|
||||
|
||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||
ReconnectTimer.Reset();
|
||||
StartReconnectTimer();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -512,18 +527,17 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
/// <summary>
|
||||
/// Sends Bytes to the server
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <param name="bytes">The bytes to send</param>
|
||||
public void SendBytes(byte[] bytes)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Client != null && TheStream != null && IsConnected)
|
||||
if (client != null && shellStream != null && IsConnected)
|
||||
{
|
||||
if (StreamDebugging.TxStreamDebuggingIsEnabled)
|
||||
this.LogInformation("Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
|
||||
this.PrintSentBytes(bytes);
|
||||
|
||||
TheStream.Write(bytes, 0, bytes.Length);
|
||||
TheStream.Flush();
|
||||
shellStream.Write(bytes, 0, bytes.Length);
|
||||
shellStream.Flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -535,23 +549,100 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
||||
this.LogException(ex, "ObjectDisposedException sending {message}", ComTextHelper.GetEscapedText(bytes));
|
||||
|
||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||
ReconnectTimer.Reset();
|
||||
StartReconnectTimer();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.LogException(ex, "Exception sending {message}", ComTextHelper.GetEscapedText(bytes));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// Safely starts the reconnect timer with exception handling
|
||||
/// </summary>
|
||||
private void StartReconnectTimer()
|
||||
{
|
||||
try
|
||||
{
|
||||
reconnectTimer?.Change(AutoReconnectIntervalMs, System.Threading.Timeout.Infinite);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Timer was disposed, ignore
|
||||
this.LogDebug("Attempted to start timer but it was already disposed");
|
||||
}
|
||||
}
|
||||
|
||||
//*****************************************************************************************************
|
||||
//*****************************************************************************************************
|
||||
/// <summary>
|
||||
/// Fired when connection changes
|
||||
/// </summary>
|
||||
public class SshConnectionChangeEventArgs : EventArgs
|
||||
/// <summary>
|
||||
/// Safely stops the reconnect timer with exception handling
|
||||
/// </summary>
|
||||
private void StopReconnectTimer()
|
||||
{
|
||||
try
|
||||
{
|
||||
reconnectTimer?.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Timer was disposed, ignore
|
||||
this.LogDebug("Attempted to stop timer but it was already disposed");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deactivate method - properly dispose of resources
|
||||
/// </summary>
|
||||
public override bool Deactivate()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.LogDebug("Deactivating SSH client - disposing resources");
|
||||
|
||||
// Stop trying reconnects
|
||||
ConnectEnabled = false;
|
||||
StopReconnectTimer();
|
||||
|
||||
// Disconnect and cleanup client
|
||||
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
|
||||
|
||||
// Dispose timer
|
||||
try
|
||||
{
|
||||
reconnectTimer?.Dispose();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Already disposed, ignore
|
||||
}
|
||||
|
||||
// Dispose semaphore
|
||||
try
|
||||
{
|
||||
connectLock?.Dispose();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Already disposed, ignore
|
||||
}
|
||||
|
||||
return base.Deactivate();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.LogException(ex, "Error during SSH client deactivation");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//*****************************************************************************************************
|
||||
//*****************************************************************************************************
|
||||
/// <summary>
|
||||
/// Represents a SshConnectionChangeEventArgs
|
||||
/// </summary>
|
||||
public class SshConnectionChangeEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Connection State
|
||||
@@ -559,17 +650,17 @@ public class SshConnectionChangeEventArgs : EventArgs
|
||||
public bool IsConnected { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Connection Status represented as a ushort
|
||||
/// Gets or sets the UIsConnected
|
||||
/// </summary>
|
||||
public ushort UIsConnected { get { return (ushort)(Client.IsConnected ? 1 : 0); } }
|
||||
|
||||
/// <summary>
|
||||
/// The client
|
||||
/// Gets or sets the Client
|
||||
/// </summary>
|
||||
public GenericSshClient Client { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Socket Status as represented by
|
||||
/// Gets or sets the Status
|
||||
/// </summary>
|
||||
public ushort Status { get { return Client.UStatus; } }
|
||||
|
||||
@@ -588,4 +679,5 @@ public class SshConnectionChangeEventArgs : EventArgs
|
||||
IsConnected = isConnected;
|
||||
Client = client;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,13 @@ using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// A class to handle basic TCP/IP communications with a server
|
||||
/// </summary>
|
||||
public class GenericTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A class to handle basic TCP/IP communications with a server
|
||||
/// </summary>
|
||||
public class GenericTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
|
||||
{
|
||||
private const string SplusKey = "Uninitialized TcpIpClient";
|
||||
/// <summary>
|
||||
/// Object to enable stream debugging
|
||||
@@ -59,7 +59,7 @@ namespace PepperDash.Core;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Port on server
|
||||
/// Gets or sets the Port
|
||||
/// </summary>
|
||||
public int Port { get; set; }
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace PepperDash.Core;
|
||||
public string ConnectionFailure { get { return ClientStatus.ToString(); } }
|
||||
|
||||
/// <summary>
|
||||
/// bool to track if auto reconnect should be set on the socket
|
||||
/// Gets or sets the AutoReconnect
|
||||
/// </summary>
|
||||
public bool AutoReconnect { get; set; }
|
||||
|
||||
@@ -232,7 +232,7 @@ namespace PepperDash.Core;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Just to help S+ set the key
|
||||
/// Initialize method
|
||||
/// </summary>
|
||||
public void Initialize(string key)
|
||||
{
|
||||
@@ -255,6 +255,9 @@ namespace PepperDash.Core;
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <summary>
|
||||
/// Deactivate method
|
||||
/// </summary>
|
||||
public override bool Deactivate()
|
||||
{
|
||||
RetryTimer.Stop();
|
||||
@@ -268,7 +271,7 @@ namespace PepperDash.Core;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to connect to the server
|
||||
/// Connect method
|
||||
/// </summary>
|
||||
public void Connect()
|
||||
{
|
||||
@@ -335,7 +338,7 @@ namespace PepperDash.Core;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to disconnect the client
|
||||
/// Disconnect method
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
@@ -355,7 +358,7 @@ namespace PepperDash.Core;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the actual disconnect business
|
||||
/// DisconnectClient method
|
||||
/// </summary>
|
||||
public void DisconnectClient()
|
||||
{
|
||||
@@ -423,10 +426,7 @@ namespace PepperDash.Core;
|
||||
var bytesHandler = BytesReceived;
|
||||
if (bytesHandler != null)
|
||||
{
|
||||
if (StreamDebugging.RxStreamDebuggingIsEnabled)
|
||||
{
|
||||
Debug.Console(0, this, "Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
|
||||
}
|
||||
this.PrintReceivedBytes(bytes);
|
||||
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
|
||||
}
|
||||
var textHandler = TextReceived;
|
||||
@@ -434,10 +434,7 @@ namespace PepperDash.Core;
|
||||
{
|
||||
var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
|
||||
|
||||
if (StreamDebugging.RxStreamDebuggingIsEnabled)
|
||||
{
|
||||
Debug.Console(0, this, "Received {1} characters of text: '{0}'", ComTextHelper.GetDebugText(str), str.Length);
|
||||
}
|
||||
this.PrintReceivedText(str);
|
||||
|
||||
textHandler(this, new GenericCommMethodReceiveTextArgs(str));
|
||||
}
|
||||
@@ -447,20 +444,19 @@ namespace PepperDash.Core;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// General send method
|
||||
/// SendText method
|
||||
/// </summary>
|
||||
public void SendText(string text)
|
||||
{
|
||||
var bytes = Encoding.GetEncoding(28591).GetBytes(text);
|
||||
// Check debug level before processing byte array
|
||||
if (StreamDebugging.TxStreamDebuggingIsEnabled)
|
||||
Debug.Console(0, this, "Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
|
||||
this.PrintSentText(text);
|
||||
if (_client != null)
|
||||
_client.SendData(bytes, bytes.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is useful from console and...?
|
||||
/// SendEscapedText method
|
||||
/// </summary>
|
||||
public void SendEscapedText(string text)
|
||||
{
|
||||
@@ -476,10 +472,12 @@ namespace PepperDash.Core;
|
||||
/// Sends Bytes to the server
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <summary>
|
||||
/// SendBytes method
|
||||
/// </summary>
|
||||
public void SendBytes(byte[] bytes)
|
||||
{
|
||||
if (StreamDebugging.TxStreamDebuggingIsEnabled)
|
||||
Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
|
||||
this.PrintSentBytes(bytes);
|
||||
if (_client != null)
|
||||
_client.SendData(bytes, bytes.Length);
|
||||
}
|
||||
@@ -508,9 +506,9 @@ namespace PepperDash.Core;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configuration properties for TCP/SSH Connections
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// Represents a TcpSshPropertiesConfig
|
||||
/// </summary>
|
||||
public class TcpSshPropertiesConfig
|
||||
{
|
||||
/// <summary>
|
||||
@@ -530,7 +528,7 @@ namespace PepperDash.Core;
|
||||
/// </summary>
|
||||
public string Username { get; set; }
|
||||
/// <summary>
|
||||
/// Passord credential
|
||||
/// Gets or sets the Password
|
||||
/// </summary>
|
||||
public string Password { get; set; }
|
||||
|
||||
@@ -540,15 +538,21 @@ namespace PepperDash.Core;
|
||||
public int BufferSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defaults to true
|
||||
/// Gets or sets the AutoReconnect
|
||||
/// </summary>
|
||||
public bool AutoReconnect { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defaults to 5000ms
|
||||
/// Gets or sets the AutoReconnectIntervalMs
|
||||
/// </summary>
|
||||
public int AutoReconnectIntervalMs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When true, turns off echo for the SSH session
|
||||
/// </summary>
|
||||
[JsonProperty("disableSshEcho")]
|
||||
public bool DisableSshEcho { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
@@ -559,6 +563,7 @@ namespace PepperDash.Core;
|
||||
AutoReconnectIntervalMs = 5000;
|
||||
Username = "";
|
||||
Password = "";
|
||||
DisableSshEcho = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,13 @@ using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using PepperDash.Core.Logging;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Generic TCP/IP client for server
|
||||
/// </summary>
|
||||
public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic TCP/IP client for server
|
||||
/// </summary>
|
||||
public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
{
|
||||
/// <summary>
|
||||
/// Band aid delegate for choked server
|
||||
/// </summary>
|
||||
@@ -69,7 +69,7 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
public string Hostname { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Port on server
|
||||
/// Gets or sets the Port
|
||||
/// </summary>
|
||||
public int Port { get; set; }
|
||||
|
||||
@@ -102,7 +102,7 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SharedKey is sent for varification to the server. Shared key can be any text (255 char limit in SIMPL+ Module), but must match the Shared Key on the Server module
|
||||
/// Gets or sets the SharedKey
|
||||
/// </summary>
|
||||
public string SharedKey { get; set; }
|
||||
|
||||
@@ -112,7 +112,7 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
private bool WaitingForSharedKeyResponse { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defaults to 2000
|
||||
/// Gets or sets the BufferSize
|
||||
/// </summary>
|
||||
public int BufferSize { get; set; }
|
||||
|
||||
@@ -289,7 +289,7 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Just to help S+ set the key
|
||||
/// Initialize method
|
||||
/// </summary>
|
||||
public void Initialize(string key)
|
||||
{
|
||||
@@ -311,7 +311,7 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect Method. Will return if already connected. Will write errors if missing address, port, or unique key/name.
|
||||
/// Connect method
|
||||
/// </summary>
|
||||
public void Connect()
|
||||
{
|
||||
@@ -442,7 +442,7 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Disconnect method
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
@@ -669,7 +669,7 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// General send method
|
||||
/// SendText method
|
||||
/// </summary>
|
||||
public void SendText(string text)
|
||||
{
|
||||
@@ -698,7 +698,7 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// SendBytes method
|
||||
/// </summary>
|
||||
public void SendBytes(byte[] bytes)
|
||||
{
|
||||
@@ -770,4 +770,6 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
||||
handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,13 +17,13 @@ using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using PepperDash.Core.Logging;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Generic TCP/IP server device
|
||||
/// </summary>
|
||||
public class GenericTcpIpServer : Device
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic TCP/IP server device
|
||||
/// </summary>
|
||||
public class GenericTcpIpServer : Device
|
||||
{
|
||||
#region Events
|
||||
/// <summary>
|
||||
/// Event for Receiving text
|
||||
@@ -52,7 +52,7 @@ public class GenericTcpIpServer : Device
|
||||
public ServerHasChokedCallbackDelegate ServerHasChoked { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Delegate for ServerHasChokedCallbackDelegate
|
||||
/// </summary>
|
||||
public delegate void ServerHasChokedCallbackDelegate();
|
||||
|
||||
@@ -82,7 +82,7 @@ public class GenericTcpIpServer : Device
|
||||
int MonitorClientFailureCount;
|
||||
|
||||
/// <summary>
|
||||
/// 3 by default
|
||||
/// Gets or sets the MonitorClientMaxFailureCount
|
||||
/// </summary>
|
||||
public int MonitorClientMaxFailureCount { get; set; }
|
||||
|
||||
@@ -171,7 +171,7 @@ public class GenericTcpIpServer : Device
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Port Server should listen on
|
||||
/// Gets or sets the Port
|
||||
/// </summary>
|
||||
public int Port { get; set; }
|
||||
|
||||
@@ -204,8 +204,7 @@ public class GenericTcpIpServer : Device
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SharedKey is sent for varification to the server. Shared key can be any text (255 char limit in SIMPL+ Module), but must match the Shared Key on the Server module.
|
||||
/// If SharedKey changes while server is listening or clients are connected, disconnect and stop listening will be called
|
||||
/// Gets or sets the SharedKey
|
||||
/// </summary>
|
||||
public string SharedKey { get; set; }
|
||||
|
||||
@@ -229,7 +228,7 @@ public class GenericTcpIpServer : Device
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Milliseconds before server expects another heartbeat. Set by property HeartbeatRequiredIntervalInSeconds which is driven from S+
|
||||
/// Gets or sets the HeartbeatRequiredIntervalMs
|
||||
/// </summary>
|
||||
public int HeartbeatRequiredIntervalMs { get; set; }
|
||||
|
||||
@@ -239,7 +238,7 @@ public class GenericTcpIpServer : Device
|
||||
public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatRequiredIntervalMs = (value * 1000); } }
|
||||
|
||||
/// <summary>
|
||||
/// String to Match for heartbeat. If null or empty any string will reset heartbeat timer
|
||||
/// Gets or sets the HeartbeatStringToMatch
|
||||
/// </summary>
|
||||
public string HeartbeatStringToMatch { get; set; }
|
||||
|
||||
@@ -257,7 +256,7 @@ public class GenericTcpIpServer : Device
|
||||
public List<uint> ConnectedClientsIndexes = new List<uint>();
|
||||
|
||||
/// <summary>
|
||||
/// Defaults to 2000
|
||||
/// Gets or sets the BufferSize
|
||||
/// </summary>
|
||||
public int BufferSize { get; set; }
|
||||
|
||||
@@ -320,7 +319,7 @@ public class GenericTcpIpServer : Device
|
||||
|
||||
#region Methods - Server Actions
|
||||
/// <summary>
|
||||
/// Disconnects all clients and stops the server
|
||||
/// KillServer method
|
||||
/// </summary>
|
||||
public void KillServer()
|
||||
{
|
||||
@@ -337,6 +336,9 @@ public class GenericTcpIpServer : Device
|
||||
/// Initialize Key for device using client name from SIMPL+. Called on Listen from SIMPL+
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <summary>
|
||||
/// Initialize method
|
||||
/// </summary>
|
||||
public void Initialize(string key)
|
||||
{
|
||||
Key = key;
|
||||
@@ -375,7 +377,7 @@ public class GenericTcpIpServer : Device
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start listening on the specified port
|
||||
/// Listen method
|
||||
/// </summary>
|
||||
public void Listen()
|
||||
{
|
||||
@@ -400,7 +402,7 @@ public class GenericTcpIpServer : Device
|
||||
if (myTcpServer == null)
|
||||
{
|
||||
myTcpServer = new TCPServer(Port, MaxClients);
|
||||
if (HeartbeatRequired)
|
||||
if(HeartbeatRequired)
|
||||
myTcpServer.SocketSendOrReceiveTimeOutInMs = (this.HeartbeatRequiredIntervalMs * 5);
|
||||
|
||||
// myTcpServer.HandshakeTimeout = 30;
|
||||
@@ -415,7 +417,7 @@ public class GenericTcpIpServer : Device
|
||||
myTcpServer.SocketStatusChange += TcpServer_SocketStatusChange;
|
||||
|
||||
ServerStopped = false;
|
||||
myTcpServer.WaitForConnectionAsync("0.0.0.0", TcpConnectCallback);
|
||||
myTcpServer.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
|
||||
OnServerStateChange(myTcpServer.State);
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "TCP Server Status: {0}, Socket Status: {1}", myTcpServer.State, myTcpServer.ServerSocketStatus);
|
||||
|
||||
@@ -432,7 +434,7 @@ public class GenericTcpIpServer : Device
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop Listening
|
||||
/// StopListening method
|
||||
/// </summary>
|
||||
public void StopListening()
|
||||
{
|
||||
@@ -457,6 +459,9 @@ public class GenericTcpIpServer : Device
|
||||
/// Disconnects Client
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <summary>
|
||||
/// DisconnectClient method
|
||||
/// </summary>
|
||||
public void DisconnectClient(uint client)
|
||||
{
|
||||
try
|
||||
@@ -470,7 +475,7 @@ public class GenericTcpIpServer : Device
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Disconnect All Clients
|
||||
/// DisconnectAllClientsForShutdown method
|
||||
/// </summary>
|
||||
public void DisconnectAllClientsForShutdown()
|
||||
{
|
||||
@@ -512,6 +517,9 @@ public class GenericTcpIpServer : Device
|
||||
/// Broadcast text from server to all connected clients
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <summary>
|
||||
/// BroadcastText method
|
||||
/// </summary>
|
||||
public void BroadcastText(string text)
|
||||
{
|
||||
CCriticalSection CCBroadcast = new CCriticalSection();
|
||||
@@ -527,7 +535,7 @@ public class GenericTcpIpServer : Device
|
||||
{
|
||||
SocketErrorCodes error = myTcpServer.SendDataAsync(i, b, b.Length, (x, y, z) => { });
|
||||
if (error != SocketErrorCodes.SOCKET_OK && error != SocketErrorCodes.SOCKET_OPERATION_PENDING)
|
||||
this.LogError("{error}", error.ToString());
|
||||
this.LogError("{error}",error.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -545,6 +553,9 @@ public class GenericTcpIpServer : Device
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="clientIndex"></param>
|
||||
/// <summary>
|
||||
/// SendTextToClient method
|
||||
/// </summary>
|
||||
public void SendTextToClient(string text, uint clientIndex)
|
||||
{
|
||||
try
|
||||
@@ -613,6 +624,9 @@ public class GenericTcpIpServer : Device
|
||||
/// </summary>
|
||||
/// <param name="clientIndex"></param>
|
||||
/// <returns>IP address of the client</returns>
|
||||
/// <summary>
|
||||
/// GetClientIPAddress method
|
||||
/// </summary>
|
||||
public string GetClientIPAddress(uint clientIndex)
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress Index: {0}", clientIndex);
|
||||
@@ -671,24 +685,35 @@ public class GenericTcpIpServer : Device
|
||||
/// Secure Server Socket Status Changed Callback
|
||||
/// </summary>
|
||||
/// <param name="server"></param>
|
||||
/// <param name="args">Event arguments</param>
|
||||
void TcpServer_SocketStatusChange(TCPServer server, TCPServerWaitingForConnectionsEventArgs args)
|
||||
/// <param name="clientIndex"></param>
|
||||
/// <param name="serverSocketStatus"></param>
|
||||
void TcpServer_SocketStatusChange(TCPServer server, uint clientIndex, SocketStatus serverSocketStatus)
|
||||
{
|
||||
try
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "TcpServerSocketStatusChange ConnectedClients: {0} ServerState: {1} Port: {2}",
|
||||
myTcpServer.NumberOfClientsConnected, myTcpServer.State, myTcpServer.PortNumber);
|
||||
|
||||
// Handle connection limit and listening state
|
||||
if (myTcpServer.MaxNumberOfClientSupported > myTcpServer.NumberOfClientsConnected)
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SecureServerSocketStatusChange Index:{0} status:{1} Port:{2} IP:{3}", clientIndex, serverSocketStatus, this.myTcpServer.GetPortNumberServerAcceptedConnectionFromForSpecificClient(clientIndex), this.myTcpServer.GetLocalAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
|
||||
if (serverSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
|
||||
{
|
||||
Listen();
|
||||
if (ConnectedClientsIndexes.Contains(clientIndex))
|
||||
ConnectedClientsIndexes.Remove(clientIndex);
|
||||
if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))
|
||||
{
|
||||
HeartbeatTimerDictionary[clientIndex].Stop();
|
||||
HeartbeatTimerDictionary[clientIndex].Dispose();
|
||||
HeartbeatTimerDictionary.Remove(clientIndex);
|
||||
}
|
||||
if (ClientReadyAfterKeyExchange.Contains(clientIndex))
|
||||
ClientReadyAfterKeyExchange.Remove(clientIndex);
|
||||
if (WaitingForSharedKey.Contains(clientIndex))
|
||||
WaitingForSharedKey.Remove(clientIndex);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Change Callback. Error: {0}", ex);
|
||||
}
|
||||
onConnectionChange(clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -745,7 +770,7 @@ public class GenericTcpIpServer : Device
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Client attempt faulty.");
|
||||
if (!ServerStopped)
|
||||
{
|
||||
server.WaitForConnectionAsync("0.0.0.0", TcpConnectCallback);
|
||||
server.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -761,7 +786,7 @@ public class GenericTcpIpServer : Device
|
||||
if ((server.State & ServerState.SERVER_LISTENING) != ServerState.SERVER_LISTENING && MaxClients > 1 && !ServerStopped)
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Waiting for next connection");
|
||||
server.WaitForConnectionAsync("0.0.0.0", TcpConnectCallback);
|
||||
server.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -996,4 +1021,5 @@ public class GenericTcpIpServer : Device
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,13 @@ using Crestron.SimplSharp.CrestronSockets;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core.Logging;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Generic UDP Server device
|
||||
/// </summary>
|
||||
public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic UDP Server device
|
||||
/// </summary>
|
||||
public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
|
||||
{
|
||||
private const string SplusKey = "Uninitialized Udp Server";
|
||||
/// <summary>
|
||||
/// Object to enable stream debugging
|
||||
@@ -52,7 +52,7 @@ public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
|
||||
{
|
||||
get
|
||||
{
|
||||
return Server.ClientStatus;
|
||||
return Server.ServerStatus;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,14 +131,14 @@ public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
|
||||
/// <param name="key"></param>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="buffefSize"></param>
|
||||
public GenericUdpServer(string key, string address, int port, int buffefSize)
|
||||
/// <param name="bufferSize"></param>
|
||||
public GenericUdpServer(string key, string address, int port, int bufferSize)
|
||||
: base(key)
|
||||
{
|
||||
StreamDebugging = new CommunicationStreamDebugging(key);
|
||||
Hostname = address;
|
||||
Port = port;
|
||||
BufferSize = buffefSize;
|
||||
BufferSize = bufferSize;
|
||||
|
||||
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
||||
CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
|
||||
@@ -150,6 +150,9 @@ public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
|
||||
/// <param name="key"></param>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <summary>
|
||||
/// Initialize method
|
||||
/// </summary>
|
||||
public void Initialize(string key, string address, ushort port)
|
||||
{
|
||||
Key = key;
|
||||
@@ -185,15 +188,29 @@ public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables the UDP Server
|
||||
/// Connect method
|
||||
/// </summary>
|
||||
public void Connect()
|
||||
{
|
||||
if (Server == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var address = IPAddress.Parse(Hostname);
|
||||
|
||||
Server = new UDPServer(address, Port, BufferSize);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.LogError("Error parsing IP Address '{ipAddress}': message: {message}", Hostname, ex.Message);
|
||||
this.LogInformation("Creating UDPServer with default buffersize");
|
||||
|
||||
Server = new UDPServer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(Hostname))
|
||||
{
|
||||
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': No address set", Key);
|
||||
@@ -222,7 +239,7 @@ public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disabled the UDP Server
|
||||
/// Disconnect method
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
@@ -264,17 +281,13 @@ public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
|
||||
var bytesHandler = BytesReceived;
|
||||
if (bytesHandler != null)
|
||||
{
|
||||
if (StreamDebugging.RxStreamDebuggingIsEnabled)
|
||||
{
|
||||
Debug.Console(0, this, "Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
|
||||
}
|
||||
this.PrintReceivedBytes(bytes);
|
||||
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
|
||||
}
|
||||
var textHandler = TextReceived;
|
||||
if (textHandler != null)
|
||||
{
|
||||
if (StreamDebugging.RxStreamDebuggingIsEnabled)
|
||||
Debug.Console(0, this, "Received {1} characters of text: '{0}'", ComTextHelper.GetDebugText(str), str.Length);
|
||||
this.PrintReceivedText(str);
|
||||
textHandler(this, new GenericCommMethodReceiveTextArgs(str));
|
||||
}
|
||||
}
|
||||
@@ -292,14 +305,16 @@ public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
|
||||
/// General send method
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <summary>
|
||||
/// SendText method
|
||||
/// </summary>
|
||||
public void SendText(string text)
|
||||
{
|
||||
var bytes = Encoding.GetEncoding(28591).GetBytes(text);
|
||||
|
||||
if (IsConnected && Server != null)
|
||||
{
|
||||
if (StreamDebugging.TxStreamDebuggingIsEnabled)
|
||||
Debug.Console(0, this, "Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
|
||||
this.PrintSentText(text);
|
||||
|
||||
Server.SendData(bytes, bytes.Length);
|
||||
}
|
||||
@@ -309,22 +324,24 @@ public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <summary>
|
||||
/// SendBytes method
|
||||
/// </summary>
|
||||
public void SendBytes(byte[] bytes)
|
||||
{
|
||||
if (StreamDebugging.TxStreamDebuggingIsEnabled)
|
||||
Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
|
||||
this.PrintSentBytes(bytes);
|
||||
|
||||
if (IsConnected && Server != null)
|
||||
Server.SendData(bytes, bytes.Length);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class GenericUdpReceiveTextExtraArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a GenericUdpReceiveTextExtraArgs
|
||||
/// </summary>
|
||||
public class GenericUdpReceiveTextExtraArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@@ -361,13 +378,13 @@ public class GenericUdpReceiveTextExtraArgs : EventArgs
|
||||
/// Stupid S+ Constructor
|
||||
/// </summary>
|
||||
public GenericUdpReceiveTextExtraArgs() { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class UdpServerPropertiesConfig
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class UdpServerPropertiesConfig
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@@ -392,4 +409,5 @@ public class UdpServerPropertiesConfig
|
||||
{
|
||||
BufferSize = 32768;
|
||||
}
|
||||
}
|
||||
}
|
||||
69
src/PepperDash.Core/Comm/StreamDebuggingExtensions.cs
Normal file
69
src/PepperDash.Core/Comm/StreamDebuggingExtensions.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for stream debugging
|
||||
/// </summary>
|
||||
public static class StreamDebuggingExtensions
|
||||
{
|
||||
private static readonly string app = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? $"App {InitialParametersClass.ApplicationNumber}" : $"{InitialParametersClass.RoomId}";
|
||||
|
||||
/// <summary>
|
||||
/// Print the sent bytes to the console
|
||||
/// </summary>
|
||||
/// <param name="comms">comms device</param>
|
||||
/// <param name="bytes">bytes to print</param>
|
||||
public static void PrintSentBytes(this IStreamDebugging comms, byte[] bytes)
|
||||
{
|
||||
if (!comms.StreamDebugging.TxStreamDebuggingIsEnabled) return;
|
||||
|
||||
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
|
||||
|
||||
CrestronConsole.PrintLine($"[{timestamp}][{app}][{comms.Key}] Sending {bytes.Length} bytes: '{ComTextHelper.GetEscapedText(bytes)}'");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Print the received bytes to the console
|
||||
/// </summary>
|
||||
/// <param name="comms">comms device</param>
|
||||
/// <param name="bytes">bytes to print</param>
|
||||
public static void PrintReceivedBytes(this IStreamDebugging comms, byte[] bytes)
|
||||
{
|
||||
if (!comms.StreamDebugging.RxStreamDebuggingIsEnabled) return;
|
||||
|
||||
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
|
||||
|
||||
CrestronConsole.PrintLine($"[{timestamp}][{app}][{comms.Key}] Received {bytes.Length} bytes: '{ComTextHelper.GetEscapedText(bytes)}'");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Print the sent text to the console
|
||||
/// </summary>
|
||||
/// <param name="comms">comms device</param>
|
||||
/// <param name="text">text to print</param>
|
||||
public static void PrintSentText(this IStreamDebugging comms, string text)
|
||||
{
|
||||
if (!comms.StreamDebugging.TxStreamDebuggingIsEnabled) return;
|
||||
|
||||
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
|
||||
|
||||
CrestronConsole.PrintLine($"[{timestamp}][{app}][{comms.Key}] Sending Text: '{ComTextHelper.GetDebugText(text)}'");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Print the received text to the console
|
||||
/// </summary>
|
||||
/// <param name="comms">comms device</param>
|
||||
/// <param name="text">text to print</param>
|
||||
public static void PrintReceivedText(this IStreamDebugging comms, string text)
|
||||
{
|
||||
if (!comms.StreamDebugging.RxStreamDebuggingIsEnabled) return;
|
||||
|
||||
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
|
||||
|
||||
CrestronConsole.PrintLine($"[{timestamp}][{app}][{comms.Key}] Received Text: '{ComTextHelper.GetDebugText(text)}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Client config object for TCP client with server that inherits from TcpSshPropertiesConfig and adds properties for shared key and heartbeat
|
||||
/// </summary>
|
||||
public class TcpClientConfigObject
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a TcpClientConfigObject
|
||||
/// </summary>
|
||||
public class TcpClientConfigObject
|
||||
{
|
||||
/// <summary>
|
||||
/// TcpSsh Properties
|
||||
/// </summary>
|
||||
@@ -55,4 +55,5 @@ public class TcpClientConfigObject
|
||||
/// </summary>
|
||||
[JsonProperty("receiveQueueSize")]
|
||||
public int ReceiveQueueSize { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,13 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Tcp Server Config object with properties for a tcp server with shared key and heartbeat capabilities
|
||||
/// </summary>
|
||||
public class TcpServerConfigObject
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Tcp Server Config object with properties for a tcp server with shared key and heartbeat capabilities
|
||||
/// </summary>
|
||||
public class TcpServerConfigObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Uique key
|
||||
/// </summary>
|
||||
@@ -56,4 +56,5 @@ public class TcpServerConfigObject
|
||||
/// Receive Queue size must be greater than 20 or defaults to 20
|
||||
/// </summary>
|
||||
public int ReceiveQueueSize { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,13 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Crestron Control Methods for a comm object
|
||||
/// </summary>
|
||||
public enum eControlMethod
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Crestron Control Methods for a comm object
|
||||
/// </summary>
|
||||
public enum eControlMethod
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@@ -74,5 +74,14 @@ public enum eControlMethod
|
||||
/// <summary>
|
||||
/// Secure TCP/IP
|
||||
/// </summary>
|
||||
SecureTcpIp
|
||||
SecureTcpIp,
|
||||
/// <summary>
|
||||
/// Used when comms needs to be handled in SIMPL and bridged opposite the normal direction
|
||||
/// </summary>
|
||||
ComBridge,
|
||||
/// <summary>
|
||||
/// InfinetEX control
|
||||
/// </summary>
|
||||
InfinetEx
|
||||
}
|
||||
}
|
||||
24
src/PepperDash.Core/Comm/eStreamDebuggingDataTypeSettings.cs
Normal file
24
src/PepperDash.Core/Comm/eStreamDebuggingDataTypeSettings.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// The available settings for stream debugging data format types
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum eStreamDebuggingDataTypeSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Debug data in byte format
|
||||
/// </summary>
|
||||
Bytes = 0,
|
||||
/// <summary>
|
||||
/// Debug data in text format
|
||||
/// </summary>
|
||||
Text = 1,
|
||||
/// <summary>
|
||||
/// Debug data in both byte and text formats
|
||||
/// </summary>
|
||||
Both = Bytes | Text
|
||||
}
|
||||
}
|
||||
28
src/PepperDash.Core/Comm/eStreamDebuggingSetting.cs
Normal file
28
src/PepperDash.Core/Comm/eStreamDebuggingSetting.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// The available settings for stream debugging
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum eStreamDebuggingSetting
|
||||
{
|
||||
/// <summary>
|
||||
/// Debug off
|
||||
/// </summary>
|
||||
Off = 0,
|
||||
/// <summary>
|
||||
/// Debug received data
|
||||
/// </summary>
|
||||
Rx = 1,
|
||||
/// <summary>
|
||||
/// Debug transmitted data
|
||||
/// </summary>
|
||||
Tx = 2,
|
||||
/// <summary>
|
||||
/// Debug both received and transmitted data
|
||||
/// </summary>
|
||||
Both = Rx | Tx
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using System.Text.RegularExpressions;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// An incoming communication stream
|
||||
/// </summary>
|
||||
public interface ICommunicationReceiver : IKeyed
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// An incoming communication stream
|
||||
/// </summary>
|
||||
public interface ICommunicationReceiver : IKeyed
|
||||
{
|
||||
/// <summary>
|
||||
/// Notifies of bytes received
|
||||
/// </summary>
|
||||
@@ -36,12 +33,12 @@ public interface ICommunicationReceiver : IKeyed
|
||||
/// Disconnect from the device
|
||||
/// </summary>
|
||||
void Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a device that uses basic connection
|
||||
/// Defines the contract for IBasicCommunication
|
||||
/// </summary>
|
||||
public interface IBasicCommunication : ICommunicationReceiver
|
||||
public interface IBasicCommunication : ICommunicationReceiver
|
||||
{
|
||||
/// <summary>
|
||||
/// Send text to the device
|
||||
@@ -56,25 +53,25 @@ public interface IBasicCommunication : ICommunicationReceiver
|
||||
void SendBytes(byte[] bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a device that implements IBasicCommunication and IStreamDebugging
|
||||
/// </summary>
|
||||
public interface IBasicCommunicationWithStreamDebugging : IBasicCommunication, IStreamDebugging
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a device that implements IBasicCommunication and IStreamDebugging
|
||||
/// </summary>
|
||||
public interface IBasicCommunicationWithStreamDebugging : IBasicCommunication, IStreamDebugging
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a device with stream debugging capablities
|
||||
/// </summary>
|
||||
public interface IStreamDebugging
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a device with stream debugging capablities
|
||||
/// </summary>
|
||||
public interface IStreamDebugging : IKeyed
|
||||
{
|
||||
/// <summary>
|
||||
/// Object to enable stream debugging
|
||||
/// </summary>
|
||||
[JsonProperty("streamDebugging")]
|
||||
CommunicationStreamDebugging StreamDebugging { get; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For IBasicCommunication classes that have SocketStatus. GenericSshClient,
|
||||
@@ -95,17 +92,17 @@ public interface IStreamDebugging
|
||||
SocketStatus ClientStatus { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes a device that implements ISocketStatus and IStreamDebugging
|
||||
/// </summary>
|
||||
public interface ISocketStatusWithStreamDebugging : ISocketStatus, IStreamDebugging
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a device that implements ISocketStatus and IStreamDebugging
|
||||
/// </summary>
|
||||
public interface ISocketStatusWithStreamDebugging : ISocketStatus, IStreamDebugging
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes a device that can automatically attempt to reconnect
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// Describes a device that can automatically attempt to reconnect
|
||||
/// </summary>
|
||||
public interface IAutoReconnect
|
||||
{
|
||||
/// <summary>
|
||||
@@ -148,7 +145,7 @@ public interface ISocketStatusWithStreamDebugging : ISocketStatus, IStreamDebugg
|
||||
public class GenericCommMethodReceiveBytesArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the Bytes
|
||||
/// </summary>
|
||||
public byte[] Bytes { get; private set; }
|
||||
|
||||
@@ -195,7 +192,7 @@ public interface ISocketStatusWithStreamDebugging : ISocketStatus, IStreamDebugg
|
||||
/// <param name="text"></param>
|
||||
/// <param name="delimiter"></param>
|
||||
public GenericCommMethodReceiveTextArgs(string text, string delimiter)
|
||||
:this(text)
|
||||
: this(text)
|
||||
{
|
||||
Delimiter = delimiter;
|
||||
}
|
||||
@@ -205,42 +202,4 @@ public interface ISocketStatusWithStreamDebugging : ISocketStatus, IStreamDebugg
|
||||
/// </summary>
|
||||
public GenericCommMethodReceiveTextArgs() { }
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class ComTextHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets escaped text for a byte array
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetEscapedText(byte[] bytes)
|
||||
{
|
||||
return String.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets escaped text for a string
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetEscapedText(string text)
|
||||
{
|
||||
var bytes = Encoding.GetEncoding(28591).GetBytes(text);
|
||||
return String.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets debug text for a string
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetDebugText(string text)
|
||||
{
|
||||
return Regex.Replace(text, @"[^\u0020-\u007E]", a => GetEscapedText(a.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,38 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Core.Config;
|
||||
|
||||
/// <summary>
|
||||
/// Reads a Portal formatted config file
|
||||
/// </summary>
|
||||
public class PortalConfigReader
|
||||
namespace PepperDash.Core.Config
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads a Portal formatted config file
|
||||
/// </summary>
|
||||
public class PortalConfigReader
|
||||
{
|
||||
const string template = "template";
|
||||
const string system = "system";
|
||||
const string systemUrl = "system_url";
|
||||
const string templateUrl = "template_url";
|
||||
const string info = "info";
|
||||
const string devices = "devices";
|
||||
const string rooms = "rooms";
|
||||
const string sourceLists = "sourceLists";
|
||||
const string destinationLists = "destinationLists";
|
||||
const string cameraLists = "cameraLists";
|
||||
const string audioControlPointLists = "audioControlPointLists";
|
||||
|
||||
const string tieLines = "tieLines";
|
||||
const string joinMaps = "joinMaps";
|
||||
const string global = "global";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads the config file, checks if it needs a merge, merges and saves, then returns the merged Object.
|
||||
/// </summary>
|
||||
@@ -24,25 +43,25 @@ public class PortalConfigReader
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
Debug.Console(1, Debug.ErrorLogLevel.Error,
|
||||
Debug.LogError(
|
||||
"ERROR: Configuration file not present. Please load file to {0} and reset program", filePath);
|
||||
}
|
||||
|
||||
using (StreamReader fs = new StreamReader(filePath))
|
||||
{
|
||||
var jsonObj = JObject.Parse(fs.ReadToEnd());
|
||||
if (jsonObj["template"] != null && jsonObj["system"] != null)
|
||||
if(jsonObj[template] != null && jsonObj[system] != null)
|
||||
{
|
||||
// it's a double-config, merge it.
|
||||
var merged = MergeConfigs(jsonObj);
|
||||
if (jsonObj["system_url"] != null)
|
||||
if (jsonObj[systemUrl] != null)
|
||||
{
|
||||
merged["systemUrl"] = jsonObj["system_url"].Value<string>();
|
||||
merged[systemUrl] = jsonObj[systemUrl].Value<string>();
|
||||
}
|
||||
|
||||
if (jsonObj["template_url"] != null)
|
||||
if (jsonObj[templateUrl] != null)
|
||||
{
|
||||
merged["templateUrl"] = jsonObj["template_url"].Value<string>();
|
||||
merged[templateUrl] = jsonObj[templateUrl].Value<string>();
|
||||
}
|
||||
|
||||
jsonObj = merged;
|
||||
@@ -67,6 +86,9 @@ public class PortalConfigReader
|
||||
/// </summary>
|
||||
/// <param name="doubleConfig"></param>
|
||||
/// <returns></returns>
|
||||
/// <summary>
|
||||
/// MergeConfigs method
|
||||
/// </summary>
|
||||
public static JObject MergeConfigs(JObject doubleConfig)
|
||||
{
|
||||
var system = JObject.FromObject(doubleConfig["system"]);
|
||||
@@ -74,62 +96,62 @@ public class PortalConfigReader
|
||||
var merged = new JObject();
|
||||
|
||||
// Put together top-level objects
|
||||
if (system["info"] != null)
|
||||
merged.Add("info", Merge(template["info"], system["info"], "infO"));
|
||||
if (system[info] != null)
|
||||
merged.Add(info, Merge(template[info], system[info], info));
|
||||
else
|
||||
merged.Add("info", template["info"]);
|
||||
merged.Add(info, template[info]);
|
||||
|
||||
merged.Add("devices", MergeArraysOnTopLevelProperty(template["devices"] as JArray,
|
||||
system["devices"] as JArray, "key", "devices"));
|
||||
merged.Add(devices, MergeArraysOnTopLevelProperty(template[devices] as JArray,
|
||||
system[devices] as JArray, "key", devices));
|
||||
|
||||
if (system["rooms"] == null)
|
||||
merged.Add("rooms", template["rooms"]);
|
||||
if (system[rooms] == null)
|
||||
merged.Add(rooms, template[rooms]);
|
||||
else
|
||||
merged.Add("rooms", MergeArraysOnTopLevelProperty(template["rooms"] as JArray,
|
||||
system["rooms"] as JArray, "key", "rooms"));
|
||||
merged.Add(rooms, MergeArraysOnTopLevelProperty(template[rooms] as JArray,
|
||||
system[rooms] as JArray, "key", rooms));
|
||||
|
||||
if (system["sourceLists"] == null)
|
||||
merged.Add("sourceLists", template["sourceLists"]);
|
||||
if (system[sourceLists] == null)
|
||||
merged.Add(sourceLists, template[sourceLists]);
|
||||
else
|
||||
merged.Add("sourceLists", Merge(template["sourceLists"], system["sourceLists"], "sourceLists"));
|
||||
merged.Add(sourceLists, Merge(template[sourceLists], system[sourceLists], sourceLists));
|
||||
|
||||
if (system["destinationLists"] == null)
|
||||
merged.Add("destinationLists", template["destinationLists"]);
|
||||
if (system[destinationLists] == null)
|
||||
merged.Add(destinationLists, template[destinationLists]);
|
||||
else
|
||||
merged.Add("destinationLists",
|
||||
Merge(template["destinationLists"], system["destinationLists"], "destinationLists"));
|
||||
merged.Add(destinationLists,
|
||||
Merge(template[destinationLists], system[destinationLists], destinationLists));
|
||||
|
||||
|
||||
if (system["cameraLists"] == null)
|
||||
merged.Add("cameraLists", template["cameraLists"]);
|
||||
if (system[cameraLists] == null)
|
||||
merged.Add(cameraLists, template[cameraLists]);
|
||||
else
|
||||
merged.Add("cameraLists", Merge(template["cameraLists"], system["cameraLists"], "cameraLists"));
|
||||
merged.Add(cameraLists, Merge(template[cameraLists], system[cameraLists], cameraLists));
|
||||
|
||||
if (system["audioControlPointLists"] == null)
|
||||
merged.Add("audioControlPointLists", template["audioControlPointLists"]);
|
||||
if (system[audioControlPointLists] == null)
|
||||
merged.Add(audioControlPointLists, template[audioControlPointLists]);
|
||||
else
|
||||
merged.Add("audioControlPointLists",
|
||||
Merge(template["audioControlPointLists"], system["audioControlPointLists"], "audioControlPointLists"));
|
||||
merged.Add(audioControlPointLists,
|
||||
Merge(template[audioControlPointLists], system[audioControlPointLists], audioControlPointLists));
|
||||
|
||||
|
||||
// Template tie lines take precedence. Config tool doesn't do them at system
|
||||
// level anyway...
|
||||
if (template["tieLines"] != null)
|
||||
merged.Add("tieLines", template["tieLines"]);
|
||||
else if (system["tieLines"] != null)
|
||||
merged.Add("tieLines", system["tieLines"]);
|
||||
if (template[tieLines] != null)
|
||||
merged.Add(tieLines, template[tieLines]);
|
||||
else if (system[tieLines] != null)
|
||||
merged.Add(tieLines, system[tieLines]);
|
||||
else
|
||||
merged.Add("tieLines", new JArray());
|
||||
merged.Add(tieLines, new JArray());
|
||||
|
||||
if (template["joinMaps"] != null)
|
||||
merged.Add("joinMaps", template["joinMaps"]);
|
||||
if (template[joinMaps] != null)
|
||||
merged.Add(joinMaps, template[joinMaps]);
|
||||
else
|
||||
merged.Add("joinMaps", new JObject());
|
||||
merged.Add(joinMaps, new JObject());
|
||||
|
||||
if (system["global"] != null)
|
||||
merged.Add("global", Merge(template["global"], system["global"], "global"));
|
||||
if (system[global] != null)
|
||||
merged.Add(global, Merge(template[global], system[global], global));
|
||||
else
|
||||
merged.Add("global", template["global"]);
|
||||
merged.Add(global, template[global]);
|
||||
|
||||
//Debug.Console(2, "MERGED CONFIG RESULT: \x0d\x0a{0}", merged);
|
||||
return merged;
|
||||
@@ -225,10 +247,11 @@ public class PortalConfigReader
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Console(1, Debug.ErrorLogLevel.Warning, "Cannot merge items at path {0}: \r{1}", propPath, e);
|
||||
Debug.LogError($"Cannot merge items at path {propPath}: \r{e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
return o1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,18 +4,28 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
public class EncodingHelper
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a EncodingHelper
|
||||
/// </summary>
|
||||
public class EncodingHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// ConvertUtf8ToAscii method
|
||||
/// </summary>
|
||||
public static string ConvertUtf8ToAscii(string utf8String)
|
||||
{
|
||||
return Encoding.ASCII.GetString(Encoding.UTF8.GetBytes(utf8String), 0, utf8String.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ConvertUtf8ToUtf16 method
|
||||
/// </summary>
|
||||
public static string ConvertUtf8ToUtf16(string utf8String)
|
||||
{
|
||||
return Encoding.Unicode.GetString(Encoding.UTF8.GetBytes(utf8String), 0, utf8String.Length);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,28 +6,30 @@ using Crestron.SimplSharp;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Unique key interface to require a unique key for the class
|
||||
/// </summary>
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique key interface to require a unique key for the class
|
||||
/// </summary>
|
||||
public interface IKeyed
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique key associated with the object.
|
||||
/// Unique Key
|
||||
/// </summary>
|
||||
[JsonProperty("key")]
|
||||
string Key { get; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Named Keyed device interface. Forces the device to have a Unique Key and a name.
|
||||
/// </summary>
|
||||
public interface IKeyName : IKeyed
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name associated with the current object.
|
||||
/// Named Keyed device interface. Forces the device to have a Unique Key and a name.
|
||||
/// </summary>
|
||||
public interface IKeyName : IKeyed
|
||||
{
|
||||
/// <summary>
|
||||
/// Isn't it obvious :)
|
||||
/// </summary>
|
||||
[JsonProperty("name")]
|
||||
string Name { get; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,11 +2,11 @@
|
||||
using System.Collections.Generic;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
//*********************************************************************************************************
|
||||
/// <summary>
|
||||
/// The core event and status-bearing class that most if not all device and connectors can derive from.
|
||||
/// Represents a Device
|
||||
/// </summary>
|
||||
public class Device : IKeyName
|
||||
{
|
||||
@@ -16,7 +16,7 @@ namespace PepperDash.Core;
|
||||
/// </summary>
|
||||
public string Key { get; protected set; }
|
||||
/// <summary>
|
||||
/// Name of the devie
|
||||
/// Gets or sets the Name
|
||||
/// </summary>
|
||||
public string Name { get; protected set; }
|
||||
/// <summary>
|
||||
@@ -24,14 +24,14 @@ namespace PepperDash.Core;
|
||||
/// </summary>
|
||||
public bool Enabled { get; protected set; }
|
||||
|
||||
///// <summary>
|
||||
///// A place to store reference to the original config object, if any. These values should
|
||||
///// NOT be used as properties on the device as they are all publicly-settable values.
|
||||
///// </summary>
|
||||
/// <summary>
|
||||
/// A place to store reference to the original config object, if any. These values should
|
||||
/// NOT be used as properties on the device as they are all publicly-settable values.
|
||||
/// </summary>
|
||||
//public DeviceConfig Config { get; private set; }
|
||||
///// <summary>
|
||||
///// Helper method to check if Config exists
|
||||
///// </summary>
|
||||
/// <summary>
|
||||
/// Helper method to check if Config exists
|
||||
/// </summary>
|
||||
//public bool HasConfig { get { return Config != null; } }
|
||||
|
||||
List<Action> _PreActivationActions;
|
||||
@@ -86,6 +86,9 @@ namespace PepperDash.Core;
|
||||
/// Adds a post activation action
|
||||
/// </summary>
|
||||
/// <param name="act"></param>
|
||||
/// <summary>
|
||||
/// AddPostActivationAction method
|
||||
/// </summary>
|
||||
public void AddPostActivationAction(Action act)
|
||||
{
|
||||
if (_PostActivationActions == null)
|
||||
@@ -94,7 +97,7 @@ namespace PepperDash.Core;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the preactivation actions
|
||||
/// PreActivate method
|
||||
/// </summary>
|
||||
public void PreActivate()
|
||||
{
|
||||
@@ -113,9 +116,7 @@ namespace PepperDash.Core;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets this device ready to be used in the system. Runs any added pre-activation items, and
|
||||
/// all post-activation at end. Classes needing additional logic to
|
||||
/// run should override CustomActivate()
|
||||
/// Activate method
|
||||
/// </summary>
|
||||
public bool Activate()
|
||||
{
|
||||
@@ -128,7 +129,7 @@ namespace PepperDash.Core;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the postactivation actions
|
||||
/// PostActivate method
|
||||
/// </summary>
|
||||
public void PostActivate()
|
||||
{
|
||||
@@ -152,6 +153,9 @@ namespace PepperDash.Core;
|
||||
/// do not need to call base.CustomActivate()
|
||||
/// </summary>
|
||||
/// <returns>true if device activated successfully.</returns>
|
||||
/// <summary>
|
||||
/// CustomActivate method
|
||||
/// </summary>
|
||||
public virtual bool CustomActivate() { return true; }
|
||||
|
||||
/// <summary>
|
||||
@@ -184,8 +188,12 @@ namespace PepperDash.Core;
|
||||
/// <remarks>The returned string is formatted as "{Key} - {Name}". If the <c>Name</c> property is
|
||||
/// null or empty, "---" is used in place of the name.</remarks>
|
||||
/// <returns>A string that represents the object, containing the key and name in the format "{Key} - {Name}".</returns>
|
||||
/// <summary>
|
||||
/// ToString method
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0} - {1}", Key, string.IsNullOrEmpty(Name) ? "---" : Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,11 @@
|
||||
using Newtonsoft.Json;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Class to help with accessing values from the CrestronEthernetHelper class
|
||||
/// </summary>
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a EthernetHelper
|
||||
/// </summary>
|
||||
public class EthernetHelper
|
||||
{
|
||||
/// <summary>
|
||||
@@ -25,7 +25,7 @@ namespace PepperDash.Core;
|
||||
// ADD OTHER HELPERS HERE
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the PortNumber
|
||||
/// </summary>
|
||||
public int PortNumber { get; private set; }
|
||||
|
||||
@@ -114,3 +114,4 @@ namespace PepperDash.Core;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Bool change event args
|
||||
/// </summary>
|
||||
@@ -17,17 +17,17 @@ namespace PepperDash.Core;
|
||||
public bool State { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Boolean ushort value property
|
||||
/// Gets or sets the IntValue
|
||||
/// </summary>
|
||||
public ushort IntValue { get { return (ushort)(State ? 1 : 0); } }
|
||||
|
||||
/// <summary>
|
||||
/// Boolean change event args type
|
||||
/// Gets or sets the Type
|
||||
/// </summary>
|
||||
public ushort Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Boolean change event args index
|
||||
/// Gets or sets the Index
|
||||
/// </summary>
|
||||
public ushort Index { get; set; }
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace PepperDash.Core;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ushort change event args
|
||||
/// Represents a UshrtChangeEventArgs
|
||||
/// </summary>
|
||||
public class UshrtChangeEventArgs : EventArgs
|
||||
{
|
||||
@@ -75,12 +75,12 @@ namespace PepperDash.Core;
|
||||
public ushort IntValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ushort change event args type
|
||||
/// Gets or sets the Type
|
||||
/// </summary>
|
||||
public ushort Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ushort change event args index
|
||||
/// Gets or sets the Index
|
||||
/// </summary>
|
||||
public ushort Index { get; set; }
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace PepperDash.Core;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// String change event args
|
||||
/// Represents a StringChangeEventArgs
|
||||
/// </summary>
|
||||
public class StringChangeEventArgs : EventArgs
|
||||
{
|
||||
@@ -128,12 +128,12 @@ namespace PepperDash.Core;
|
||||
public string StringValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// String change event args type
|
||||
/// Gets or sets the Type
|
||||
/// </summary>
|
||||
public ushort Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// string change event args index
|
||||
/// Gets or sets the Index
|
||||
/// </summary>
|
||||
public ushort Index { get; set; }
|
||||
|
||||
@@ -169,3 +169,4 @@ namespace PepperDash.Core;
|
||||
Index = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,13 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Core.GenericRESTfulCommunications;
|
||||
|
||||
namespace PepperDash.Core.GenericRESTfulCommunications
|
||||
{
|
||||
/// <summary>
|
||||
/// Constants
|
||||
/// </summary>
|
||||
public class GenericRESTfulConstants
|
||||
{
|
||||
public class GenericRESTfulConstants
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic boolean change
|
||||
/// </summary>
|
||||
@@ -35,4 +35,5 @@ public class GenericRESTfulConstants
|
||||
/// Error string change
|
||||
/// </summary>
|
||||
public const ushort ErrorStringChange = 203;
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,13 @@ using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.Net.Http;
|
||||
using Crestron.SimplSharp.Net.Https;
|
||||
|
||||
namespace PepperDash.Core.GenericRESTfulCommunications;
|
||||
|
||||
/// <summary>
|
||||
/// Generic RESTful communication class
|
||||
/// </summary>
|
||||
public class GenericRESTfulClient
|
||||
namespace PepperDash.Core.GenericRESTfulCommunications
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic RESTful communication class
|
||||
/// </summary>
|
||||
public class GenericRESTfulClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Boolean event handler
|
||||
/// </summary>
|
||||
@@ -78,7 +78,7 @@ public class GenericRESTfulClient
|
||||
|
||||
client.KeepAlive = false;
|
||||
|
||||
if (port >= 1 || port <= 65535)
|
||||
if(port >= 1 || port <= 65535)
|
||||
client.Port = port;
|
||||
else
|
||||
client.Port = 80;
|
||||
@@ -93,7 +93,7 @@ public class GenericRESTfulClient
|
||||
if (!string.IsNullOrEmpty(contentType))
|
||||
request.Header.ContentType = contentType;
|
||||
|
||||
request.Url = Crestron.SimplSharp.Net.Http.UrlParser.Parse(url);
|
||||
request.Url.Parse(url);
|
||||
request.RequestType = (Crestron.SimplSharp.Net.Http.RequestType)requestType;
|
||||
|
||||
response = client.Dispatch(request);
|
||||
@@ -148,17 +148,17 @@ public class GenericRESTfulClient
|
||||
if (!string.IsNullOrEmpty(contentType))
|
||||
request.Header.ContentType = contentType;
|
||||
|
||||
request.Url = Crestron.SimplSharp.Net.Https.UrlParser.Parse(url);
|
||||
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()))
|
||||
if(!string.IsNullOrEmpty(response.ContentString.ToString()))
|
||||
OnStringChange(response.ContentString.ToString(), 0, GenericRESTfulConstants.ResponseStringChange);
|
||||
|
||||
if (response.Code > 0)
|
||||
if(response.Code > 0)
|
||||
OnUshrtChange((ushort)response.Code, 0, GenericRESTfulConstants.ResponseCodeChange);
|
||||
|
||||
}
|
||||
@@ -196,7 +196,7 @@ public class GenericRESTfulClient
|
||||
var msg = string.Format("EncodeBase64({0}, {1}) failed:\r{2}", username, password, e);
|
||||
CrestronConsole.PrintLine(msg);
|
||||
ErrorLog.Error(msg);
|
||||
return "";
|
||||
return "" ;
|
||||
}
|
||||
|
||||
return authorization;
|
||||
@@ -252,4 +252,5 @@ public class GenericRESTfulClient
|
||||
StringChange(this, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Core.JsonStandardObjects;
|
||||
|
||||
namespace PepperDash.Core.JsonStandardObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Constants for simpl modules
|
||||
/// </summary>
|
||||
@@ -33,12 +33,12 @@ namespace PepperDash.Core.JsonStandardObjects;
|
||||
public DeviceConfig Device { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Device change event args type
|
||||
/// Gets or sets the Type
|
||||
/// </summary>
|
||||
public ushort Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Device change event args index
|
||||
/// Gets or sets the Index
|
||||
/// </summary>
|
||||
public ushort Index { get; set; }
|
||||
|
||||
@@ -74,3 +74,4 @@ namespace PepperDash.Core.JsonStandardObjects;
|
||||
Index = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using Crestron.SimplSharp;
|
||||
using PepperDash.Core.JsonToSimpl;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Core.JsonStandardObjects;
|
||||
|
||||
namespace PepperDash.Core.JsonStandardObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Device class
|
||||
/// </summary>
|
||||
@@ -58,6 +58,9 @@ namespace PepperDash.Core.JsonStandardObjects;
|
||||
/// </summary>
|
||||
/// <param name="uniqueID"></param>
|
||||
/// <param name="deviceKey"></param>
|
||||
/// <summary>
|
||||
/// Initialize method
|
||||
/// </summary>
|
||||
public void Initialize(string uniqueID, string deviceKey)
|
||||
{
|
||||
// S+ set EvaluateFb low
|
||||
@@ -180,3 +183,4 @@ namespace PepperDash.Core.JsonStandardObjects;
|
||||
|
||||
#endregion EventHandler Helpers
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Core.JsonStandardObjects;
|
||||
|
||||
namespace PepperDash.Core.JsonStandardObjects
|
||||
{
|
||||
/*
|
||||
Convert JSON snippt to C#: http://json2csharp.com/#
|
||||
|
||||
@@ -48,12 +48,12 @@ namespace PepperDash.Core.JsonStandardObjects;
|
||||
}
|
||||
*/
|
||||
/// <summary>
|
||||
/// Device communication parameter class
|
||||
/// Represents a ComParamsConfig
|
||||
/// </summary>
|
||||
public class ComParamsConfig
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the baudRate
|
||||
/// </summary>
|
||||
public int baudRate { get; set; }
|
||||
/// <summary>
|
||||
@@ -87,11 +87,11 @@ namespace PepperDash.Core.JsonStandardObjects;
|
||||
|
||||
// convert properties for simpl
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the simplBaudRate
|
||||
/// </summary>
|
||||
public ushort simplBaudRate { get { return Convert.ToUInt16(baudRate); } }
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the simplDataBits
|
||||
/// </summary>
|
||||
public ushort simplDataBits { get { return Convert.ToUInt16(dataBits); } }
|
||||
/// <summary>
|
||||
@@ -144,11 +144,11 @@ namespace PepperDash.Core.JsonStandardObjects;
|
||||
|
||||
// convert properties for simpl
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the simplPort
|
||||
/// </summary>
|
||||
public ushort simplPort { get { return Convert.ToUInt16(port); } }
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the simplAutoReconnect
|
||||
/// </summary>
|
||||
public ushort simplAutoReconnect { get { return (ushort)(autoReconnect ? 1 : 0); } }
|
||||
/// <summary>
|
||||
@@ -193,7 +193,7 @@ namespace PepperDash.Core.JsonStandardObjects;
|
||||
|
||||
// convert properties for simpl
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the simplControlPortNumber
|
||||
/// </summary>
|
||||
public ushort simplControlPortNumber { get { return Convert.ToUInt16(controlPortNumber); } }
|
||||
|
||||
@@ -208,7 +208,7 @@ namespace PepperDash.Core.JsonStandardObjects;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Device properties class
|
||||
/// Represents a PropertiesConfig
|
||||
/// </summary>
|
||||
public class PropertiesConfig
|
||||
{
|
||||
@@ -227,11 +227,11 @@ namespace PepperDash.Core.JsonStandardObjects;
|
||||
|
||||
// convert properties for simpl
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the simplDeviceId
|
||||
/// </summary>
|
||||
public ushort simplDeviceId { get { return Convert.ToUInt16(deviceId); } }
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the simplEnabled
|
||||
/// </summary>
|
||||
public ushort simplEnabled { get { return (ushort)(enabled ? 1 : 0); } }
|
||||
|
||||
@@ -254,3 +254,4 @@ namespace PepperDash.Core.JsonStandardObjects;
|
||||
/// </summary>
|
||||
public List<DeviceConfig> devices { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Core.JsonToSimpl;
|
||||
|
||||
namespace PepperDash.Core.JsonToSimpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Constants for Simpl modules
|
||||
/// </summary>
|
||||
@@ -89,7 +89,7 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
public class SPlusValueWrapper
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the ValueType
|
||||
/// </summary>
|
||||
public SPlusType ValueType { get; private set; }
|
||||
/// <summary>
|
||||
@@ -123,7 +123,7 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// S+ types enum
|
||||
/// Enumeration of SPlusType values
|
||||
/// </summary>
|
||||
public enum SPlusType
|
||||
{
|
||||
@@ -140,3 +140,4 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
/// </summary>
|
||||
String
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,11 @@ using Serilog.Events;
|
||||
|
||||
//using PepperDash.Core;
|
||||
|
||||
namespace PepperDash.Core.JsonToSimpl;
|
||||
|
||||
/// <summary>
|
||||
/// The global class to manage all the instances of JsonToSimplMaster
|
||||
/// </summary>
|
||||
namespace PepperDash.Core.JsonToSimpl
|
||||
{
|
||||
/// <summary>
|
||||
/// The global class to manage all the instances of JsonToSimplMaster
|
||||
/// </summary>
|
||||
public class J2SGlobal
|
||||
{
|
||||
static List<JsonToSimplMaster> Masters = new List<JsonToSimplMaster>();
|
||||
@@ -23,6 +23,9 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
/// </summary>
|
||||
/// <param name="master">New master to add</param>
|
||||
///
|
||||
/// <summary>
|
||||
/// AddMaster method
|
||||
/// </summary>
|
||||
public static void AddMaster(JsonToSimplMaster master)
|
||||
{
|
||||
if (master == null)
|
||||
@@ -50,10 +53,11 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a master by its key. Case-insensitive
|
||||
/// GetMasterByFile method
|
||||
/// </summary>
|
||||
public static JsonToSimplMaster GetMasterByFile(string file)
|
||||
{
|
||||
return Masters.FirstOrDefault(m => m.UniqueID.Equals(file, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,11 @@ using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Core.JsonToSimpl;
|
||||
|
||||
/// <summary>
|
||||
/// Used to interact with an array of values with the S+ modules
|
||||
/// </summary>
|
||||
namespace PepperDash.Core.JsonToSimpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a JsonToSimplArrayLookupChild
|
||||
/// </summary>
|
||||
public class JsonToSimplArrayLookupChild : JsonToSimplChildObjectBase
|
||||
{
|
||||
/// <summary>
|
||||
@@ -77,8 +77,9 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process all values
|
||||
/// ProcessAll method
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public override void ProcessAll()
|
||||
{
|
||||
if (FindInArray())
|
||||
@@ -159,3 +160,4 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,11 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace PepperDash.Core.JsonToSimpl;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for JSON objects
|
||||
/// </summary>
|
||||
namespace PepperDash.Core.JsonToSimpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for JSON objects
|
||||
/// </summary>
|
||||
public abstract class JsonToSimplChildObjectBase : IKeyed
|
||||
{
|
||||
/// <summary>
|
||||
@@ -29,12 +29,12 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
public SPlusValuesDelegate GetAllValuesDelegate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Use a callback to reduce task switch/threading
|
||||
/// Gets or sets the SetAllPathsDelegate
|
||||
/// </summary>
|
||||
public SPlusValuesDelegate SetAllPathsDelegate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unique identifier for instance
|
||||
/// Gets or sets the Key
|
||||
/// </summary>
|
||||
public string Key { get; protected set; }
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
public string PathSuffix { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the instance is linked to an object
|
||||
/// Gets or sets the LinkedToObject
|
||||
/// </summary>
|
||||
public bool LinkedToObject { get; protected set; }
|
||||
|
||||
@@ -96,6 +96,9 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
/// Sets the path prefix for the object
|
||||
/// </summary>
|
||||
/// <param name="pathPrefix"></param>
|
||||
/// <summary>
|
||||
/// SetPathPrefix method
|
||||
/// </summary>
|
||||
public void SetPathPrefix(string pathPrefix)
|
||||
{
|
||||
PathPrefix = pathPrefix;
|
||||
@@ -111,7 +114,7 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the JPath for a ushort out index.
|
||||
/// SetUshortPath method
|
||||
/// </summary>
|
||||
public void SetUshortPath(ushort index, string path)
|
||||
{
|
||||
@@ -121,7 +124,7 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the JPath for a string output index.
|
||||
/// SetStringPath method
|
||||
/// </summary>
|
||||
public void SetStringPath(ushort index, string path)
|
||||
{
|
||||
@@ -131,9 +134,9 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evalutates all outputs with defined paths. called by S+ when paths are ready to process
|
||||
/// and by Master when file is read.
|
||||
/// ProcessAll method
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public virtual void ProcessAll()
|
||||
{
|
||||
if (!LinkedToObject)
|
||||
@@ -277,6 +280,9 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="theValue"></param>
|
||||
/// <summary>
|
||||
/// USetBoolValue method
|
||||
/// </summary>
|
||||
public void USetBoolValue(ushort key, ushort theValue)
|
||||
{
|
||||
SetBoolValue(key, theValue == 1);
|
||||
@@ -287,6 +293,9 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="theValue"></param>
|
||||
/// <summary>
|
||||
/// SetBoolValue method
|
||||
/// </summary>
|
||||
public void SetBoolValue(ushort key, bool theValue)
|
||||
{
|
||||
if (BoolPaths.ContainsKey(key))
|
||||
@@ -298,6 +307,9 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="theValue"></param>
|
||||
/// <summary>
|
||||
/// SetUShortValue method
|
||||
/// </summary>
|
||||
public void SetUShortValue(ushort key, ushort theValue)
|
||||
{
|
||||
if (UshortPaths.ContainsKey(key))
|
||||
@@ -309,6 +321,9 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="theValue"></param>
|
||||
/// <summary>
|
||||
/// SetStringValue method
|
||||
/// </summary>
|
||||
public void SetStringValue(ushort key, string theValue)
|
||||
{
|
||||
if (StringPaths.ContainsKey(key))
|
||||
@@ -320,6 +335,9 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
/// </summary>
|
||||
/// <param name="keyPath"></param>
|
||||
/// <param name="valueToSave"></param>
|
||||
/// <summary>
|
||||
/// SetValueOnMaster method
|
||||
/// </summary>
|
||||
public void SetValueOnMaster(string keyPath, JValue valueToSave)
|
||||
{
|
||||
var path = GetFullPath(keyPath);
|
||||
@@ -401,3 +419,4 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace PepperDash.Core.JsonToSimpl;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a JSON file that can be read and written to
|
||||
/// </summary>
|
||||
public class JsonToSimplFileMaster : JsonToSimplMaster
|
||||
namespace PepperDash.Core.JsonToSimpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a JSON file that can be read and written to
|
||||
/// </summary>
|
||||
public class JsonToSimplFileMaster : JsonToSimplMaster
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the filepath as well as registers this with the Global.Masters list
|
||||
/// </summary>
|
||||
public string Filepath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Filepath to the actual file that will be read (Portal or local)
|
||||
/// Gets or sets the ActualFilePath
|
||||
/// </summary>
|
||||
public string ActualFilePath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the Filename
|
||||
/// </summary>
|
||||
public string Filename { get; private set; }
|
||||
/// <summary>
|
||||
@@ -92,7 +92,7 @@ public class JsonToSimplFileMaster : JsonToSimplMaster
|
||||
OnStringChange(roomName, 0, JsonToSimplConstants.RoomNameChange);
|
||||
}
|
||||
|
||||
var rootDirectory = Directory.GetCurrentDirectory();
|
||||
var rootDirectory = Directory.GetApplicationRootDirectory();
|
||||
OnStringChange(rootDirectory, 0, JsonToSimplConstants.RootDirectoryChange);
|
||||
|
||||
var splusPath = string.Empty;
|
||||
@@ -161,7 +161,7 @@ public class JsonToSimplFileMaster : JsonToSimplMaster
|
||||
OnStringChange(string.Format(@"JSON File Path is {0}", actualFile.DirectoryName), 0, JsonToSimplConstants.StringValueChange);
|
||||
Debug.Console(1, "JSON File Path is {0}", FilePathName);
|
||||
|
||||
var json = File.ReadAllText(ActualFilePath, System.Text.Encoding.ASCII);
|
||||
var json = File.ReadToEnd(ActualFilePath, System.Text.Encoding.ASCII);
|
||||
|
||||
JsonObject = JObject.Parse(json);
|
||||
foreach (var child in Children)
|
||||
@@ -194,6 +194,9 @@ public class JsonToSimplFileMaster : JsonToSimplMaster
|
||||
/// Sets the debug level
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
/// <summary>
|
||||
/// setDebugLevel method
|
||||
/// </summary>
|
||||
public void setDebugLevel(uint level)
|
||||
{
|
||||
Debug.SetDebugLevel(level);
|
||||
@@ -285,4 +288,5 @@ public class JsonToSimplFileMaster : JsonToSimplMaster
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
|
||||
namespace PepperDash.Core.JsonToSimpl;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
namespace PepperDash.Core.JsonToSimpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a JsonToSimplFixedPathObject
|
||||
/// </summary>
|
||||
public class JsonToSimplFixedPathObject : JsonToSimplChildObjectBase
|
||||
{
|
||||
/// <summary>
|
||||
@@ -15,3 +15,4 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
this.LinkedToObject = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,13 @@ using System.Collections.Generic;
|
||||
using Crestron.SimplSharp;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace PepperDash.Core.JsonToSimpl;
|
||||
|
||||
/// <summary>
|
||||
/// Generic Master
|
||||
/// </summary>
|
||||
public class JsonToSimplGenericMaster : JsonToSimplMaster
|
||||
namespace PepperDash.Core.JsonToSimpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a JsonToSimplGenericMaster
|
||||
/// </summary>
|
||||
public class JsonToSimplGenericMaster : JsonToSimplMaster
|
||||
{
|
||||
/*****************************************************************************************/
|
||||
/** Privates **/
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
static object WriteLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Callback action for saving
|
||||
/// Gets or sets the SaveCallback
|
||||
/// </summary>
|
||||
public Action<string> SaveCallback { get; set; }
|
||||
|
||||
@@ -60,6 +60,9 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
/// Loads JSON into JsonObject, but does not trigger evaluation by children
|
||||
/// </summary>
|
||||
/// <param name="json"></param>
|
||||
/// <summary>
|
||||
/// SetJsonWithoutEvaluating method
|
||||
/// </summary>
|
||||
public void SetJsonWithoutEvaluating(string json)
|
||||
{
|
||||
try
|
||||
@@ -73,8 +76,9 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Save method
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public override void Save()
|
||||
{
|
||||
// this code is duplicated in the other masters!!!!!!!!!!!!!
|
||||
@@ -115,3 +119,4 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
Debug.Console(0, this, "WARNING: No save callback defined.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,15 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace PepperDash.Core.JsonToSimpl;
|
||||
|
||||
/// <summary>
|
||||
/// Abstract base class for JsonToSimpl interactions
|
||||
/// </summary>
|
||||
namespace PepperDash.Core.JsonToSimpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract base class for JsonToSimpl interactions
|
||||
/// </summary>
|
||||
public abstract class JsonToSimplMaster : IKeyed
|
||||
{
|
||||
/// <summary>
|
||||
@@ -38,7 +39,7 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
public string Key { get { return UniqueID; } }
|
||||
|
||||
/// <summary>
|
||||
/// A unique ID
|
||||
/// Gets or sets the UniqueID
|
||||
/// </summary>
|
||||
public string UniqueID { get; protected set; }
|
||||
|
||||
@@ -53,8 +54,7 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
string _DebugName = "";
|
||||
|
||||
/// <summary>
|
||||
/// This will be prepended to all paths to allow path swapping or for more organized
|
||||
/// sub-paths
|
||||
/// Gets or sets the PathPrefix
|
||||
/// </summary>
|
||||
public string PathPrefix { get; set; }
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the JsonObject
|
||||
/// </summary>
|
||||
public JObject JsonObject { get; protected set; }
|
||||
|
||||
@@ -119,6 +119,9 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
/// Adds a child "module" to this master
|
||||
/// </summary>
|
||||
/// <param name="child"></param>
|
||||
/// <summary>
|
||||
/// AddChild method
|
||||
/// </summary>
|
||||
public void AddChild(JsonToSimplChildObjectBase child)
|
||||
{
|
||||
if (!Children.Contains(child))
|
||||
@@ -128,7 +131,7 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from the child to add changed or new values for saving
|
||||
/// AddUnsavedValue method
|
||||
/// </summary>
|
||||
public void AddUnsavedValue(string path, JValue value)
|
||||
{
|
||||
@@ -159,7 +162,11 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
/// <returns></returns>
|
||||
public static JObject ParseObject(string json)
|
||||
{
|
||||
using (var reader = new JsonTextReader(new StringReader(json)))
|
||||
#if NET6_0
|
||||
using (var reader = new JsonTextReader(new System.IO.StringReader(json)))
|
||||
#else
|
||||
using (var reader = new JsonTextReader(new Crestron.SimplSharp.CrestronIO.StringReader(json)))
|
||||
#endif
|
||||
{
|
||||
var startDepth = reader.Depth;
|
||||
var obj = JObject.Load(reader);
|
||||
@@ -174,10 +181,16 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
/// </summary>
|
||||
/// <param name="json"></param>
|
||||
/// <returns></returns>
|
||||
/// <summary>
|
||||
/// ParseArray method
|
||||
/// </summary>
|
||||
public static JArray ParseArray(string json)
|
||||
{
|
||||
|
||||
using (var reader = new JsonTextReader(new StringReader(json)))
|
||||
#if NET6_0
|
||||
using (var reader = new JsonTextReader(new System.IO.StringReader(json)))
|
||||
#else
|
||||
using (var reader = new JsonTextReader(new Crestron.SimplSharp.CrestronIO.StringReader(json)))
|
||||
#endif
|
||||
{
|
||||
var startDepth = reader.Depth;
|
||||
var obj = JArray.Load(reader);
|
||||
@@ -236,3 +249,4 @@ namespace PepperDash.Core.JsonToSimpl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core.Config;
|
||||
|
||||
namespace PepperDash.Core.JsonToSimpl;
|
||||
|
||||
/// <summary>
|
||||
/// Portal File Master
|
||||
/// </summary>
|
||||
public class JsonToSimplPortalFileMaster : JsonToSimplMaster
|
||||
namespace PepperDash.Core.JsonToSimpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Portal File Master
|
||||
/// </summary>
|
||||
public class JsonToSimplPortalFileMaster : JsonToSimplMaster
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the filepath as well as registers this with the Global.Masters list
|
||||
/// </summary>
|
||||
public string PortalFilepath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// File path of the actual file being read (Portal or local)
|
||||
/// Gets or sets the ActualFilePath
|
||||
/// </summary>
|
||||
public string ActualFilePath { get; private set; }
|
||||
|
||||
@@ -92,7 +92,7 @@ public class JsonToSimplPortalFileMaster : JsonToSimplMaster
|
||||
// At this point we should have a local file. Do it.
|
||||
Debug.Console(1, "Reading local JSON file {0}", ActualFilePath);
|
||||
|
||||
string json = File.ReadAllText(ActualFilePath, System.Text.Encoding.ASCII);
|
||||
string json = File.ReadToEnd(ActualFilePath, System.Text.Encoding.ASCII);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -128,6 +128,9 @@ public class JsonToSimplPortalFileMaster : JsonToSimplMaster
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
/// <summary>
|
||||
/// setDebugLevel method
|
||||
/// </summary>
|
||||
public void setDebugLevel(uint level)
|
||||
{
|
||||
Debug.SetDebugLevel(level);
|
||||
@@ -187,4 +190,5 @@ public class JsonToSimplPortalFileMaster : JsonToSimplMaster
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,13 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PepperDash.Core.Logging;
|
||||
|
||||
public class CrestronEnricher : ILogEventEnricher
|
||||
namespace PepperDash.Core.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a CrestronEnricher
|
||||
/// </summary>
|
||||
public class CrestronEnricher : ILogEventEnricher
|
||||
{
|
||||
static readonly string _appName;
|
||||
|
||||
static CrestronEnricher()
|
||||
@@ -27,10 +30,14 @@ public class CrestronEnricher : ILogEventEnricher
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Enrich method
|
||||
/// </summary>
|
||||
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
|
||||
{
|
||||
var property = propertyFactory.CreateProperty("App", _appName);
|
||||
|
||||
logEvent.AddOrUpdateProperty(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronDataStore;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Crestron.SimplSharp.CrestronLogger;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core.Logging;
|
||||
@@ -16,18 +17,16 @@ using Serilog.Formatting.Compact;
|
||||
using Serilog.Formatting.Json;
|
||||
using Serilog.Templates;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Contains debug commands for use in various situations
|
||||
/// </summary>
|
||||
public static class Debug
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public static class Debug
|
||||
{
|
||||
private static readonly string LevelStoreKey = "ConsoleDebugLevel";
|
||||
private static readonly string WebSocketLevelStoreKey = "WebsocketDebugLevel";
|
||||
private static readonly string ErrorLogLevelStoreKey = "ErrorLogDebugLevel";
|
||||
private static readonly string FileLevelStoreKey = "FileDebugLevel";
|
||||
private static readonly string DoNotLoadOnNextBootKey = "DoNotLoadOnNextBoot";
|
||||
|
||||
private static readonly Dictionary<uint, LogEventLevel> _logLevels = new Dictionary<uint, LogEventLevel>()
|
||||
{
|
||||
@@ -41,21 +40,27 @@ public static class Debug
|
||||
|
||||
private static ILogger _logger;
|
||||
|
||||
private static readonly LoggingLevelSwitch _consoleLoggingLevelSwitch;
|
||||
private static readonly LoggingLevelSwitch _consoleLogLevelSwitch;
|
||||
|
||||
private static readonly LoggingLevelSwitch _websocketLoggingLevelSwitch;
|
||||
private static readonly LoggingLevelSwitch _websocketLogLevelSwitch;
|
||||
|
||||
private static readonly LoggingLevelSwitch _errorLogLevelSwitch;
|
||||
|
||||
private static readonly LoggingLevelSwitch _fileLevelSwitch;
|
||||
private static readonly LoggingLevelSwitch _fileLogLevelSwitch;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the minimum log level for the websocket sink.
|
||||
/// </summary>
|
||||
public static LogEventLevel WebsocketMinimumLogLevel
|
||||
{
|
||||
get { return _websocketLoggingLevelSwitch.MinimumLevel; }
|
||||
get { return _websocketLogLevelSwitch.MinimumLevel; }
|
||||
}
|
||||
|
||||
private static readonly DebugWebsocketSink _websocketSink;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the websocket sink for debug logging.
|
||||
/// </summary>
|
||||
public static DebugWebsocketSink WebsocketSink
|
||||
{
|
||||
get { return _websocketSink; }
|
||||
@@ -79,12 +84,12 @@ public static class Debug
|
||||
public static string FileName = string.Format(@"app{0}Debug.json", InitialParametersClass.ApplicationNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Debug level to set for a given program.
|
||||
/// Gets or sets the Level
|
||||
/// </summary>
|
||||
public static int Level { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// When this is true, the configuration file will NOT be loaded until triggered by either a console command or a signal
|
||||
/// Gets or sets the DoNotLoadConfigOnNextBoot
|
||||
/// </summary>
|
||||
public static bool DoNotLoadConfigOnNextBoot { get; private set; }
|
||||
|
||||
@@ -92,10 +97,13 @@ public static class Debug
|
||||
|
||||
private const int SaveTimeoutMs = 30000;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the system is running on an appliance.
|
||||
/// </summary>
|
||||
public static bool IsRunningOnAppliance = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance;
|
||||
|
||||
/// <summary>
|
||||
/// Version for the currently loaded PepperDashCore dll
|
||||
/// Gets or sets the PepperDashCoreVersion
|
||||
/// </summary>
|
||||
public static string PepperDashCoreVersion { get; private set; }
|
||||
|
||||
@@ -107,19 +115,18 @@ public static class Debug
|
||||
/// </summary>
|
||||
private static bool _excludeAllMode;
|
||||
|
||||
//static bool ExcludeNoKeyMessages;
|
||||
|
||||
private static readonly Dictionary<string, object> IncludedExcludedKeys;
|
||||
|
||||
private static readonly LoggerConfiguration _defaultLoggerConfiguration;
|
||||
|
||||
private static LoggerConfiguration _loggerConfiguration;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the logger configuration for the debug logging.
|
||||
/// </summary>
|
||||
public static LoggerConfiguration LoggerConfiguration => _loggerConfiguration;
|
||||
|
||||
static Debug()
|
||||
{
|
||||
try
|
||||
{
|
||||
CrestronDataStoreStatic.InitCrestronDataStore();
|
||||
|
||||
@@ -131,19 +138,19 @@ public static class Debug
|
||||
|
||||
var defaultFileLogLevel = GetStoredLogEventLevel(FileLevelStoreKey);
|
||||
|
||||
_consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel);
|
||||
_consoleLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel);
|
||||
|
||||
_websocketLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel);
|
||||
_websocketLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel);
|
||||
|
||||
_errorLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultErrorLogLevel);
|
||||
|
||||
_fileLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultFileLogLevel);
|
||||
_fileLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultFileLogLevel);
|
||||
|
||||
_websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true));
|
||||
|
||||
var logFilePath = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ?
|
||||
$@"{Directory.GetCurrentDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}app{InitialParametersClass.ApplicationNumber}{Path.DirectorySeparatorChar}global-log.log" :
|
||||
$@"{Directory.GetCurrentDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}room{InitialParametersClass.RoomId}{Path.DirectorySeparatorChar}global-log.log";
|
||||
$@"{Directory.GetApplicationRootDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}app{InitialParametersClass.ApplicationNumber}{Path.DirectorySeparatorChar}global-log.log" :
|
||||
$@"{Directory.GetApplicationRootDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}room{InitialParametersClass.RoomId}{Path.DirectorySeparatorChar}global-log.log";
|
||||
|
||||
CrestronConsole.PrintLine($"Saving log files to {logFilePath}");
|
||||
|
||||
@@ -155,16 +162,31 @@ public static class Debug
|
||||
.MinimumLevel.Verbose()
|
||||
.Enrich.FromLogContext()
|
||||
.Enrich.With(new CrestronEnricher())
|
||||
.WriteTo.Sink(new DebugConsoleSink(new ExpressionTemplate("[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}")), levelSwitch: _consoleLoggingLevelSwitch)
|
||||
.WriteTo.Sink(_websocketSink, levelSwitch: _websocketLoggingLevelSwitch)
|
||||
.WriteTo.Sink(new DebugConsoleSink(new ExpressionTemplate("[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}")), levelSwitch: _consoleLogLevelSwitch)
|
||||
.WriteTo.Sink(_websocketSink, levelSwitch: _websocketLogLevelSwitch)
|
||||
.WriteTo.Sink(new DebugErrorLogSink(new ExpressionTemplate(errorLogTemplate)), levelSwitch: _errorLogLevelSwitch)
|
||||
.WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath,
|
||||
rollingInterval: RollingInterval.Day,
|
||||
restrictedToMinimumLevel: LogEventLevel.Debug,
|
||||
retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 30 : 60,
|
||||
levelSwitch: _fileLevelSwitch
|
||||
retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 7 : 14,
|
||||
levelSwitch: _fileLogLevelSwitch
|
||||
);
|
||||
|
||||
try
|
||||
{
|
||||
if (InitialParametersClass.NumberOfRemovableDrives > 0)
|
||||
{
|
||||
CrestronConsole.PrintLine("{0} RM Drive(s) Present. Initializing CrestronLogger", InitialParametersClass.NumberOfRemovableDrives);
|
||||
_defaultLoggerConfiguration.WriteTo.Sink(new DebugCrestronLoggerSink());
|
||||
}
|
||||
else
|
||||
CrestronConsole.PrintLine("No RM Drive(s) Present. Not using Crestron Logger");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CrestronConsole.PrintLine("Initializing of CrestronLogger failed: {0}", e);
|
||||
}
|
||||
|
||||
// Instantiate the root logger
|
||||
_loggerConfiguration = _defaultLoggerConfiguration;
|
||||
|
||||
@@ -206,35 +228,27 @@ public static class Debug
|
||||
|
||||
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
|
||||
|
||||
DoNotLoadConfigOnNextBoot = GetDoNotLoadOnNextBoot();
|
||||
LoadMemory();
|
||||
|
||||
var context = _contexts.GetOrCreateItem("DEFAULT");
|
||||
Level = context.Level;
|
||||
DoNotLoadConfigOnNextBoot = context.DoNotLoadOnNextBoot;
|
||||
|
||||
if (DoNotLoadConfigOnNextBoot)
|
||||
CrestronConsole.PrintLine(string.Format("Program {0} will not load config after next boot. Use console command go:{0} to load the config manually", InitialParametersClass.ApplicationNumber));
|
||||
|
||||
_consoleLoggingLevelSwitch.MinimumLevelChanged += (sender, args) =>
|
||||
_errorLogLevelSwitch.MinimumLevelChanged += (sender, args) =>
|
||||
{
|
||||
LogMessage(LogEventLevel.Information, "Console debug level set to {minimumLevel}", _consoleLoggingLevelSwitch.MinimumLevel);
|
||||
LogMessage(LogEventLevel.Information, "Error log debug level set to {minimumLevel}", _errorLogLevelSwitch.MinimumLevel);
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogError(ex, "Exception in Debug static constructor: {message}", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool GetDoNotLoadOnNextBoot()
|
||||
{
|
||||
var err = CrestronDataStoreStatic.GetLocalBoolValue(DoNotLoadOnNextBootKey, out var doNotLoad);
|
||||
|
||||
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
|
||||
{
|
||||
LogError("Error retrieving DoNotLoadOnNextBoot value: {err}", err);
|
||||
doNotLoad = false;
|
||||
}
|
||||
|
||||
return doNotLoad;
|
||||
|
||||
// Set initial error log level based on platform && stored level. If appliance, use stored level, otherwise default to verbose
|
||||
SetErrorLogMinimumDebugLevel(CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? _errorLogLevelSwitch.MinimumLevel : LogEventLevel.Verbose);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UpdateLoggerConfiguration method
|
||||
/// </summary>
|
||||
public static void UpdateLoggerConfiguration(LoggerConfiguration config)
|
||||
{
|
||||
_loggerConfiguration = config;
|
||||
@@ -242,6 +256,9 @@ public static class Debug
|
||||
_logger = config.CreateLogger();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ResetLoggerConfiguration method
|
||||
/// </summary>
|
||||
public static void ResetLoggerConfiguration()
|
||||
{
|
||||
_loggerConfiguration = _defaultLoggerConfiguration;
|
||||
@@ -258,7 +275,10 @@ public static class Debug
|
||||
if (result != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
|
||||
{
|
||||
CrestronConsole.Print($"Unable to retrieve stored log level for {levelStoreKey}.\r\nError: {result}.\r\nSetting level to {LogEventLevel.Information}\r\n");
|
||||
return LogEventLevel.Information;
|
||||
|
||||
CrestronDataStoreStatic.SetLocalIntValue(levelStoreKey, levelStoreKey == ErrorLogLevelStoreKey ? (int)LogEventLevel.Warning : (int)LogEventLevel.Information);
|
||||
|
||||
return levelStoreKey == ErrorLogLevelStoreKey ? LogEventLevel.Warning : LogEventLevel.Information;
|
||||
}
|
||||
|
||||
if (logLevel < 0 || logLevel > 5)
|
||||
@@ -267,6 +287,8 @@ public static class Debug
|
||||
return LogEventLevel.Information;
|
||||
}
|
||||
|
||||
CrestronConsole.PrintLine($"Stored log level for {levelStoreKey} is {logLevel}");
|
||||
|
||||
return (LogEventLevel)logLevel;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -322,6 +344,9 @@ public static class Debug
|
||||
/// Callback for console command
|
||||
/// </summary>
|
||||
/// <param name="levelString"></param>
|
||||
/// <summary>
|
||||
/// SetDebugFromConsole method
|
||||
/// </summary>
|
||||
public static void SetDebugFromConsole(string levelString)
|
||||
{
|
||||
try
|
||||
@@ -329,7 +354,11 @@ public static class Debug
|
||||
if (levelString.Trim() == "?")
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse(
|
||||
"Used to set the minimum level of debug messages to be printed to the console:\r\n" +
|
||||
"Used to set the minimum level of debug messages:\r\n" +
|
||||
"Usage: appdebug:P [sink] [level]\r\n" +
|
||||
" sink: console (default), errorlog, file, all\r\n" +
|
||||
" all: sets all sinks to the specified level\r\n" +
|
||||
" level: 0-5 or LogEventLevel name\r\n" +
|
||||
$"{_logLevels[0]} = 0\r\n" +
|
||||
$"{_logLevels[1]} = 1\r\n" +
|
||||
$"{_logLevels[2]} = 2\r\n" +
|
||||
@@ -341,32 +370,88 @@ public static class Debug
|
||||
|
||||
if (string.IsNullOrEmpty(levelString.Trim()))
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("AppDebug level = {0}", _consoleLoggingLevelSwitch.MinimumLevel);
|
||||
CrestronConsole.ConsoleCommandResponse("Console log level = {0}\r\n", _consoleLogLevelSwitch.MinimumLevel);
|
||||
CrestronConsole.ConsoleCommandResponse("File log level = {0}\r\n", _fileLogLevelSwitch.MinimumLevel);
|
||||
CrestronConsole.ConsoleCommandResponse("Error log level = {0}\r\n", _errorLogLevelSwitch.MinimumLevel);
|
||||
return;
|
||||
}
|
||||
|
||||
if (int.TryParse(levelString, out var levelInt))
|
||||
// Parse tokens: first token is sink (defaults to console), second token is level
|
||||
var tokens = levelString.Trim().Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
string sinkName;
|
||||
string levelToken;
|
||||
|
||||
if (tokens.Length == 1)
|
||||
{
|
||||
// Single token - assume it's a level for console sink
|
||||
sinkName = "console";
|
||||
levelToken = tokens[0];
|
||||
}
|
||||
else if (tokens.Length == 2)
|
||||
{
|
||||
// Two tokens - first is sink, second is level
|
||||
sinkName = tokens[0].ToLowerInvariant();
|
||||
levelToken = tokens[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [sink] [level]");
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the level using the same logic as before
|
||||
LogEventLevel level;
|
||||
|
||||
if (int.TryParse(levelToken, out var levelInt))
|
||||
{
|
||||
if (levelInt < 0 || levelInt > 5)
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelString} to valid log level. If using a number, value must be between 0-5");
|
||||
return;
|
||||
}
|
||||
SetDebugLevel((uint)levelInt);
|
||||
CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelToken} to valid log level. If using a number, value must be between 0-5");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Enum.TryParse<LogEventLevel>(levelString, out var levelEnum))
|
||||
if (!_logLevels.TryGetValue((uint)levelInt, out level))
|
||||
{
|
||||
SetDebugLevel(levelEnum);
|
||||
level = LogEventLevel.Information;
|
||||
CrestronConsole.ConsoleCommandResponse($"{levelInt} not valid. Setting level to {level}");
|
||||
}
|
||||
}
|
||||
else if (Enum.TryParse(levelToken, true, out level))
|
||||
{
|
||||
// Successfully parsed as LogEventLevel enum
|
||||
}
|
||||
else
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelToken} to valid log level");
|
||||
return;
|
||||
}
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelString} to valid log level");
|
||||
// Set the level for the specified sink
|
||||
switch (sinkName)
|
||||
{
|
||||
case "console":
|
||||
SetDebugLevel(level);
|
||||
break;
|
||||
case "errorlog":
|
||||
SetErrorLogMinimumDebugLevel(level);
|
||||
break;
|
||||
case "file":
|
||||
SetFileMinimumDebugLevel(level);
|
||||
break;
|
||||
case "all":
|
||||
SetDebugLevel(level);
|
||||
SetErrorLogMinimumDebugLevel(level);
|
||||
SetFileMinimumDebugLevel(level);
|
||||
break;
|
||||
default:
|
||||
CrestronConsole.ConsoleCommandResponse($"Error: Unknown sink '{sinkName}'. Valid sinks: console, errorlog, file");
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [0-5]");
|
||||
CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [sink] [level]");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,6 +459,9 @@ public static class Debug
|
||||
/// Sets the debug level
|
||||
/// </summary>
|
||||
/// <param name="level"> Valid values 0-5</param>
|
||||
/// <summary>
|
||||
/// SetDebugLevel method
|
||||
/// </summary>
|
||||
public static void SetDebugLevel(uint level)
|
||||
{
|
||||
if (!_logLevels.TryGetValue(level, out var logLevel))
|
||||
@@ -388,12 +476,15 @@ public static class Debug
|
||||
SetDebugLevel(logLevel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SetDebugLevel method
|
||||
/// </summary>
|
||||
public static void SetDebugLevel(LogEventLevel level)
|
||||
{
|
||||
_consoleLoggingLevelSwitch.MinimumLevel = level;
|
||||
_consoleLogLevelSwitch.MinimumLevel = level;
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse("[Application {0}], Debug level set to {1}\r\n",
|
||||
InitialParametersClass.ApplicationNumber, _consoleLoggingLevelSwitch.MinimumLevel);
|
||||
CrestronConsole.ConsoleCommandResponse("[Application {0}] Debug level set to {1}\r\n",
|
||||
InitialParametersClass.ApplicationNumber, _consoleLogLevelSwitch.MinimumLevel);
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}");
|
||||
|
||||
@@ -405,46 +496,68 @@ public static class Debug
|
||||
CrestronConsole.PrintLine($"Error saving console debug level setting: {err}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SetWebSocketMinimumDebugLevel method
|
||||
/// </summary>
|
||||
public static void SetWebSocketMinimumDebugLevel(LogEventLevel level)
|
||||
{
|
||||
_websocketLoggingLevelSwitch.MinimumLevel = level;
|
||||
_websocketLogLevelSwitch.MinimumLevel = level;
|
||||
|
||||
var err = CrestronDataStoreStatic.SetLocalUintValue(WebSocketLevelStoreKey, (uint)level);
|
||||
|
||||
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
|
||||
LogMessage(LogEventLevel.Information, "Error saving websocket debug level setting: {erro}", err);
|
||||
|
||||
LogMessage(LogEventLevel.Information, "Websocket debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel);
|
||||
LogMessage(LogEventLevel.Information, "Websocket debug level set to {0}", _websocketLogLevelSwitch.MinimumLevel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SetErrorLogMinimumDebugLevel method
|
||||
/// </summary>
|
||||
public static void SetErrorLogMinimumDebugLevel(LogEventLevel level)
|
||||
{
|
||||
_errorLogLevelSwitch.MinimumLevel = level;
|
||||
|
||||
var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)level);
|
||||
CrestronConsole.ConsoleCommandResponse("[Application {0}] Error log level set to {1}\r\n",
|
||||
InitialParametersClass.ApplicationNumber, _errorLogLevelSwitch.MinimumLevel);
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}");
|
||||
|
||||
var err = CrestronDataStoreStatic.SetLocalIntValue(ErrorLogLevelStoreKey, (int)level);
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse($"Store result: {err}:{(int)level}");
|
||||
|
||||
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
|
||||
LogMessage(LogEventLevel.Information, "Error saving Error Log debug level setting: {error}", err);
|
||||
|
||||
LogMessage(LogEventLevel.Information, "Error log debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel);
|
||||
CrestronConsole.PrintLine($"Error saving error log debug level setting: {err}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SetFileMinimumDebugLevel method
|
||||
/// </summary>
|
||||
public static void SetFileMinimumDebugLevel(LogEventLevel level)
|
||||
{
|
||||
_errorLogLevelSwitch.MinimumLevel = level;
|
||||
_fileLogLevelSwitch.MinimumLevel = level;
|
||||
|
||||
var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)level);
|
||||
CrestronConsole.ConsoleCommandResponse("[Application {0}] File log level set to {1}\r\n",
|
||||
InitialParametersClass.ApplicationNumber, _fileLogLevelSwitch.MinimumLevel);
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}");
|
||||
|
||||
var err = CrestronDataStoreStatic.SetLocalIntValue(FileLevelStoreKey, (int)level);
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse($"Store result: {err}:{(int)level}");
|
||||
|
||||
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
|
||||
LogMessage(LogEventLevel.Information, "Error saving File debug level setting: {error}", err);
|
||||
|
||||
LogMessage(LogEventLevel.Information, "File debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel);
|
||||
CrestronConsole.PrintLine($"Error saving file debug level setting: {err}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback for console command
|
||||
/// </summary>
|
||||
/// <param name="stateString"></param>
|
||||
/// <summary>
|
||||
/// SetDoNotLoadOnNextBootFromConsole method
|
||||
/// </summary>
|
||||
public static void SetDoNotLoadOnNextBootFromConsole(string stateString)
|
||||
{
|
||||
try
|
||||
@@ -467,6 +580,9 @@ public static class Debug
|
||||
/// Callback for console command
|
||||
/// </summary>
|
||||
/// <param name="items"></param>
|
||||
/// <summary>
|
||||
/// SetDebugFilterFromConsole method
|
||||
/// </summary>
|
||||
public static void SetDebugFilterFromConsole(string items)
|
||||
{
|
||||
var str = items.Trim();
|
||||
@@ -562,6 +678,9 @@ public static class Debug
|
||||
/// </summary>
|
||||
/// <param name="deviceKey"></param>
|
||||
/// <returns></returns>
|
||||
/// <summary>
|
||||
/// GetDeviceDebugSettingsForKey method
|
||||
/// </summary>
|
||||
public static object GetDeviceDebugSettingsForKey(string deviceKey)
|
||||
{
|
||||
return _contexts.GetDebugSettingsForKey(deviceKey);
|
||||
@@ -574,17 +693,15 @@ public static class Debug
|
||||
public static void SetDoNotLoadConfigOnNextBoot(bool state)
|
||||
{
|
||||
DoNotLoadConfigOnNextBoot = state;
|
||||
_contexts.GetOrCreateItem("DEFAULT").DoNotLoadOnNextBoot = state;
|
||||
SaveMemoryOnTimeout();
|
||||
|
||||
var err = CrestronDataStoreStatic.SetLocalBoolValue(DoNotLoadOnNextBootKey, state);
|
||||
|
||||
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
|
||||
LogError("Error saving console debug level setting: {err}", err);
|
||||
|
||||
LogInformation("Do Not Load Config on Next Boot set to {state}", DoNotLoadConfigOnNextBoot);
|
||||
CrestronConsole.ConsoleCommandResponse("[Application {0}], Do Not Load Config on Next Boot set to {1}",
|
||||
InitialParametersClass.ApplicationNumber, DoNotLoadConfigOnNextBoot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// ShowDebugLog method
|
||||
/// </summary>
|
||||
public static void ShowDebugLog(string s)
|
||||
{
|
||||
@@ -600,6 +717,9 @@ public static class Debug
|
||||
/// <param name="message">Message template</param>
|
||||
/// <param name="device">Optional IKeyed device. If provided, the Key of the device will be added to the log message</param>
|
||||
/// <param name="args">Args to put into message template</param>
|
||||
/// <summary>
|
||||
/// LogMessage method
|
||||
/// </summary>
|
||||
public static void LogMessage(Exception ex, string message, IKeyed device = null, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", device?.Key))
|
||||
@@ -623,21 +743,36 @@ public static class Debug
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a message at the specified log level.
|
||||
/// </summary>
|
||||
/// <param name="level">Level to log at</param>
|
||||
/// <param name="message">Message template</param>
|
||||
/// <param name="args">Args to put into message template</param>
|
||||
public static void LogMessage(LogEventLevel level, string message, params object[] args)
|
||||
{
|
||||
_logger.Write(level, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogMessage method
|
||||
/// </summary>
|
||||
public static void LogMessage(LogEventLevel level, Exception ex, string message, params object[] args)
|
||||
{
|
||||
_logger.Write(level, ex, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogMessage method
|
||||
/// </summary>
|
||||
public static void LogMessage(LogEventLevel level, IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
LogMessage(level, message, keyed, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogMessage method
|
||||
/// </summary>
|
||||
public static void LogMessage(LogEventLevel level, Exception ex, IKeyed device, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", device?.Key))
|
||||
@@ -647,6 +782,9 @@ public static class Debug
|
||||
}
|
||||
|
||||
#region Explicit methods for logging levels
|
||||
/// <summary>
|
||||
/// LogVerbose method
|
||||
/// </summary>
|
||||
public static void LogVerbose(IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
@@ -655,6 +793,9 @@ public static class Debug
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogVerbose method
|
||||
/// </summary>
|
||||
public static void LogVerbose(Exception ex, IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
@@ -663,16 +804,25 @@ public static class Debug
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogVerbose method
|
||||
/// </summary>
|
||||
public static void LogVerbose(string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Verbose, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogVerbose method
|
||||
/// </summary>
|
||||
public static void LogVerbose(Exception ex, string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Verbose, ex, null, message, args);
|
||||
_logger.Write(LogEventLevel.Verbose, ex, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogDebug method
|
||||
/// </summary>
|
||||
public static void LogDebug(IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
@@ -681,6 +831,9 @@ public static class Debug
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogDebug method
|
||||
/// </summary>
|
||||
public static void LogDebug(Exception ex, IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
@@ -689,16 +842,25 @@ public static class Debug
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogDebug method
|
||||
/// </summary>
|
||||
public static void LogDebug(string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Debug, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogDebug method
|
||||
/// </summary>
|
||||
public static void LogDebug(Exception ex, string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Debug, ex, null, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogInformation method
|
||||
/// </summary>
|
||||
public static void LogInformation(IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
@@ -707,6 +869,9 @@ public static class Debug
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogInformation method
|
||||
/// </summary>
|
||||
public static void LogInformation(Exception ex, IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
@@ -715,16 +880,25 @@ public static class Debug
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogInformation method
|
||||
/// </summary>
|
||||
public static void LogInformation(string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Information, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogInformation method
|
||||
/// </summary>
|
||||
public static void LogInformation(Exception ex, string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Information, ex, null, message, args);
|
||||
_logger.Write(LogEventLevel.Information, ex, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogWarning method
|
||||
/// </summary>
|
||||
public static void LogWarning(IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
@@ -733,6 +907,9 @@ public static class Debug
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogWarning method
|
||||
/// </summary>
|
||||
public static void LogWarning(Exception ex, IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
@@ -741,16 +918,25 @@ public static class Debug
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogWarning method
|
||||
/// </summary>
|
||||
public static void LogWarning(string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Warning, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogWarning method
|
||||
/// </summary>
|
||||
public static void LogWarning(Exception ex, string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Warning, ex, null, message, args);
|
||||
_logger.Write(LogEventLevel.Warning, ex, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogError method
|
||||
/// </summary>
|
||||
public static void LogError(IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
@@ -759,6 +945,9 @@ public static class Debug
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogError method
|
||||
/// </summary>
|
||||
public static void LogError(Exception ex, IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
@@ -767,16 +956,25 @@ public static class Debug
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogError method
|
||||
/// </summary>
|
||||
public static void LogError(string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Error, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogError method
|
||||
/// </summary>
|
||||
public static void LogError(Exception ex, string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Error, ex, null, message, args);
|
||||
_logger.Write(LogEventLevel.Error, ex, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogFatal method
|
||||
/// </summary>
|
||||
public static void LogFatal(IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
@@ -785,6 +983,9 @@ public static class Debug
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogFatal method
|
||||
/// </summary>
|
||||
public static void LogFatal(Exception ex, IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
@@ -793,14 +994,20 @@ public static class Debug
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogFatal method
|
||||
/// </summary>
|
||||
public static void LogFatal(string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Fatal, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogFatal method
|
||||
/// </summary>
|
||||
public static void LogFatal(Exception ex, string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Fatal, ex, null, message, args);
|
||||
_logger.Write(LogEventLevel.Fatal, ex, message, args);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -1004,11 +1211,11 @@ public static class Debug
|
||||
return string.Format(@"\user\debugSettings\program{0}", InitialParametersClass.ApplicationNumber);
|
||||
}
|
||||
|
||||
return string.Format("{0}{1}user{1}debugSettings{1}{2}.json", Directory.GetCurrentDirectory(), Path.DirectorySeparatorChar, InitialParametersClass.RoomId);
|
||||
return string.Format("{0}{1}user{1}debugSettings{1}{2}.json", Directory.GetApplicationRootDirectory(), Path.DirectorySeparatorChar, InitialParametersClass.RoomId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error level to for message to be logged at
|
||||
/// Enumeration of ErrorLogLevel values
|
||||
/// </summary>
|
||||
public enum ErrorLogLevel
|
||||
{
|
||||
@@ -1029,4 +1236,5 @@ public static class Debug
|
||||
/// </summary>
|
||||
None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,18 @@ using System.IO;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
public class DebugConsoleSink : ILogEventSink
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a DebugConsoleSink
|
||||
/// </summary>
|
||||
public class DebugConsoleSink : ILogEventSink
|
||||
{
|
||||
private readonly ITextFormatter _textFormatter;
|
||||
|
||||
/// <summary>
|
||||
/// Emit method
|
||||
/// </summary>
|
||||
public void Emit(LogEvent logEvent)
|
||||
{
|
||||
if (!Debug.IsRunningOnAppliance) return;
|
||||
@@ -40,14 +46,19 @@ public class DebugConsoleSink : ILogEventSink
|
||||
_textFormatter = formatProvider ?? new JsonFormatter();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class DebugConsoleSinkExtensions
|
||||
{
|
||||
public static class DebugConsoleSinkExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// DebugConsoleSink method
|
||||
/// </summary>
|
||||
public static LoggerConfiguration DebugConsoleSink(
|
||||
this LoggerSinkConfiguration loggerConfiguration,
|
||||
ITextFormatter formatProvider = null)
|
||||
{
|
||||
return loggerConfiguration.Sink(new DebugConsoleSink(formatProvider));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a debugging context
|
||||
/// </summary>
|
||||
public class DebugContext
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a debugging context
|
||||
/// </summary>
|
||||
public class DebugContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the folder location where a given program stores it's debug level memory. By default, the
|
||||
/// file written will be named appNdebug where N is 1-10.
|
||||
/// </summary>
|
||||
public string Key { get; private set; }
|
||||
|
||||
///// <summary>
|
||||
///// The name of the file containing the current debug settings.
|
||||
///// </summary>
|
||||
/// <summary>
|
||||
/// The name of the file containing the current debug settings.
|
||||
/// </summary>
|
||||
//string FileName = string.Format(@"\nvram\debug\app{0}Debug.json", InitialParametersClass.ApplicationNumber);
|
||||
|
||||
DebugContextSaveData SaveData;
|
||||
@@ -38,6 +38,9 @@ public class DebugContext
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
/// <summary>
|
||||
/// GetDebugContext method
|
||||
/// </summary>
|
||||
public static DebugContext GetDebugContext(string key)
|
||||
{
|
||||
var context = Contexts.FirstOrDefault(c => c.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
|
||||
@@ -92,6 +95,9 @@ public class DebugContext
|
||||
/// Callback for console command
|
||||
/// </summary>
|
||||
/// <param name="levelString"></param>
|
||||
/// <summary>
|
||||
/// SetDebugFromConsole method
|
||||
/// </summary>
|
||||
public void SetDebugFromConsole(string levelString)
|
||||
{
|
||||
try
|
||||
@@ -114,6 +120,9 @@ public class DebugContext
|
||||
/// Sets the debug level
|
||||
/// </summary>
|
||||
/// <param name="level"> Valid values 0 (no debug), 1 (critical), 2 (all messages)</param>
|
||||
/// <summary>
|
||||
/// SetDebugLevel method
|
||||
/// </summary>
|
||||
public void SetDebugLevel(int level)
|
||||
{
|
||||
if (level <= 2)
|
||||
@@ -141,7 +150,7 @@ public class DebugContext
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends a device Key to the beginning of a message
|
||||
/// Console method
|
||||
/// </summary>
|
||||
public void Console(uint level, IKeyed dev, string format, params object[] items)
|
||||
{
|
||||
@@ -191,6 +200,9 @@ public class DebugContext
|
||||
/// </summary>
|
||||
/// <param name="errorLogLevel"></param>
|
||||
/// <param name="str"></param>
|
||||
/// <summary>
|
||||
/// LogError method
|
||||
/// </summary>
|
||||
public void LogError(Debug.ErrorLogLevel errorLogLevel, string str)
|
||||
{
|
||||
string msg = string.Format("App {0}:{1}", InitialParametersClass.ApplicationNumber, str);
|
||||
@@ -266,15 +278,16 @@ public class DebugContext
|
||||
{
|
||||
return string.Format(@"\NVRAM\debugSettings\program{0}-{1}", InitialParametersClass.ApplicationNumber, Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class DebugContextSaveData
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class DebugContextSaveData
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int Level { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,16 @@ using Crestron.SimplSharp.CrestronLogger;
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Core.Logging;
|
||||
|
||||
public class DebugCrestronLoggerSink : ILogEventSink
|
||||
namespace PepperDash.Core.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a DebugCrestronLoggerSink
|
||||
/// </summary>
|
||||
public class DebugCrestronLoggerSink : ILogEventSink
|
||||
{
|
||||
/// <summary>
|
||||
/// Emit method
|
||||
/// </summary>
|
||||
public void Emit(LogEvent logEvent)
|
||||
{
|
||||
if (!Debug.IsRunningOnAppliance) return;
|
||||
@@ -25,4 +31,5 @@ public class DebugCrestronLoggerSink : ILogEventSink
|
||||
{
|
||||
CrestronLogger.Initialize(1, LoggerModeEnum.RM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,13 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PepperDash.Core.Logging;
|
||||
|
||||
public class DebugErrorLogSink : ILogEventSink
|
||||
namespace PepperDash.Core.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a DebugErrorLogSink
|
||||
/// </summary>
|
||||
public class DebugErrorLogSink : ILogEventSink
|
||||
{
|
||||
private ITextFormatter _formatter;
|
||||
|
||||
private Dictionary<LogEventLevel, Action<string>> _errorLogMap = new Dictionary<LogEventLevel, Action<string>>
|
||||
@@ -24,6 +27,9 @@ public class DebugErrorLogSink : ILogEventSink
|
||||
{LogEventLevel.Error, (msg) => ErrorLog.Error(msg) },
|
||||
{LogEventLevel.Fatal, (msg) => ErrorLog.Error(msg) }
|
||||
};
|
||||
/// <summary>
|
||||
/// Emit method
|
||||
/// </summary>
|
||||
public void Emit(LogEvent logEvent)
|
||||
{
|
||||
string message;
|
||||
@@ -61,4 +67,5 @@ public class DebugErrorLogSink : ILogEventSink
|
||||
{
|
||||
_formatter = formatter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,73 +1,113 @@
|
||||
using Serilog.Events;
|
||||
using System;
|
||||
using System;
|
||||
using Serilog.Events;
|
||||
using Log = PepperDash.Core.Debug;
|
||||
|
||||
namespace PepperDash.Core.Logging;
|
||||
|
||||
public static class DebugExtensions
|
||||
namespace PepperDash.Core.Logging
|
||||
{
|
||||
public static class DebugExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// LogException method
|
||||
/// </summary>
|
||||
public static void LogException(this IKeyed device, Exception ex, string message, params object[] args)
|
||||
{
|
||||
Log.LogMessage(ex, message, device, args);
|
||||
Log.LogMessage(ex, message, device: device, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogVerbose method
|
||||
/// </summary>
|
||||
public static void LogVerbose(this IKeyed device, Exception ex, string message, params object[] args)
|
||||
{
|
||||
Log.LogMessage(LogEventLevel.Verbose, ex, message, device, args);
|
||||
Log.LogVerbose(ex, device, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogVerbose method
|
||||
/// </summary>
|
||||
public static void LogVerbose(this IKeyed device, string message, params object[] args)
|
||||
{
|
||||
Log.LogMessage(LogEventLevel.Verbose, device, message, args);
|
||||
Log.LogVerbose(device, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogDebug method
|
||||
/// </summary>
|
||||
public static void LogDebug(this IKeyed device, Exception ex, string message, params object[] args)
|
||||
{
|
||||
Log.LogMessage(LogEventLevel.Debug, ex, message, device, args);
|
||||
Log.LogDebug(ex, device, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogDebug method
|
||||
/// </summary>
|
||||
public static void LogDebug(this IKeyed device, string message, params object[] args)
|
||||
{
|
||||
Log.LogMessage(LogEventLevel.Debug, device, message, args);
|
||||
Log.LogDebug(device, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogInformation method
|
||||
/// </summary>
|
||||
public static void LogInformation(this IKeyed device, Exception ex, string message, params object[] args)
|
||||
{
|
||||
Log.LogMessage(LogEventLevel.Information, ex, message, device, args);
|
||||
Log.LogInformation(ex, device, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogInformation method
|
||||
/// </summary>
|
||||
public static void LogInformation(this IKeyed device, string message, params object[] args)
|
||||
{
|
||||
Log.LogMessage(LogEventLevel.Information, device, message, args);
|
||||
Log.LogInformation(device, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogWarning method
|
||||
/// </summary>
|
||||
public static void LogWarning(this IKeyed device, Exception ex, string message, params object[] args)
|
||||
{
|
||||
Log.LogMessage(LogEventLevel.Warning, ex, message, device, args);
|
||||
Log.LogWarning(ex, device, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogWarning method
|
||||
/// </summary>
|
||||
public static void LogWarning(this IKeyed device, string message, params object[] args)
|
||||
{
|
||||
Log.LogMessage(LogEventLevel.Warning, device, message, args);
|
||||
Log.LogWarning(device, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogError method
|
||||
/// </summary>
|
||||
public static void LogError(this IKeyed device, Exception ex, string message, params object[] args)
|
||||
{
|
||||
Log.LogMessage(LogEventLevel.Error, ex, message, device, args);
|
||||
Log.LogError(ex, device, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogError method
|
||||
/// </summary>
|
||||
public static void LogError(this IKeyed device, string message, params object[] args)
|
||||
{
|
||||
Log.LogMessage(LogEventLevel.Error, device, message, args);
|
||||
Log.LogError(device, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogFatal method
|
||||
/// </summary>
|
||||
public static void LogFatal(this IKeyed device, Exception ex, string message, params object[] args)
|
||||
{
|
||||
Log.LogMessage(LogEventLevel.Fatal, ex, message, device, args);
|
||||
Log.LogFatal(ex, device, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LogFatal method
|
||||
/// </summary>
|
||||
public static void LogFatal(this IKeyed device, string message, params object[] args)
|
||||
{
|
||||
Log.LogMessage(LogEventLevel.Fatal, device, message, args);
|
||||
Log.LogFatal(device, message, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
using Crestron.SimplSharp;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PepperDash.Core.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// Class to persist current Debug settings across program restarts
|
||||
/// </summary>
|
||||
namespace PepperDash.Core.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a DebugContextCollection
|
||||
/// </summary>
|
||||
public class DebugContextCollection
|
||||
{
|
||||
/// <summary>
|
||||
@@ -39,6 +39,9 @@ namespace PepperDash.Core.Logging;
|
||||
/// </summary>
|
||||
/// <param name="contextKey"></param>
|
||||
/// <param name="level"></param>
|
||||
/// <summary>
|
||||
/// SetLevel method
|
||||
/// </summary>
|
||||
public void SetLevel(string contextKey, int level)
|
||||
{
|
||||
if (level < 0 || level > 2)
|
||||
@@ -51,6 +54,9 @@ namespace PepperDash.Core.Logging;
|
||||
/// </summary>
|
||||
/// <param name="contextKey"></param>
|
||||
/// <returns></returns>
|
||||
/// <summary>
|
||||
/// GetOrCreateItem method
|
||||
/// </summary>
|
||||
public DebugContextItem GetOrCreateItem(string contextKey)
|
||||
{
|
||||
if (!_items.ContainsKey(contextKey))
|
||||
@@ -65,6 +71,9 @@ namespace PepperDash.Core.Logging;
|
||||
/// <param name="deviceKey"></param>
|
||||
/// <param name="settings"></param>
|
||||
/// <returns></returns>
|
||||
/// <summary>
|
||||
/// SetDebugSettingsForKey method
|
||||
/// </summary>
|
||||
public void SetDebugSettingsForKey(string deviceKey, object settings)
|
||||
{
|
||||
try
|
||||
@@ -89,6 +98,9 @@ namespace PepperDash.Core.Logging;
|
||||
/// </summary>
|
||||
/// <param name="deviceKey"></param>
|
||||
/// <returns></returns>
|
||||
/// <summary>
|
||||
/// GetDebugSettingsForKey method
|
||||
/// </summary>
|
||||
public object GetDebugSettingsForKey(string deviceKey)
|
||||
{
|
||||
return DeviceDebugSettings[deviceKey];
|
||||
@@ -112,3 +124,4 @@ namespace PepperDash.Core.Logging;
|
||||
[JsonProperty("doNotLoadOnNextBoot")]
|
||||
public bool DoNotLoadOnNextBoot { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,37 @@
|
||||
using Crestron.SimplSharp;
|
||||
using Org.BouncyCastle.Asn1.X509;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using Serilog.Configuration;
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
using Serilog.Formatting;
|
||||
using Serilog.Formatting.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Authentication;
|
||||
using WebSocketSharp;
|
||||
using Serilog.Configuration;
|
||||
using WebSocketSharp.Server;
|
||||
using Crestron.SimplSharp;
|
||||
using WebSocketSharp;
|
||||
using System.Security.Authentication;
|
||||
using WebSocketSharp.Net;
|
||||
using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2;
|
||||
using System.IO;
|
||||
using Org.BouncyCastle.Asn1.X509;
|
||||
using Serilog.Formatting;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Serilog.Formatting.Json;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a WebSocket-based logging sink for debugging purposes, allowing log events to be broadcast to connected
|
||||
/// WebSocket clients.
|
||||
/// </summary>
|
||||
/// <remarks>This class implements the <see cref="ILogEventSink"/> interface and is designed to send
|
||||
/// formatted log events to WebSocket clients connected to a secure WebSocket server. The server is hosted locally
|
||||
/// and uses a self-signed certificate for SSL/TLS encryption.</remarks>
|
||||
public class DebugWebsocketSink : ILogEventSink, IKeyed
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a DebugWebsocketSink
|
||||
/// </summary>
|
||||
public class DebugWebsocketSink : ILogEventSink
|
||||
{
|
||||
private HttpServer _httpsServer;
|
||||
|
||||
private readonly string _path = "/debug/join/";
|
||||
private string _path = "/debug/join/";
|
||||
private const string _certificateName = "selfCres";
|
||||
private const string _certificatePassword = "cres12345";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the port number on which the HTTPS server is currently running.
|
||||
/// </summary>
|
||||
public int Port
|
||||
{ get
|
||||
{
|
||||
@@ -42,11 +41,6 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the WebSocket URL for the current server instance.
|
||||
/// </summary>
|
||||
/// <remarks>The URL is dynamically constructed based on the server's current IP address, port,
|
||||
/// and WebSocket path.</remarks>
|
||||
public string Url
|
||||
{
|
||||
get
|
||||
@@ -57,30 +51,20 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the HTTPS server is currently listening for incoming connections.
|
||||
/// Gets or sets the IsRunning
|
||||
/// </summary>
|
||||
public bool IsRunning { get => _httpsServer?.IsListening ?? false; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Key => "DebugWebsocketSink";
|
||||
|
||||
private readonly ITextFormatter _textFormatter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DebugWebsocketSink"/> class with the specified text formatter.
|
||||
/// </summary>
|
||||
/// <remarks>This constructor initializes the WebSocket sink and ensures that a certificate is
|
||||
/// available for secure communication. If the required certificate does not exist, it will be created
|
||||
/// automatically. Additionally, the sink is configured to stop the server when the program is
|
||||
/// stopping.</remarks>
|
||||
/// <param name="formatProvider">The text formatter used to format log messages. If null, a default JSON formatter is used.</param>
|
||||
public DebugWebsocketSink(ITextFormatter formatProvider)
|
||||
{
|
||||
|
||||
_textFormatter = formatProvider ?? new JsonFormatter();
|
||||
|
||||
if (!File.Exists($"\\user\\{_certificateName}.pfx"))
|
||||
CreateCert();
|
||||
CreateCert(null);
|
||||
|
||||
CrestronEnvironment.ProgramStatusEventHandler += type =>
|
||||
{
|
||||
@@ -91,41 +75,45 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
||||
};
|
||||
}
|
||||
|
||||
private static void CreateCert()
|
||||
private void CreateCert(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Debug.Console(0,"CreateCert Creating Utility");
|
||||
CrestronConsole.PrintLine("CreateCert Creating Utility");
|
||||
//var utility = new CertificateUtility();
|
||||
var utility = new BouncyCertificate();
|
||||
|
||||
//Debug.Console(0, "CreateCert Calling CreateCert");
|
||||
CrestronConsole.PrintLine("CreateCert Calling CreateCert");
|
||||
//utility.CreateCert();
|
||||
var ipAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);
|
||||
var hostName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0);
|
||||
var domainName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0);
|
||||
|
||||
//Debug.Console(0, "DomainName: {0} | HostName: {1} | {1}.{0}@{2}", domainName, hostName, ipAddress);
|
||||
CrestronConsole.PrintLine(string.Format("DomainName: {0} | HostName: {1} | {1}.{0}@{2}", domainName, hostName, ipAddress));
|
||||
|
||||
var certificate = utility.CreateSelfSignedCertificate(string.Format("CN={0}.{1}", hostName, domainName), [string.Format("{0}.{1}", hostName, domainName), ipAddress], [KeyPurposeID.id_kp_serverAuth, KeyPurposeID.id_kp_clientAuth]);
|
||||
|
||||
var certificate = utility.CreateSelfSignedCertificate(string.Format("CN={0}.{1}", hostName, domainName), new[] { string.Format("{0}.{1}", hostName, domainName), ipAddress }, new[] { KeyPurposeID.id_kp_serverAuth, KeyPurposeID.id_kp_clientAuth });
|
||||
//Crestron fails to let us do this...perhaps it should be done through their Dll's but haven't tested
|
||||
|
||||
var separator = Path.DirectorySeparatorChar;
|
||||
|
||||
//Debug.Print($"CreateCert Storing Certificate To My.LocalMachine");
|
||||
//utility.AddCertToStore(certificate, StoreName.My, StoreLocation.LocalMachine);
|
||||
//Debug.Console(0, "CreateCert Saving Cert to \\user\\");
|
||||
CrestronConsole.PrintLine("CreateCert Saving Cert to \\user\\");
|
||||
utility.CertificatePassword = _certificatePassword;
|
||||
utility.WriteCertificate(certificate, @$"{separator}user{separator}", _certificateName);
|
||||
utility.WriteCertificate(certificate, @"\user\", _certificateName);
|
||||
//Debug.Console(0, "CreateCert Ending CreateCert");
|
||||
CrestronConsole.PrintLine("CreateCert Ending CreateCert");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//Debug.Console(0, "WSS CreateCert Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace);
|
||||
CrestronConsole.PrintLine("WSS CreateCert Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace);
|
||||
CrestronConsole.PrintLine(string.Format("WSS CreateCert Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a log event to all connected WebSocket clients.
|
||||
/// Emit method
|
||||
/// </summary>
|
||||
/// <remarks>The log event is formatted using the configured text formatter and then broadcasted
|
||||
/// to all clients connected to the WebSocket server. If the WebSocket server is not initialized or not
|
||||
/// listening, the method exits without performing any action.</remarks>
|
||||
/// <param name="logEvent">The log event to be formatted and broadcasted. Cannot be null.</param>
|
||||
public void Emit(LogEvent logEvent)
|
||||
{
|
||||
if (_httpsServer == null || !_httpsServer.IsListening) return;
|
||||
@@ -133,16 +121,13 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
||||
var sw = new StringWriter();
|
||||
_textFormatter.Format(logEvent, sw);
|
||||
|
||||
_httpsServer.WebSocketServices[_path].Sessions.Broadcast(sw.ToString());
|
||||
_httpsServer.WebSocketServices.Broadcast(sw.ToString());
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the WebSocket server on the specified port and configures it with the appropriate certificate.
|
||||
/// StartServerAndSetPort method
|
||||
/// </summary>
|
||||
/// <remarks>This method initializes the WebSocket server and binds it to the specified port. It
|
||||
/// also applies the server's certificate for secure communication. Ensure that the port is not already in use
|
||||
/// and that the certificate file is accessible.</remarks>
|
||||
/// <param name="port">The port number on which the WebSocket server will listen. Must be a valid, non-negative port number.</param>
|
||||
public void StartServerAndSetPort(int port)
|
||||
{
|
||||
Debug.Console(0, "Starting Websocket Server on port: {0}", port);
|
||||
@@ -157,19 +142,21 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
||||
{
|
||||
_httpsServer = new HttpServer(port, true);
|
||||
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(certPath))
|
||||
{
|
||||
Debug.Console(0, "Assigning SSL Configuration");
|
||||
|
||||
_httpsServer.SslConfiguration.ServerCertificate = new X509Certificate2(certPath, certPassword);
|
||||
_httpsServer.SslConfiguration.ClientCertificateRequired = false;
|
||||
_httpsServer.SslConfiguration.CheckCertificateRevocation = false;
|
||||
_httpsServer.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls12;
|
||||
_httpsServer.SslConfiguration = new ServerSslConfiguration(new X509Certificate2(certPath, certPassword))
|
||||
{
|
||||
ClientCertificateRequired = false,
|
||||
CheckCertificateRevocation = false,
|
||||
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls,
|
||||
//this is just to test, you might want to actually validate
|
||||
_httpsServer.SslConfiguration.ClientCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
|
||||
ClientCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
|
||||
{
|
||||
Debug.Console(0, "HTTPS ClientCerticateValidation Callback triggered");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
Debug.Console(0, "Adding Debug Client Service");
|
||||
@@ -219,10 +206,8 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the WebSocket server if it is currently running.
|
||||
/// StopServer method
|
||||
/// </summary>
|
||||
/// <remarks>This method halts the WebSocket server and releases any associated resources. After
|
||||
/// calling this method, the server will no longer accept or process incoming connections.</remarks>
|
||||
public void StopServer()
|
||||
{
|
||||
Debug.Console(0, "Stopping Websocket Server");
|
||||
@@ -230,45 +215,28 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
||||
|
||||
_httpsServer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the logger to write log events to a debug WebSocket sink.
|
||||
/// </summary>
|
||||
/// <remarks>This extension method allows you to direct log events to a WebSocket sink for debugging
|
||||
/// purposes.</remarks>
|
||||
public static class DebugWebsocketSinkExtensions
|
||||
{
|
||||
public static class DebugWebsocketSinkExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures a logger to write log events to a debug WebSocket sink.
|
||||
/// DebugWebsocketSink method
|
||||
/// </summary>
|
||||
/// <remarks>This method adds a sink that writes log events to a WebSocket for debugging purposes.
|
||||
/// It is typically used during development to stream log events in real-time.</remarks>
|
||||
/// <param name="loggerConfiguration">The logger sink configuration to apply the WebSocket sink to.</param>
|
||||
/// <param name="formatProvider">An optional text formatter to format the log events. If not provided, a default formatter will be used.</param>
|
||||
/// <returns>A <see cref="LoggerConfiguration"/> object that can be used to further configure the logger.</returns>
|
||||
public static LoggerConfiguration DebugWebsocketSink(
|
||||
this LoggerSinkConfiguration loggerConfiguration,
|
||||
ITextFormatter formatProvider = null)
|
||||
{
|
||||
return loggerConfiguration.Sink(new DebugWebsocketSink(formatProvider));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a WebSocket client for debugging purposes, providing connection lifecycle management and message
|
||||
/// handling functionality.
|
||||
/// </summary>
|
||||
/// <remarks>The <see cref="DebugClient"/> class extends <see cref="WebSocketBehavior"/> to handle
|
||||
/// WebSocket connections, including events for opening, closing, receiving messages, and errors. It tracks the
|
||||
/// duration of the connection and logs relevant events for debugging.</remarks>
|
||||
public class DebugClient : WebSocketBehavior
|
||||
{
|
||||
private DateTime _connectionTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration of time the WebSocket connection has been active.
|
||||
/// Represents a DebugClient
|
||||
/// </summary>
|
||||
public class DebugClient : WebSocketBehavior
|
||||
{
|
||||
private DateTime _connectionTime;
|
||||
|
||||
public TimeSpan ConnectedDuration
|
||||
{
|
||||
get
|
||||
@@ -284,17 +252,11 @@ public class DebugClient : WebSocketBehavior
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DebugClient"/> class.
|
||||
/// </summary>
|
||||
/// <remarks>This constructor creates a new <see cref="DebugClient"/> instance and logs its
|
||||
/// creation using the <see cref="Debug.Console(int, string)"/> method with a debug level of 0.</remarks>
|
||||
public DebugClient()
|
||||
{
|
||||
Debug.Console(0, "DebugClient Created");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnOpen()
|
||||
{
|
||||
base.OnOpen();
|
||||
@@ -305,7 +267,6 @@ public class DebugClient : WebSocketBehavior
|
||||
_connectionTime = DateTime.Now;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnMessage(MessageEventArgs e)
|
||||
{
|
||||
base.OnMessage(e);
|
||||
@@ -313,7 +274,6 @@ public class DebugClient : WebSocketBehavior
|
||||
Debug.Console(0, "WebSocket UiClient Message: {0}", e.Data);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnClose(CloseEventArgs e)
|
||||
{
|
||||
base.OnClose(e);
|
||||
@@ -322,11 +282,11 @@ public class DebugClient : WebSocketBehavior
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnError(WebSocketSharp.ErrorEventArgs e)
|
||||
{
|
||||
base.OnError(e);
|
||||
|
||||
Debug.Console(2, Debug.ErrorLogLevel.Notice, "WebSocket UiClient Error: {0} message: {1}", e.Exception, e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Not in use
|
||||
/// </summary>
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Not in use
|
||||
/// </summary>
|
||||
public static class NetworkComm
|
||||
{
|
||||
/// <summary>
|
||||
@@ -18,3 +18,5 @@ namespace PepperDash.Core;
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Core.PasswordManagement;
|
||||
|
||||
namespace PepperDash.Core.PasswordManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// JSON password configuration
|
||||
/// </summary>
|
||||
@@ -23,3 +23,4 @@ namespace PepperDash.Core.PasswordManagement;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Core.PasswordManagement;
|
||||
|
||||
namespace PepperDash.Core.PasswordManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// Constants
|
||||
/// </summary>
|
||||
@@ -54,3 +54,4 @@ namespace PepperDash.Core.PasswordManagement;
|
||||
/// </summary>
|
||||
public const ushort StringValueChange = 201;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace PepperDash.Core.PasswordManagement;
|
||||
|
||||
/// <summary>
|
||||
/// A class to allow user interaction with the PasswordManager
|
||||
/// </summary>
|
||||
namespace PepperDash.Core.PasswordManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a PasswordClient
|
||||
/// </summary>
|
||||
public class PasswordClient
|
||||
{
|
||||
/// <summary>
|
||||
@@ -59,6 +59,9 @@ namespace PepperDash.Core.PasswordManagement;
|
||||
/// Retrieve password by index
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <summary>
|
||||
/// GetPasswordByIndex method
|
||||
/// </summary>
|
||||
public void GetPasswordByIndex(ushort key)
|
||||
{
|
||||
OnUshrtChange((ushort)PasswordManager.Passwords.Count, 0, PasswordManagementConstants.PasswordManagerCountChange);
|
||||
@@ -81,6 +84,9 @@ namespace PepperDash.Core.PasswordManagement;
|
||||
/// Password validation method
|
||||
/// </summary>
|
||||
/// <param name="password"></param>
|
||||
/// <summary>
|
||||
/// ValidatePassword method
|
||||
/// </summary>
|
||||
public void ValidatePassword(string password)
|
||||
{
|
||||
if (string.IsNullOrEmpty(password))
|
||||
@@ -99,6 +105,9 @@ namespace PepperDash.Core.PasswordManagement;
|
||||
/// password against the selected password when the length of the 2 are equal
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <summary>
|
||||
/// BuildPassword method
|
||||
/// </summary>
|
||||
public void BuildPassword(string data)
|
||||
{
|
||||
PasswordToValidate = String.Concat(PasswordToValidate, data);
|
||||
@@ -109,7 +118,7 @@ namespace PepperDash.Core.PasswordManagement;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the user entered password and resets the LEDs
|
||||
/// ClearPassword method
|
||||
/// </summary>
|
||||
public void ClearPassword()
|
||||
{
|
||||
@@ -184,3 +193,4 @@ namespace PepperDash.Core.PasswordManagement;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,11 @@
|
||||
using System.Collections.Generic;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Core.PasswordManagement;
|
||||
|
||||
/// <summary>
|
||||
/// Allows passwords to be stored and managed
|
||||
/// </summary>
|
||||
namespace PepperDash.Core.PasswordManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a PasswordManager
|
||||
/// </summary>
|
||||
public class PasswordManager
|
||||
{
|
||||
/// <summary>
|
||||
@@ -71,6 +71,9 @@ namespace PepperDash.Core.PasswordManagement;
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="password"></param>
|
||||
/// <summary>
|
||||
/// UpdatePassword method
|
||||
/// </summary>
|
||||
public void UpdatePassword(ushort key, string password)
|
||||
{
|
||||
// validate the parameters
|
||||
@@ -152,6 +155,9 @@ namespace PepperDash.Core.PasswordManagement;
|
||||
/// Method to change the default timer value, (default 5000ms/5s)
|
||||
/// </summary>
|
||||
/// <param name="time"></param>
|
||||
/// <summary>
|
||||
/// PasswordTimerMs method
|
||||
/// </summary>
|
||||
public void PasswordTimerMs(ushort time)
|
||||
{
|
||||
PasswordTimerElapsedMs = Convert.ToInt64(time);
|
||||
@@ -238,3 +244,4 @@ namespace PepperDash.Core.PasswordManagement;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ProjectType>Library</ProjectType>
|
||||
<Configurations>Debug;Release;Test</Configurations>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RootNamespace>PepperDash.Core</RootNamespace>
|
||||
<AssemblyName>PepperDashCore</AssemblyName>
|
||||
<TargetFramework>net8</TargetFramework>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<Deterministic>true</Deterministic>
|
||||
<NeutralLanguage>en</NeutralLanguage>
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
@@ -25,10 +24,6 @@
|
||||
<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>
|
||||
@@ -46,22 +41,16 @@
|
||||
<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="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Serilog" Version="4.3.0" />
|
||||
<PackageReference Include="Serilog.Expressions" Version="5.0.0" />
|
||||
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="SSH.NET" Version="2025.0.0" />
|
||||
<PackageReference Include="WebSocketSharp-netstandard" Version="1.0.1" />
|
||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
|
||||
<PackageReference Include="Crestron.SimplSharp.SDK.Library" Version="2.21.90" />
|
||||
<PackageReference Include="Serilog" Version="3.1.1" />
|
||||
<PackageReference Include="Serilog.Expressions" Version="4.0.0" />
|
||||
<PackageReference Include="Serilog.Formatting.Compact" Version="2.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="SSH.NET" Version="2024.2.0" />
|
||||
<PackageReference Include="WebSocketSharp" Version="1.0.3-rc11" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net6'">
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
|
||||
@@ -4,8 +4,8 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Core.SystemInfo;
|
||||
|
||||
namespace PepperDash.Core.SystemInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Constants
|
||||
/// </summary>
|
||||
@@ -69,7 +69,7 @@ namespace PepperDash.Core.SystemInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processor Change Event Args Class
|
||||
/// Represents a ProcessorChangeEventArgs
|
||||
/// </summary>
|
||||
public class ProcessorChangeEventArgs : EventArgs
|
||||
{
|
||||
@@ -115,7 +115,7 @@ namespace PepperDash.Core.SystemInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ethernet Change Event Args Class
|
||||
/// Represents a EthernetChangeEventArgs
|
||||
/// </summary>
|
||||
public class EthernetChangeEventArgs : EventArgs
|
||||
{
|
||||
@@ -166,7 +166,7 @@ namespace PepperDash.Core.SystemInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Control Subnet Chage Event Args Class
|
||||
/// Represents a ControlSubnetChangeEventArgs
|
||||
/// </summary>
|
||||
public class ControlSubnetChangeEventArgs : EventArgs
|
||||
{
|
||||
@@ -212,7 +212,7 @@ namespace PepperDash.Core.SystemInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Program Change Event Args Class
|
||||
/// Represents a ProgramChangeEventArgs
|
||||
/// </summary>
|
||||
public class ProgramChangeEventArgs : EventArgs
|
||||
{
|
||||
@@ -261,3 +261,4 @@ namespace PepperDash.Core.SystemInfo;
|
||||
Index = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Core.SystemInfo;
|
||||
|
||||
namespace PepperDash.Core.SystemInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Processor info class
|
||||
/// </summary>
|
||||
@@ -201,3 +201,4 @@ namespace PepperDash.Core.SystemInfo;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Core.SystemInfo;
|
||||
|
||||
namespace PepperDash.Core.SystemInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// System Info class
|
||||
/// </summary>
|
||||
@@ -101,7 +101,7 @@ namespace PepperDash.Core.SystemInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current ethernet info
|
||||
/// GetEthernetInfo method
|
||||
/// </summary>
|
||||
public void GetEthernetInfo()
|
||||
{
|
||||
@@ -162,7 +162,7 @@ namespace PepperDash.Core.SystemInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current control subnet info
|
||||
/// GetControlSubnetInfo method
|
||||
/// </summary>
|
||||
public void GetControlSubnetInfo()
|
||||
{
|
||||
@@ -206,6 +206,9 @@ namespace PepperDash.Core.SystemInfo;
|
||||
/// Gets the program info by index
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <summary>
|
||||
/// GetProgramInfoByIndex method
|
||||
/// </summary>
|
||||
public void GetProgramInfoByIndex(ushort index)
|
||||
{
|
||||
if (index < 1 || index > 10)
|
||||
@@ -264,7 +267,7 @@ namespace PepperDash.Core.SystemInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the processor uptime and passes it to S+
|
||||
/// RefreshProcessorUptime method
|
||||
/// </summary>
|
||||
public void RefreshProcessorUptime()
|
||||
{
|
||||
@@ -287,6 +290,9 @@ namespace PepperDash.Core.SystemInfo;
|
||||
/// Gets the program uptime, by index, and passes it to S+
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <summary>
|
||||
/// RefreshProgramUptimeByIndex method
|
||||
/// </summary>
|
||||
public void RefreshProgramUptimeByIndex(int index)
|
||||
{
|
||||
try
|
||||
@@ -308,6 +314,9 @@ namespace PepperDash.Core.SystemInfo;
|
||||
/// Sends command to console, passes response back using string change event
|
||||
/// </summary>
|
||||
/// <param name="cmd"></param>
|
||||
/// <summary>
|
||||
/// SendConsoleCommand method
|
||||
/// </summary>
|
||||
public void SendConsoleCommand(string cmd)
|
||||
{
|
||||
if (string.IsNullOrEmpty(cmd))
|
||||
@@ -459,3 +468,4 @@ namespace PepperDash.Core.SystemInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,13 +20,13 @@ using Org.BouncyCastle.Crypto.Operators;
|
||||
using BigInteger = Org.BouncyCastle.Math.BigInteger;
|
||||
using X509Certificate = Org.BouncyCastle.X509.X509Certificate;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Taken From https://github.com/rlipscombe/bouncy-castle-csharp/
|
||||
/// </summary>
|
||||
internal class BouncyCertificate
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Taken From https://github.com/rlipscombe/bouncy-castle-csharp/
|
||||
/// </summary>
|
||||
internal class BouncyCertificate
|
||||
{
|
||||
public string CertificatePassword { get; set; } = "password";
|
||||
public X509Certificate2 LoadCertificate(string issuerFileName, string password)
|
||||
{
|
||||
@@ -35,6 +35,9 @@ internal class BouncyCertificate
|
||||
return issuerCertificate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IssueCertificate method
|
||||
/// </summary>
|
||||
public X509Certificate2 IssueCertificate(string subjectName, X509Certificate2 issuerCertificate, string[] subjectAlternativeNames, KeyPurposeID[] usages)
|
||||
{
|
||||
// It's self-signed, so these are the same.
|
||||
@@ -56,6 +59,9 @@ internal class BouncyCertificate
|
||||
return ConvertCertificate(certificate, subjectKeyPair, random);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CreateCertificateAuthorityCertificate method
|
||||
/// </summary>
|
||||
public X509Certificate2 CreateCertificateAuthorityCertificate(string subjectName, string[] subjectAlternativeNames, KeyPurposeID[] usages)
|
||||
{
|
||||
// It's self-signed, so these are the same.
|
||||
@@ -78,6 +84,9 @@ internal class BouncyCertificate
|
||||
return ConvertCertificate(certificate, subjectKeyPair, random);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CreateSelfSignedCertificate method
|
||||
/// </summary>
|
||||
public X509Certificate2 CreateSelfSignedCertificate(string subjectName, string[] subjectAlternativeNames, KeyPurposeID[] usages)
|
||||
{
|
||||
// It's self-signed, so these are the same.
|
||||
@@ -305,6 +314,9 @@ internal class BouncyCertificate
|
||||
return convertedCertificate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WriteCertificate method
|
||||
/// </summary>
|
||||
public void WriteCertificate(X509Certificate2 certificate, string outputDirectory, string certName)
|
||||
{
|
||||
// This password is the one attached to the PFX file. Use 'null' for no password.
|
||||
@@ -332,6 +344,9 @@ internal class BouncyCertificate
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// AddCertToStore method
|
||||
/// </summary>
|
||||
public bool AddCertToStore(X509Certificate2 cert, System.Security.Cryptography.X509Certificates.StoreName st, System.Security.Cryptography.X509Certificates.StoreLocation sl)
|
||||
{
|
||||
bool bRet = false;
|
||||
@@ -352,4 +367,5 @@ internal class BouncyCertificate
|
||||
|
||||
return bRet;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,17 @@
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
|
||||
namespace PepperDash.Core.Web.RequestHandlers;
|
||||
|
||||
/// <summary>
|
||||
/// Web API default request handler
|
||||
/// </summary>
|
||||
public class DefaultRequestHandler : WebApiBaseRequestHandler
|
||||
namespace PepperDash.Core.Web.RequestHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a DefaultRequestHandler
|
||||
/// </summary>
|
||||
public class DefaultRequestHandler : WebApiBaseRequestHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public DefaultRequestHandler()
|
||||
: base(true)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
using System;
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
|
||||
namespace PepperDash.Core.Web.RequestHandlers;
|
||||
|
||||
public abstract class WebApiBaseRequestAsyncHandler : IHttpCwsHandler
|
||||
namespace PepperDash.Core.Web.RequestHandlers
|
||||
{
|
||||
public abstract class WebApiBaseRequestAsyncHandler:IHttpCwsHandler
|
||||
{
|
||||
private readonly Dictionary<string, Func<HttpCwsContext, Task>> _handlers;
|
||||
protected readonly bool EnableCors;
|
||||
|
||||
@@ -142,6 +142,9 @@ public abstract class WebApiBaseRequestAsyncHandler : IHttpCwsHandler
|
||||
/// Process request
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <summary>
|
||||
/// ProcessRequest method
|
||||
/// </summary>
|
||||
public void ProcessRequest(HttpCwsContext context)
|
||||
{
|
||||
if (!_handlers.TryGetValue(context.Request.HttpMethod, out Func<HttpCwsContext, Task> handler))
|
||||
@@ -159,4 +162,5 @@ public abstract class WebApiBaseRequestAsyncHandler : IHttpCwsHandler
|
||||
|
||||
handlerTask.GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
using System.Collections.Generic;
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
|
||||
namespace PepperDash.Core.Web.RequestHandlers;
|
||||
|
||||
/// <summary>
|
||||
/// CWS Base Handler, implements IHttpCwsHandler
|
||||
/// </summary>
|
||||
public abstract class WebApiBaseRequestHandler : IHttpCwsHandler
|
||||
namespace PepperDash.Core.Web.RequestHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// CWS Base Handler, implements IHttpCwsHandler
|
||||
/// </summary>
|
||||
public abstract class WebApiBaseRequestHandler : IHttpCwsHandler
|
||||
{
|
||||
private readonly Dictionary<string, Action<HttpCwsContext>> _handlers;
|
||||
protected readonly bool EnableCors;
|
||||
|
||||
@@ -144,6 +144,9 @@ public abstract class WebApiBaseRequestHandler : IHttpCwsHandler
|
||||
/// Process request
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <summary>
|
||||
/// ProcessRequest method
|
||||
/// </summary>
|
||||
public void ProcessRequest(HttpCwsContext context)
|
||||
{
|
||||
Action<HttpCwsContext> handler;
|
||||
@@ -161,4 +164,5 @@ public abstract class WebApiBaseRequestHandler : IHttpCwsHandler
|
||||
|
||||
handler(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,13 @@ using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core.Web.RequestHandlers;
|
||||
|
||||
namespace PepperDash.Core.Web;
|
||||
|
||||
/// <summary>
|
||||
/// Web API server
|
||||
/// </summary>
|
||||
public class WebApiServer : IKeyName
|
||||
namespace PepperDash.Core.Web
|
||||
{
|
||||
/// <summary>
|
||||
/// Web API server
|
||||
/// </summary>
|
||||
public class WebApiServer : IKeyName
|
||||
{
|
||||
private const string SplusKey = "Uninitialized Web API Server";
|
||||
private const string DefaultName = "Web API Server";
|
||||
private const string DefaultBasePath = "/api";
|
||||
@@ -26,22 +26,22 @@ public class WebApiServer : IKeyName
|
||||
private HttpCwsServer _server;
|
||||
|
||||
/// <summary>
|
||||
/// Web API server key
|
||||
/// Gets or sets the Key
|
||||
/// </summary>
|
||||
public string Key { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Web API server name
|
||||
/// Gets or sets the Name
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// CWS base path, will default to "/api" if not set via initialize method
|
||||
/// Gets or sets the BasePath
|
||||
/// </summary>
|
||||
public string BasePath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates CWS is registered with base path
|
||||
/// Gets or sets the IsRegistered
|
||||
/// </summary>
|
||||
public bool IsRegistered { get; private set; }
|
||||
|
||||
@@ -138,7 +138,7 @@ public class WebApiServer : IKeyName
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes CWS class
|
||||
/// Initialize method
|
||||
/// </summary>
|
||||
public void Initialize(string key, string basePath)
|
||||
{
|
||||
@@ -165,6 +165,9 @@ public class WebApiServer : IKeyName
|
||||
/// Removes a route from CWS
|
||||
/// </summary>
|
||||
/// <param name="route"></param>
|
||||
/// <summary>
|
||||
/// RemoveRoute method
|
||||
/// </summary>
|
||||
public void RemoveRoute(HttpCwsRoute route)
|
||||
{
|
||||
if (route == null)
|
||||
@@ -177,7 +180,7 @@ public class WebApiServer : IKeyName
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of the current routes
|
||||
/// GetRouteCollection method
|
||||
/// </summary>
|
||||
public HttpCwsRouteCollection GetRouteCollection()
|
||||
{
|
||||
@@ -223,7 +226,7 @@ public class WebApiServer : IKeyName
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop CWS instance
|
||||
/// Stop method
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
@@ -280,4 +283,5 @@ public class WebApiServer : IKeyName
|
||||
Debug.Console(DebugVerbose, this, "ReceivedRequestEventHandler Exception InnerException: {0}", ex.InnerException);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace PepperDash.Core.WebApi.Presets;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a preset
|
||||
/// </summary>
|
||||
namespace PepperDash.Core.WebApi.Presets
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Preset
|
||||
/// </summary>
|
||||
public class Preset
|
||||
{
|
||||
/// <summary>
|
||||
@@ -13,27 +13,27 @@ namespace PepperDash.Core.WebApi.Presets;
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User ID
|
||||
/// Gets or sets the UserId
|
||||
/// </summary>
|
||||
public int UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Room Type ID
|
||||
/// Gets or sets the RoomTypeId
|
||||
/// </summary>
|
||||
public int RoomTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Preset Name
|
||||
/// Gets or sets the PresetName
|
||||
/// </summary>
|
||||
public string PresetName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Preset Number
|
||||
/// Gets or sets the PresetNumber
|
||||
/// </summary>
|
||||
public int PresetNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Preset Data
|
||||
/// Gets or sets the Data
|
||||
/// </summary>
|
||||
public string Data { get; set; }
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace PepperDash.Core.WebApi.Presets;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Represents a PresetReceivedEventArgs
|
||||
/// </summary>
|
||||
public class PresetReceivedEventArgs : EventArgs
|
||||
{
|
||||
@@ -59,12 +59,12 @@ namespace PepperDash.Core.WebApi.Presets;
|
||||
public bool LookupSuccess { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// S+ helper
|
||||
/// Gets or sets the ULookupSuccess
|
||||
/// </summary>
|
||||
public ushort ULookupSuccess { get { return (ushort)(LookupSuccess ? 1 : 0); } }
|
||||
|
||||
/// <summary>
|
||||
/// The preset
|
||||
/// Gets or sets the Preset
|
||||
/// </summary>
|
||||
public Preset Preset { get; private set; }
|
||||
|
||||
@@ -84,3 +84,4 @@ namespace PepperDash.Core.WebApi.Presets;
|
||||
Preset = preset;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Core.WebApi.Presets;
|
||||
|
||||
namespace PepperDash.Core.WebApi.Presets
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@@ -17,17 +17,17 @@ namespace PepperDash.Core.WebApi.Presets;
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the ExternalId
|
||||
/// </summary>
|
||||
public string ExternalId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the FirstName
|
||||
/// </summary>
|
||||
public string FirstName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the LastName
|
||||
/// </summary>
|
||||
public string LastName { get; set; }
|
||||
}
|
||||
@@ -44,12 +44,12 @@ namespace PepperDash.Core.WebApi.Presets;
|
||||
public bool LookupSuccess { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// For stupid S+
|
||||
/// Gets or sets the ULookupSuccess
|
||||
/// </summary>
|
||||
public ushort ULookupSuccess { get { return (ushort)(LookupSuccess ? 1 : 0); } }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the User
|
||||
/// </summary>
|
||||
public User User { get; private set; }
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace PepperDash.Core.WebApi.Presets;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Represents a UserAndRoomMessage
|
||||
/// </summary>
|
||||
public class UserAndRoomMessage
|
||||
{
|
||||
@@ -81,12 +81,13 @@ namespace PepperDash.Core.WebApi.Presets;
|
||||
public int UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the RoomTypeId
|
||||
/// </summary>
|
||||
public int RoomTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets or sets the PresetNumber
|
||||
/// </summary>
|
||||
public int PresetNumber { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Crestron.SimplSharp; // For Basic SIMPL# Classes
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Crestron.SimplSharp.Net.Http;
|
||||
using Crestron.SimplSharp.Net.Https;
|
||||
using Newtonsoft.Json;
|
||||
@@ -8,13 +8,13 @@ using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core.JsonToSimpl;
|
||||
|
||||
|
||||
namespace PepperDash.Core.WebApi.Presets;
|
||||
|
||||
/// <summary>
|
||||
/// Passcode client for the WebApi
|
||||
/// </summary>
|
||||
public class WebApiPasscodeClient : IKeyed
|
||||
namespace PepperDash.Core.WebApi.Presets
|
||||
{
|
||||
/// <summary>
|
||||
/// Passcode client for the WebApi
|
||||
/// </summary>
|
||||
public class WebApiPasscodeClient : IKeyed
|
||||
{
|
||||
/// <summary>
|
||||
/// Notifies when user received
|
||||
/// </summary>
|
||||
@@ -26,7 +26,7 @@ public class WebApiPasscodeClient : IKeyed
|
||||
public event EventHandler<PresetReceivedEventArgs> PresetReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Unique identifier for this instance
|
||||
/// Gets or sets the Key
|
||||
/// </summary>
|
||||
public string Key { get; private set; }
|
||||
|
||||
@@ -77,6 +77,9 @@ public class WebApiPasscodeClient : IKeyed
|
||||
/// Gets the user for a passcode
|
||||
/// </summary>
|
||||
/// <param name="passcode"></param>
|
||||
/// <summary>
|
||||
/// GetUserForPasscode method
|
||||
/// </summary>
|
||||
public void GetUserForPasscode(string passcode)
|
||||
{
|
||||
// Bullshit duplicate code here... These two cases should be the same
|
||||
@@ -115,6 +118,9 @@ public class WebApiPasscodeClient : IKeyed
|
||||
/// </summary>
|
||||
/// <param name="roomTypeId"></param>
|
||||
/// <param name="presetNumber"></param>
|
||||
/// <summary>
|
||||
/// GetPresetForThisUser method
|
||||
/// </summary>
|
||||
public void GetPresetForThisUser(int roomTypeId, int presetNumber)
|
||||
{
|
||||
if (CurrentUser == null)
|
||||
@@ -212,6 +218,9 @@ public class WebApiPasscodeClient : IKeyed
|
||||
/// </summary>
|
||||
/// <param name="roomTypeId"></param>
|
||||
/// <param name="presetNumber"></param>
|
||||
/// <summary>
|
||||
/// SavePresetForThisUser method
|
||||
/// </summary>
|
||||
public void SavePresetForThisUser(int roomTypeId, int presetNumber)
|
||||
{
|
||||
if (CurrentPreset == null)
|
||||
@@ -269,4 +278,5 @@ public class WebApiPasscodeClient : IKeyed
|
||||
CrestronConsole.PrintLine("Preset save exception {0}", e.Response.Code);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using PepperDash.Core.Intersystem.Tokens;
|
||||
|
||||
namespace PepperDash.Core.Intersystem.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Interface to determine XSig serialization for an object.
|
||||
/// </summary>
|
||||
public interface IXSigSerialization
|
||||
namespace PepperDash.Core.Intersystem.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface to determine XSig serialization for an object.
|
||||
/// </summary>
|
||||
public interface IXSigSerialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Serialize the sig data
|
||||
/// </summary>
|
||||
@@ -21,4 +21,5 @@ public interface IXSigSerialization
|
||||
/// <param name="tokens"></param>
|
||||
/// <returns></returns>
|
||||
T Deserialize<T>(IEnumerable<XSigToken> tokens) where T : class, IXSigSerialization;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace PepperDash.Core.Intersystem.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Class to handle this specific exception type
|
||||
/// </summary>
|
||||
public class XSigSerializationException : Exception
|
||||
namespace PepperDash.Core.Intersystem.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to handle this specific exception type
|
||||
/// </summary>
|
||||
public class XSigSerializationException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// default constructor
|
||||
/// </summary>
|
||||
@@ -24,4 +24,5 @@ public class XSigSerializationException : Exception
|
||||
/// <param name="message"></param>
|
||||
/// <param name="inner"></param>
|
||||
public XSigSerializationException(string message, Exception inner) : base(message, inner) { }
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user