mirror of
https://github.com/PepperDash/PepperDashCore.git
synced 2026-08-31 19:08:26 +00:00
commit
ab61b2673a
105 changed files with 12503 additions and 13387 deletions
BIN
._Readme.md
BIN
._Readme.md
Binary file not shown.
45
.github/scripts/GenerateVersionNumber.ps1
vendored
45
.github/scripts/GenerateVersionNumber.ps1
vendored
|
|
@ -1,45 +0,0 @@
|
||||||
$latestVersions = $(git tag --merged origin/main)
|
|
||||||
$latestVersion = [version]"0.0.0"
|
|
||||||
Foreach ($version in $latestVersions) {
|
|
||||||
Write-Host $version
|
|
||||||
try {
|
|
||||||
if (([version]$version) -ge $latestVersion) {
|
|
||||||
$latestVersion = $version
|
|
||||||
Write-Host "Setting latest version to: $latestVersion"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Host "Unable to convert $($version). Skipping"
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$newVersion = [version]$latestVersion
|
|
||||||
$phase = ""
|
|
||||||
$newVersionString = ""
|
|
||||||
switch -regex ($Env:GITHUB_REF) {
|
|
||||||
'^refs\/heads\/main*.' {
|
|
||||||
$newVersionString = "{0}.{1}.{2}" -f $newVersion.Major, $newVersion.Minor, $newVersion.Build
|
|
||||||
}
|
|
||||||
'^refs\/heads\/feature\/*.' {
|
|
||||||
$phase = 'alpha'
|
|
||||||
$newVersionString = "{0}.{1}.{2}-{3}-{4}" -f $newVersion.Major, $newVersion.Minor, ($newVersion.Build + 1), $phase, $Env:GITHUB_RUN_NUMBER
|
|
||||||
}
|
|
||||||
'^refs\/heads\/release\/*.' {
|
|
||||||
$splitRef = $Env:GITHUB_REF -split "/"
|
|
||||||
$version = [version]($splitRef[-1] -replace "v", "")
|
|
||||||
$phase = 'rc'
|
|
||||||
$newVersionString = "{0}.{1}.{2}-{3}-{4}" -f $version.Major, $version.Minor, $version.Build, $phase, $Env:GITHUB_RUN_NUMBER
|
|
||||||
}
|
|
||||||
'^refs\/heads\/development*.' {
|
|
||||||
$phase = 'beta'
|
|
||||||
$newVersionString = "{0}.{1}.{2}-{3}-{4}" -f $newVersion.Major, $newVersion.Minor, ($newVersion.Build + 1), $phase, $Env:GITHUB_RUN_NUMBER
|
|
||||||
}
|
|
||||||
'^refs\/heads\/hotfix\/*.' {
|
|
||||||
$phase = 'hotfix'
|
|
||||||
$newVersionString = "{0}.{1}.{2}-{3}-{4}" -f $newVersion.Major, $newVersion.Minor, ($newVersion.Build + 1), $phase, $Env:GITHUB_RUN_NUMBER
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Write-Output $newVersionString
|
|
||||||
40
.github/scripts/UpdateAssemblyVersion.ps1
vendored
40
.github/scripts/UpdateAssemblyVersion.ps1
vendored
|
|
@ -1,40 +0,0 @@
|
||||||
function Update-SourceVersion {
|
|
||||||
Param ([string]$Version)
|
|
||||||
#$fullVersion = $Version
|
|
||||||
$baseVersion = [regex]::Match($Version, "(\d+.\d+.\d+).*").captures.groups[1].value
|
|
||||||
$NewAssemblyVersion = ‘AssemblyVersion("‘ + $baseVersion + ‘.*")’
|
|
||||||
Write-Output "AssemblyVersion = $NewAssemblyVersion"
|
|
||||||
$NewAssemblyInformationalVersion = ‘AssemblyInformationalVersion("‘ + $Version + ‘")’
|
|
||||||
Write-Output "AssemblyInformationalVersion = $NewAssemblyInformationalVersion"
|
|
||||||
|
|
||||||
foreach ($o in $input) {
|
|
||||||
Write-output $o.FullName
|
|
||||||
$TmpFile = $o.FullName + “.tmp”
|
|
||||||
get-content $o.FullName |
|
|
||||||
ForEach-Object {
|
|
||||||
$_ -replace ‘AssemblyVersion\(".*"\)’, $NewAssemblyVersion } |
|
|
||||||
ForEach-Object {
|
|
||||||
$_ -replace ‘AssemblyInformationalVersion\(".*"\)’, $NewAssemblyInformationalVersion
|
|
||||||
} > $TmpFile
|
|
||||||
move-item $TmpFile $o.FullName -force
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Update-AllAssemblyInfoFiles ( $version ) {
|
|
||||||
foreach ($file in “AssemblyInfo.cs”, “AssemblyInfo.vb” ) {
|
|
||||||
get-childitem -Path $Env:GITHUB_WORKSPACE -recurse | Where-Object { $_.Name -eq $file } | Update-SourceVersion $version ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# validate arguments
|
|
||||||
$r = [System.Text.RegularExpressions.Regex]::Match($args[0], "\d+\.\d+\.\d+.*");
|
|
||||||
if ($r.Success) {
|
|
||||||
Write-Output "Updating Assembly Version to $args ...";
|
|
||||||
Update-AllAssemblyInfoFiles $args[0];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Output ” “;
|
|
||||||
Write-Output “Error: Input version does not match x.y.z format!”
|
|
||||||
Write-Output ” “;
|
|
||||||
Write-Output "Unable to apply version to AssemblyInfo.cs files";
|
|
||||||
}
|
|
||||||
42
.github/scripts/ZipBuildOutput.ps1
vendored
42
.github/scripts/ZipBuildOutput.ps1
vendored
|
|
@ -1,42 +0,0 @@
|
||||||
# Uncomment these for local testing
|
|
||||||
# $Env:GITHUB_WORKSPACE = "C:\Working Directories\PD\essentials"
|
|
||||||
# $Env:SOLUTION_FILE = "PepperDashEssentials"
|
|
||||||
# $Env:VERSION = "0.0.0-buildType-test"
|
|
||||||
|
|
||||||
# Sets the root directory for the operation
|
|
||||||
$destination = "$($Env:GITHUB_HOME)\output"
|
|
||||||
New-Item -ItemType Directory -Force -Path ($destination)
|
|
||||||
Get-ChildItem ($destination)
|
|
||||||
$exclusions = @(git submodule foreach --quiet 'echo $name')
|
|
||||||
$exclusions += "Newtonsoft.Json.Compact.dll"
|
|
||||||
# Trying to get any .json schema files (not currently working)
|
|
||||||
# Gets any files with the listed extensions.
|
|
||||||
Get-ChildItem -recurse -Path "$($Env:GITHUB_WORKSPACE)" -include "*.clz", "*.cpz", "*.cplz", "*.json", "*.nuspec" | ForEach-Object {
|
|
||||||
$allowed = $true;
|
|
||||||
# Exclude any files in submodules
|
|
||||||
foreach ($exclude in $exclusions) {
|
|
||||||
if ((Split-Path $_.FullName -Parent).contains("$($exclude)")) {
|
|
||||||
$allowed = $false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($allowed) {
|
|
||||||
Write-Host "allowing $($_)"
|
|
||||||
$_;
|
|
||||||
}
|
|
||||||
} | Copy-Item -Destination ($destination) -Force
|
|
||||||
Write-Host "Getting matching files..."
|
|
||||||
# Get any files from the output folder that match the following extensions
|
|
||||||
Get-ChildItem -Path $destination | Where-Object { (($_.Extension -eq ".clz") -or ($_.Extension -eq ".cpz") -or ($_.Extension -eq ".cplz")) } | ForEach-Object {
|
|
||||||
# Replace the extensions with dll or xml and create an array
|
|
||||||
$filenames = @($($_ -replace "cpz|clz|cplz", "dll"), $($_ -replace "cpz|clz|cplz", "xml"))
|
|
||||||
Write-Host "Filenames:"
|
|
||||||
Write-Host $filenames
|
|
||||||
if ($filenames.length -gt 0) {
|
|
||||||
# Attempt to get the files and return them to the output directory
|
|
||||||
Get-ChildItem -Recurse -Path "$($Env:GITHUB_WORKSPACE)" -include $filenames | Copy-Item -Destination ($destination) -Force
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Compress-Archive -Path $destination -DestinationPath "$($Env:GITHUB_WORKSPACE)\$($Env:SOLUTION_FILE)-$($Env:VERSION).zip" -Force
|
|
||||||
Write-Host "Output Contents post Zip"
|
|
||||||
Get-ChildItem -Path $destination
|
|
||||||
21
.github/workflows/EssentialsPlugins-builds-4-series-caller.yml
vendored
Normal file
21
.github/workflows/EssentialsPlugins-builds-4-series-caller.yml
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
name: Build PepperDash Core
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
getVersion:
|
||||||
|
uses: PepperDash/workflow-templates/.github/workflows/essentialsplugins-getversion.yml@main
|
||||||
|
secrets: inherit
|
||||||
|
build-4Series:
|
||||||
|
uses: PepperDash/workflow-templates/.github/workflows/essentialsplugins-4Series-builds.yml@main
|
||||||
|
secrets: inherit
|
||||||
|
needs: getVersion
|
||||||
|
if: needs.getVersion.outputs.newVersion == 'true'
|
||||||
|
with:
|
||||||
|
newVersion: ${{ needs.getVersion.outputs.newVersion }}
|
||||||
|
version: ${{ needs.getVersion.outputs.version }}
|
||||||
|
tag: ${{ needs.getVersion.outputs.tag }}
|
||||||
|
channel: ${{ needs.getVersion.outputs.channel }}
|
||||||
154
.github/workflows/docker.yml
vendored
154
.github/workflows/docker.yml
vendored
|
|
@ -1,154 +0,0 @@
|
||||||
name: Branch Build Using Docker
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- feature/*
|
|
||||||
- hotfix/*
|
|
||||||
- release/*
|
|
||||||
- development
|
|
||||||
|
|
||||||
env:
|
|
||||||
# solution path doesn't need slashes unless there it is multiple folders deep
|
|
||||||
# solution name does not include extension. .sln is assumed
|
|
||||||
SOLUTION_PATH: Pepperdash Core
|
|
||||||
SOLUTION_FILE: PepperDash Core
|
|
||||||
# Do not edit this, we're just creating it here
|
|
||||||
VERSION: 0.0.0-buildtype-buildnumber
|
|
||||||
# Defaults to debug for build type
|
|
||||||
BUILD_TYPE: Debug
|
|
||||||
# Defaults to main as the release branch. Change as necessary
|
|
||||||
RELEASE_BRANCH: main
|
|
||||||
jobs:
|
|
||||||
Build_Project:
|
|
||||||
runs-on: windows-2019
|
|
||||||
steps:
|
|
||||||
# First we checkout the source repo
|
|
||||||
- name: Checkout repo
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
# And any submodules
|
|
||||||
- name: Checkout submodules
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
git config --global url."https://github.com/".insteadOf "git@github.com:"
|
|
||||||
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
|
|
||||||
git submodule sync --recursive
|
|
||||||
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
|
|
||||||
# Fetch all tags
|
|
||||||
- name: Fetch tags
|
|
||||||
run: git fetch --tags
|
|
||||||
# Generate the appropriate version number
|
|
||||||
- name: Set Version Number
|
|
||||||
shell: powershell
|
|
||||||
run: |
|
|
||||||
$version = ./.github/scripts/GenerateVersionNumber.ps1
|
|
||||||
echo "VERSION=$version" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
|
||||||
# Use the version number to set the version of the assemblies
|
|
||||||
- name: Update AssemblyInfo.cs
|
|
||||||
shell: powershell
|
|
||||||
run: |
|
|
||||||
Write-Output ${{ env.VERSION }}
|
|
||||||
./.github/scripts/UpdateAssemblyVersion.ps1 ${{ env.VERSION }}
|
|
||||||
# Login to Docker
|
|
||||||
- name: Login to Docker
|
|
||||||
uses: azure/docker-login@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.dockerhub_user }}
|
|
||||||
password: ${{ secrets.dockerhub_password }}
|
|
||||||
# Build the solutions in the docker image
|
|
||||||
- name: Build Solution
|
|
||||||
shell: powershell
|
|
||||||
run: |
|
|
||||||
Invoke-Expression "docker run --rm --mount type=bind,source=""$($Env:GITHUB_WORKSPACE)"",target=""c:/project"" pepperdash/sspbuilder c:\cihelpers\vsidebuild.exe -Solution ""c:\project\$($Env:SOLUTION_PATH)\$($Env:SOLUTION_FILE).sln"" -BuildSolutionConfiguration $($ENV:BUILD_TYPE)"
|
|
||||||
- name: Zip Build Output
|
|
||||||
shell: powershell
|
|
||||||
run: ./.github/scripts/ZipBuildOutput.ps1
|
|
||||||
# Write the version to a file to be consumed by the push jobs
|
|
||||||
- name: Write Version
|
|
||||||
run: Write-Output "$($Env:VERSION)" | Out-File -FilePath "$($Env:GITHUB_HOME)\output\version.txt"
|
|
||||||
# Upload the build output as an artifact
|
|
||||||
- name: Upload Build Output
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: Build
|
|
||||||
path: ./${{ env.SOLUTION_FILE}}-${{ env.VERSION}}.zip
|
|
||||||
# Upload the Version file as an artifact
|
|
||||||
- name: Upload version.txt
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: Version
|
|
||||||
path: ${{env.GITHUB_HOME}}\output\version.txt
|
|
||||||
# Create the release on the source repo
|
|
||||||
- name: Create tag for non-rc builds
|
|
||||||
if: contains(env.VERSION, 'alpha') || contains(env.VERSION, 'beta')
|
|
||||||
run: |
|
|
||||||
git tag $($Env:VERSION)
|
|
||||||
git push --tags origin
|
|
||||||
- name: Create Release
|
|
||||||
id: create_release
|
|
||||||
# using contributor's version to allow for pointing at the right commit
|
|
||||||
if: contains(env.VERSION,'-rc-') || contains(env.VERSION,'-hotfix-')
|
|
||||||
uses: fleskesvor/create-release@feature/support-target-commitish
|
|
||||||
with:
|
|
||||||
tag_name: ${{ env.VERSION }}
|
|
||||||
release_name: ${{ env.VERSION }}
|
|
||||||
prerelease: ${{contains('debug', env.BUILD_TYPE)}}
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
# Upload the build package to the release
|
|
||||||
- name: Upload Release Package
|
|
||||||
if: contains(env.VERSION,'-rc-') || contains(env.VERSION,'-hotfix-')
|
|
||||||
id: upload_release
|
|
||||||
uses: actions/upload-release-asset@v1
|
|
||||||
with:
|
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
|
||||||
asset_path: ./${{ env.SOLUTION_FILE}}-${{ env.VERSION}}.zip
|
|
||||||
asset_name: ${{ env.SOLUTION_FILE}}-${{ env.VERSION}}.zip
|
|
||||||
asset_content_type: application/zip
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN}}
|
|
||||||
Push_Nuget_Package:
|
|
||||||
needs: Build_Project
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- name: Download Build Version Info
|
|
||||||
uses: actions/download-artifact@v1
|
|
||||||
with:
|
|
||||||
name: Version
|
|
||||||
- name: Set Version Number
|
|
||||||
shell: powershell
|
|
||||||
run: |
|
|
||||||
Get-ChildItem "./Version"
|
|
||||||
$version = Get-Content -Path ./Version/version.txt
|
|
||||||
Write-Host "Version: $version"
|
|
||||||
echo "VERSION=$version" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
|
||||||
Remove-Item -Path ./Version/version.txt
|
|
||||||
Remove-Item -Path ./Version
|
|
||||||
- name: Download Build output
|
|
||||||
uses: actions/download-artifact@v1
|
|
||||||
with:
|
|
||||||
name: Build
|
|
||||||
path: ./
|
|
||||||
- name: Unzip Build file
|
|
||||||
run: |
|
|
||||||
Get-ChildItem .\*.zip | Expand-Archive -DestinationPath .\
|
|
||||||
Remove-Item -Path .\*.zip
|
|
||||||
- name: Copy Files to root & delete output directory
|
|
||||||
run: |
|
|
||||||
Remove-Item -Path .\* -Include @("*.cpz","*.md","*.cplz","*.json","*.dll","*.clz")
|
|
||||||
Get-ChildItem -Path .\output\* | Copy-Item -Destination .\
|
|
||||||
Remove-Item -Path .\output -Recurse
|
|
||||||
- name: Add nuget.exe
|
|
||||||
uses: nuget/setup-nuget@v1
|
|
||||||
- name: Add Github Packages source
|
|
||||||
run: nuget sources add -name github -source https://nuget.pkg.github.com/pepperdash/index.json -username Pepperdash -password ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Add nuget.org API Key
|
|
||||||
run: nuget setApiKey ${{ secrets.NUGET_API_KEY }}
|
|
||||||
- name: Create nuget package
|
|
||||||
run: nuget pack "./PepperDash_Core.nuspec" -version ${{ env.VERSION }}
|
|
||||||
- name: Publish nuget package to Github registry
|
|
||||||
run: nuget push **/*.nupkg -source github
|
|
||||||
- name: Publish nuget package to nuget.org
|
|
||||||
run: nuget push **/*.nupkg -Source https://api.nuget.org/v3/index.json
|
|
||||||
129
.github/workflows/main.yml
vendored
129
.github/workflows/main.yml
vendored
|
|
@ -1,129 +0,0 @@
|
||||||
name: Main Build using Docker
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types:
|
|
||||||
- created
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
env:
|
|
||||||
# solution path doesn't need slashes unless there it is multiple folders deep
|
|
||||||
# solution name does not include extension. .sln is assumed
|
|
||||||
SOLUTION_PATH: Pepperdash Core
|
|
||||||
SOLUTION_FILE: Pepperdash Core
|
|
||||||
# Do not edit this, we're just creating it here
|
|
||||||
VERSION: 0.0.0-buildtype-buildnumber
|
|
||||||
# Defaults to debug for build type
|
|
||||||
BUILD_TYPE: Release
|
|
||||||
# Defaults to main as the release branch. Change as necessary
|
|
||||||
RELEASE_BRANCH: main
|
|
||||||
jobs:
|
|
||||||
Build_Project:
|
|
||||||
runs-on: windows-2019
|
|
||||||
steps:
|
|
||||||
# First we checkout the source repo
|
|
||||||
- name: Checkout repo
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
# And any submodules
|
|
||||||
- name: Checkout submodules
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
git config --global url."https://github.com/".insteadOf "git@github.com:"
|
|
||||||
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
|
|
||||||
git submodule sync --recursive
|
|
||||||
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
|
|
||||||
# Generate the appropriate version number
|
|
||||||
- name: Set Version Number
|
|
||||||
shell: powershell
|
|
||||||
env:
|
|
||||||
TAG_NAME: ${{ github.event.release.tag_name }}
|
|
||||||
run: echo "VERSION=$($Env:TAG_NAME)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
|
||||||
# Use the version number to set the version of the assemblies
|
|
||||||
- name: Update AssemblyInfo.cs
|
|
||||||
shell: powershell
|
|
||||||
run: |
|
|
||||||
Write-Output ${{ env.VERSION }}
|
|
||||||
./.github/scripts/UpdateAssemblyVersion.ps1 ${{ env.VERSION }}
|
|
||||||
# Login to Docker
|
|
||||||
- name: Login to Docker
|
|
||||||
uses: azure/docker-login@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.dockerhub_user }}
|
|
||||||
password: ${{ secrets.dockerhub_password }}
|
|
||||||
# Build the solutions in the docker image
|
|
||||||
- name: Build Solution
|
|
||||||
shell: powershell
|
|
||||||
run: |
|
|
||||||
Invoke-Expression "docker run --rm --mount type=bind,source=""$($Env:GITHUB_WORKSPACE)"",target=""c:/project"" pepperdash/sspbuilder c:\cihelpers\vsidebuild.exe -Solution ""c:\project\$($Env:SOLUTION_PATH)\$($Env:SOLUTION_FILE).sln"" -BuildSolutionConfiguration $($ENV:BUILD_TYPE)"
|
|
||||||
# Zip up the output files as needed
|
|
||||||
- name: Zip Build Output
|
|
||||||
shell: powershell
|
|
||||||
run: ./.github/scripts/ZipBuildOutput.ps1
|
|
||||||
# Write the version to a file to be consumed by the push jobs
|
|
||||||
- name: Write Version
|
|
||||||
run: Write-Output "$($Env:VERSION)" | Out-File -FilePath "$($Env:GITHUB_HOME)\output\version.txt"
|
|
||||||
- name: Upload Build Output
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: Build
|
|
||||||
path: ./${{ env.SOLUTION_FILE}}-${{ env.VERSION}}.zip
|
|
||||||
# Upload the Version file as an artifact
|
|
||||||
- name: Upload version.txt
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: Version
|
|
||||||
path: ${{env.GITHUB_HOME}}\output\version.txt
|
|
||||||
# Upload the build package to the release
|
|
||||||
- name: Upload Release Package
|
|
||||||
id: upload_release
|
|
||||||
uses: actions/upload-release-asset@v1
|
|
||||||
with:
|
|
||||||
upload_url: ${{ github.event.release.upload_url }}
|
|
||||||
asset_path: ./${{ env.SOLUTION_FILE}}-${{ env.VERSION}}.zip
|
|
||||||
asset_name: ${{ env.SOLUTION_FILE}}-${{ env.VERSION}}.zip
|
|
||||||
asset_content_type: application/zip
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
Push_Nuget_Package:
|
|
||||||
needs: Build_Project
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- name: Download Build Version Info
|
|
||||||
uses: actions/download-artifact@v1
|
|
||||||
with:
|
|
||||||
name: Version
|
|
||||||
- name: Set Version Number
|
|
||||||
shell: powershell
|
|
||||||
run: |
|
|
||||||
Get-ChildItem "./Version"
|
|
||||||
$version = Get-Content -Path ./Version/version.txt
|
|
||||||
Write-Host "Version: $version"
|
|
||||||
echo "VERSION=$version" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
|
||||||
Remove-Item -Path ./Version/version.txt
|
|
||||||
Remove-Item -Path ./Version
|
|
||||||
- name: Download Build output
|
|
||||||
uses: actions/download-artifact@v1
|
|
||||||
with:
|
|
||||||
name: Build
|
|
||||||
path: ./
|
|
||||||
- name: Unzip Build file
|
|
||||||
run: |
|
|
||||||
Get-ChildItem .\*.zip | Expand-Archive -DestinationPath .\
|
|
||||||
Remove-Item -Path .\*.zip
|
|
||||||
- name: Copy Files to root & delete output directory
|
|
||||||
run: |
|
|
||||||
Remove-Item -Path .\* -Include @("*.cpz","*.md","*.cplz","*.json","*.dll","*.clz")
|
|
||||||
Get-ChildItem -Path .\output\* | Copy-Item -Destination .\
|
|
||||||
Remove-Item -Path .\output -Recurse
|
|
||||||
- name: Add nuget.exe
|
|
||||||
uses: nuget/setup-nuget@v1
|
|
||||||
- name: Add Github Packages source
|
|
||||||
run: nuget sources add -name github -source https://nuget.pkg.github.com/pepperdash/index.json -username Pepperdash -password ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Add nuget.org API Key
|
|
||||||
run: nuget setApiKey ${{ secrets.NUGET_API_KEY }}
|
|
||||||
- name: Create nuget package
|
|
||||||
run: nuget pack "./PepperDash_Core.nuspec" -version ${{ env.VERSION }}
|
|
||||||
- name: Publish nuget package to Github registry
|
|
||||||
run: nuget push **/*.nupkg -source github
|
|
||||||
- name: Publish nuget package to nuget.org
|
|
||||||
run: nuget push **/*.nupkg -Source https://api.nuget.org/v3/index.json
|
|
||||||
412
.gitignore
vendored
412
.gitignore
vendored
|
|
@ -1,25 +1,401 @@
|
||||||
#ignore thumbnails created by windows
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
Thumbs.db
|
## files generated by popular Visual Studio add-ons.
|
||||||
#Ignore files build by Visual Studio
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
*.user
|
*.user
|
||||||
*.aps
|
*.userosscache
|
||||||
*.pch
|
*.sln.docstates
|
||||||
*.vspscc
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
mono_crash.*
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
[Ww][Ii][Nn]32/
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUnit
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
nunit-*.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# ASP.NET Scaffolding
|
||||||
|
ScaffoldingReadMe.txt
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
*_i.c
|
*_i.c
|
||||||
*_p.c
|
*_p.c
|
||||||
*.ncb
|
*_h.h
|
||||||
*.suo
|
|
||||||
*.bak
|
|
||||||
*.cache
|
|
||||||
*.ilk
|
*.ilk
|
||||||
*.log
|
*.meta
|
||||||
[Bb]in/
|
*.obj
|
||||||
[Dd]ebug*/
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
*.sbr
|
*.sbr
|
||||||
obj/
|
*.tlb
|
||||||
[Rr]elease*/
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.tlog
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
_ReSharper*/
|
_ReSharper*/
|
||||||
SIMPLSharpLogs/
|
*.[Rr]e[Ss]harper
|
||||||
*.projectinfo
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Coverlet is a free, cross platform Code Coverage Tool
|
||||||
|
coverage*.json
|
||||||
|
coverage*.xml
|
||||||
|
coverage*.info
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
*.nupkg
|
*.nupkg
|
||||||
lib/
|
# NuGet Symbol Packages
|
||||||
|
*.snupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
*.appxbundle
|
||||||
|
*.appxupload
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!?*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
*- [Bb]ackup.rdl
|
||||||
|
*- [Bb]ackup ([0-9]).rdl
|
||||||
|
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||||
|
*.vbp
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||||
|
*.dsw
|
||||||
|
*.dsp
|
||||||
|
|
||||||
|
# Visual Studio 6 technical files
|
||||||
|
*.ncb
|
||||||
|
*.aps
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
||||||
|
|
||||||
|
# Visual Studio History (VSHistory) files
|
||||||
|
.vshistory/
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
healthchecksdb
|
||||||
|
|
||||||
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
|
MigrationBackup/
|
||||||
|
|
||||||
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
|
.ionide/
|
||||||
|
|
||||||
|
# Fody - auto-generated XML schema
|
||||||
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
# VS Code files for those working on multiple tools
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
# Local History for Visual Studio Code
|
||||||
|
.history/
|
||||||
|
|
||||||
|
# Windows Installer files from build outputs
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
*.sln.iml
|
||||||
|
*.projectinfo
|
||||||
|
|
||||||
|
output/
|
||||||
36
.releaserc.json
Normal file
36
.releaserc.json
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
[
|
||||||
|
"@semantic-release/commit-analyzer",
|
||||||
|
{
|
||||||
|
"releaseRules": [
|
||||||
|
{ "scope": "force-patch", "release": "patch" },
|
||||||
|
{ "scope": "no-release", "release": false }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@semantic-release/release-notes-generator",
|
||||||
|
["@semantic-release/changelog",
|
||||||
|
{
|
||||||
|
"changelogFile": "CHANGELOG.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"@semantic-release/exec",
|
||||||
|
{
|
||||||
|
"verifyReleaseCmd": "echo \"newVersion=true\" >> $GITHUB_OUTPUT",
|
||||||
|
"publishCmd": "echo \"version=${nextRelease.version}\" >> $GITHUB_OUTPUT && echo \"tag=${nextRelease.gitTag}\" >> $GITHUB_OUTPUT && echo \"type=${nextRelease.type}\" >> $GITHUB_OUTPUT && echo \"channel=${nextRelease.channel}\" >> $GITHUB_OUTPUT"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"branches": [
|
||||||
|
"main",
|
||||||
|
{"name": "development", "prerelease": "beta", "channel": "beta"},
|
||||||
|
{"name": "release", "prerelease": "rc", "channel": "rc"},
|
||||||
|
{
|
||||||
|
"name": "replace-me-feature-branch",
|
||||||
|
"prerelease": "replace-me-prerelease",
|
||||||
|
"channel": "replace-me-prerelease"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
25
PepperDash.Core.4Series.sln
Normal file
25
PepperDash.Core.4Series.sln
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.1.32228.430
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Core.4Series", "src\PepperDash.Core.4Series.csproj", "{100ABA44-9471-4B18-8092-4D94D7D82223}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{100ABA44-9471-4B18-8092-4D94D7D82223}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{100ABA44-9471-4B18-8092-4D94D7D82223}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{100ABA44-9471-4B18-8092-4D94D7D82223}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{100ABA44-9471-4B18-8092-4D94D7D82223}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {E4615FA3-8C8C-4DC0-897B-E85408B4E341}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
|
||||||
# Visual Studio 2008
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash_Core", "Pepperdash Core\PepperDash_Core.csproj", "{87E29B4C-569B-4368-A4ED-984AC1440C96}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{87E29B4C-569B-4368-A4ED-984AC1440C96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{87E29B4C-569B-4368-A4ED-984AC1440C96}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{87E29B4C-569B-4368-A4ED-984AC1440C96}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{87E29B4C-569B-4368-A4ED-984AC1440C96}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
|
||||||
# Visual Studio 2008
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash_Core", "Pepperdash Core\PepperDash_Core.csproj", "{87E29B4C-569B-4368-A4ED-984AC1440C96}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{87E29B4C-569B-4368-A4ED-984AC1440C96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{87E29B4C-569B-4368-A4ED-984AC1440C96}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{87E29B4C-569B-4368-A4ED-984AC1440C96}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{87E29B4C-569B-4368-A4ED-984AC1440C96}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,684 +0,0 @@
|
||||||
/*PepperDash Technology Corp.
|
|
||||||
JAG
|
|
||||||
Copyright: 2017
|
|
||||||
------------------------------------
|
|
||||||
***Notice of Ownership and Copyright***
|
|
||||||
The material in which this notice appears is the property of PepperDash Technology Corporation,
|
|
||||||
which claims copyright under the laws of the United States of America in the entire body of material
|
|
||||||
and in all parts thereof, regardless of the use to which it is being put. Any use, in whole or in part,
|
|
||||||
of this material by another party without the express written permission of PepperDash Technology Corporation is prohibited.
|
|
||||||
PepperDash Technology Corporation reserves all rights under applicable laws.
|
|
||||||
------------------------------------ */
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
using Crestron.SimplSharp.CrestronSockets;
|
|
||||||
using PepperDash.Core;
|
|
||||||
|
|
||||||
namespace DynamicTCP
|
|
||||||
{
|
|
||||||
public class DynamicTCPServer : Device
|
|
||||||
{
|
|
||||||
#region Events
|
|
||||||
/// <summary>
|
|
||||||
/// Event for Receiving text
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<CopyCoreForSimplpGenericCommMethodReceiveTextArgs> TextReceived;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event for client connection socket status change
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<DynamicTCPSocketStatusChangeEventArgs> ClientConnectionChange;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event for Server State Change
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<DynamicTCPServerStateChangedEventArgs> ServerStateChange;
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Properties/Variables
|
|
||||||
/// <summary>
|
|
||||||
/// Secure or unsecure TCP server. Defaults to Unsecure or standard TCP server without SSL
|
|
||||||
/// </summary>
|
|
||||||
public bool Secure { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// S+ Helper for Secure bool. Parameter in SIMPL+ so there is no get, one way set from simpl+ Param to property in func main of SIMPL+
|
|
||||||
/// </summary>
|
|
||||||
public ushort USecure
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == 1)
|
|
||||||
Secure = true;
|
|
||||||
else if (value == 0)
|
|
||||||
Secure = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Text representation of the Socket Status enum values for the server
|
|
||||||
/// </summary>
|
|
||||||
public string Status
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (Secure ? SecureServer != null : UnsecureServer != null)
|
|
||||||
return Secure ? SecureServer.State.ToString() : UnsecureServer.State.ToString();
|
|
||||||
else
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Bool showing if socket is connected
|
|
||||||
/// </summary>
|
|
||||||
public bool IsConnected
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return (Secure ? SecureServer != null : UnsecureServer != null) &&
|
|
||||||
(Secure ? SecureServer.State == ServerState.SERVER_CONNECTED : UnsecureServer.State == ServerState.SERVER_CONNECTED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// S+ helper for IsConnected
|
|
||||||
/// </summary>
|
|
||||||
public ushort UIsConnected
|
|
||||||
{
|
|
||||||
get { return (ushort)(IsConnected ? 1 : 0); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Bool showing if socket is connected
|
|
||||||
/// </summary>
|
|
||||||
public bool IsListening
|
|
||||||
{
|
|
||||||
get { return (Secure ? SecureServer != null : UnsecureServer != null) &&
|
|
||||||
(Secure ? SecureServer.State == ServerState.SERVER_LISTENING : UnsecureServer.State == ServerState.SERVER_LISTENING); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// S+ helper for IsConnected
|
|
||||||
/// </summary>
|
|
||||||
public ushort UIsListening
|
|
||||||
{
|
|
||||||
get { return (ushort)(IsListening ? 1 : 0); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public ushort MaxClients { get; set; } // should be set by parameter in SIMPL+ in the MAIN method, Should not ever need to be configurable
|
|
||||||
/// <summary>
|
|
||||||
/// Number of clients currently connected.
|
|
||||||
/// </summary>
|
|
||||||
public ushort NumberOfClientsConnected
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (Secure ? SecureServer != null : UnsecureServer != null)
|
|
||||||
return Secure ? (ushort)SecureServer.NumberOfClientsConnected : (ushort)UnsecureServer.NumberOfClientsConnected;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Port Server should listen on
|
|
||||||
/// </summary>
|
|
||||||
public int Port { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// S+ helper for Port
|
|
||||||
/// </summary>
|
|
||||||
public ushort UPort
|
|
||||||
{
|
|
||||||
get { return Convert.ToUInt16(Port); }
|
|
||||||
set { Port = Convert.ToInt32(value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Bool to show whether the server requires a preshared key. Must be set the same in the client, and if true shared keys must be identical on server/client
|
|
||||||
/// </summary>
|
|
||||||
public bool SharedKeyRequired { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// S+ helper for requires shared key bool
|
|
||||||
/// </summary>
|
|
||||||
public ushort USharedKeyRequired
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == 1)
|
|
||||||
SharedKeyRequired = true;
|
|
||||||
else
|
|
||||||
SharedKeyRequired = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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
|
|
||||||
/// </summary>
|
|
||||||
public string SharedKey { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Heartbeat Required bool sets whether server disconnects client if heartbeat is not received
|
|
||||||
/// </summary>
|
|
||||||
public bool HeartbeatRequired { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// S+ Helper for Heartbeat Required
|
|
||||||
/// </summary>
|
|
||||||
public ushort UHeartbeatRequired
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == 1)
|
|
||||||
HeartbeatRequired = true;
|
|
||||||
else
|
|
||||||
HeartbeatRequired = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Milliseconds before server expects another heartbeat. Set by property HeartbeatRequiredIntervalInSeconds which is driven from S+
|
|
||||||
/// </summary>
|
|
||||||
public int HeartbeatRequiredIntervalMs { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Simpl+ Heartbeat Analog value in seconds
|
|
||||||
/// </summary>
|
|
||||||
public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatRequiredIntervalMs = (value * 1000); } }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// String to Match for heartbeat. If null or empty any string will reset heartbeat timer
|
|
||||||
/// </summary>
|
|
||||||
public string HeartbeatStringToMatch { get; set; }
|
|
||||||
|
|
||||||
//private timers for Heartbeats per client
|
|
||||||
Dictionary<uint, CTimer> HeartbeatTimerDictionary = new Dictionary<uint, CTimer>();
|
|
||||||
|
|
||||||
//flags to show the secure server is waiting for client at index to send the shared key
|
|
||||||
List<uint> WaitingForSharedKey = new List<uint>();
|
|
||||||
|
|
||||||
//Store the connected client indexes
|
|
||||||
List<uint> ConnectedClientsIndexes = new List<uint>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defaults to 2000
|
|
||||||
/// </summary>
|
|
||||||
public int BufferSize { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Private flag to note that the server has stopped intentionally
|
|
||||||
/// </summary>
|
|
||||||
private bool ServerStopped { get; set; }
|
|
||||||
|
|
||||||
//Servers
|
|
||||||
SecureTCPServer SecureServer;
|
|
||||||
TCPServer UnsecureServer;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Constructors
|
|
||||||
/// <summary>
|
|
||||||
/// constructor
|
|
||||||
/// </summary>
|
|
||||||
public DynamicTCPServer()
|
|
||||||
: base("Uninitialized Dynamic TCP Server")
|
|
||||||
{
|
|
||||||
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
|
||||||
BufferSize = 2000;
|
|
||||||
Secure = false;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Methods - Server Actions
|
|
||||||
/// <summary>
|
|
||||||
/// Initialize Key for device using client name from SIMPL+. Called on Listen from SIMPL+
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
public void Initialize(string key)
|
|
||||||
{
|
|
||||||
Key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Start listening on the specified port
|
|
||||||
/// </summary>
|
|
||||||
public void Listen()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (Port < 1 || Port > 65535)
|
|
||||||
{
|
|
||||||
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericSecureTcpClient '{0}': Invalid port", Key);
|
|
||||||
ErrorLog.Warn(string.Format("GenericSecureTcpClient '{0}': Invalid port", Key));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
|
|
||||||
{
|
|
||||||
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericSecureTcpClient '{0}': No Shared Key set", Key);
|
|
||||||
ErrorLog.Warn(string.Format("GenericSecureTcpClient '{0}': No Shared Key set", Key));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (IsListening)
|
|
||||||
return;
|
|
||||||
if (Secure)
|
|
||||||
{
|
|
||||||
SecureServer = new SecureTCPServer(Port, MaxClients);
|
|
||||||
SecureServer.SocketStatusChange += new SecureTCPServerSocketStatusChangeEventHandler(SecureServer_SocketStatusChange);
|
|
||||||
ServerStopped = false;
|
|
||||||
SecureServer.WaitForConnectionAsync(IPAddress.Any, SecureConnectCallback);
|
|
||||||
onServerStateChange();
|
|
||||||
Debug.Console(2, "Secure Server Status: {0}, Socket Status: {1}\r\n", SecureServer.State.ToString(), SecureServer.ServerSocketStatus);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UnsecureServer = new TCPServer(Port, MaxClients);
|
|
||||||
UnsecureServer.SocketStatusChange += new TCPServerSocketStatusChangeEventHandler(UnsecureServer_SocketStatusChange);
|
|
||||||
ServerStopped = false;
|
|
||||||
UnsecureServer.WaitForConnectionAsync(IPAddress.Any, UnsecureConnectCallback);
|
|
||||||
onServerStateChange();
|
|
||||||
Debug.Console(2, "Unsecure Server Status: {0}, Socket Status: {1}\r\n", UnsecureServer.State.ToString(), UnsecureServer.ServerSocketStatus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ErrorLog.Error("Error with Dynamic Server: {0}", ex.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stop Listeneing
|
|
||||||
/// </summary>
|
|
||||||
public void StopListening()
|
|
||||||
{
|
|
||||||
Debug.Console(2, "Stopping Listener");
|
|
||||||
if (SecureServer != null)
|
|
||||||
SecureServer.Stop();
|
|
||||||
if (UnsecureServer != null)
|
|
||||||
UnsecureServer.Stop();
|
|
||||||
ServerStopped = true;
|
|
||||||
onServerStateChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Disconnect All Clients
|
|
||||||
/// </summary>
|
|
||||||
public void DisconnectAllClients()
|
|
||||||
{
|
|
||||||
Debug.Console(2, "Disconnecting All Clients");
|
|
||||||
if (SecureServer != null)
|
|
||||||
SecureServer.DisconnectAll();
|
|
||||||
if (UnsecureServer != null)
|
|
||||||
UnsecureServer.DisconnectAll();
|
|
||||||
onConnectionChange();
|
|
||||||
onServerStateChange(); //State shows both listening and connected
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Broadcast text from server to all connected clients
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="text"></param>
|
|
||||||
public void BroadcastText(string text)
|
|
||||||
{
|
|
||||||
if (ConnectedClientsIndexes.Count > 0)
|
|
||||||
{
|
|
||||||
byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
|
|
||||||
if (Secure)
|
|
||||||
foreach (uint i in ConnectedClientsIndexes)
|
|
||||||
SecureServer.SendDataAsync(i, b, b.Length, SecureSendDataAsyncCallback);
|
|
||||||
else
|
|
||||||
foreach (uint i in ConnectedClientsIndexes)
|
|
||||||
UnsecureServer.SendDataAsync(i, b, b.Length, UnsecureSendDataAsyncCallback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Not sure this is useful in library, maybe Pro??
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="text"></param>
|
|
||||||
/// <param name="clientIndex"></param>
|
|
||||||
public void SendTextToClient(string text, uint clientIndex)
|
|
||||||
{
|
|
||||||
byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
|
|
||||||
if (Secure)
|
|
||||||
SecureServer.SendDataAsync(clientIndex, b, b.Length, SecureSendDataAsyncCallback);
|
|
||||||
else
|
|
||||||
UnsecureServer.SendDataAsync(clientIndex, b, b.Length, UnsecureSendDataAsyncCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
//private method to check heartbeat requirements and start or reset timer
|
|
||||||
void checkHeartbeat(uint clientIndex, string received)
|
|
||||||
{
|
|
||||||
if (HeartbeatRequired)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(HeartbeatStringToMatch))
|
|
||||||
{
|
|
||||||
if (received == HeartbeatStringToMatch)
|
|
||||||
{
|
|
||||||
if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
|
|
||||||
HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
|
|
||||||
HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
|
|
||||||
HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
|
|
||||||
HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Methods - HeartbeatTimer Callback
|
|
||||||
|
|
||||||
void HeartbeatTimer_CallbackFunction(object o)
|
|
||||||
{
|
|
||||||
uint clientIndex = (uint)o;
|
|
||||||
|
|
||||||
string address = Secure ? SecureServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex) :
|
|
||||||
UnsecureServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
|
|
||||||
|
|
||||||
ErrorLog.Error("Heartbeat not received for Client at IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE", address);
|
|
||||||
Debug.Console(2, "Heartbeat not received for Client at IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE", address);
|
|
||||||
|
|
||||||
SendTextToClient("Heartbeat not received by server, closing connection", clientIndex);
|
|
||||||
|
|
||||||
if (Secure)
|
|
||||||
SecureServer.Disconnect(clientIndex);
|
|
||||||
else
|
|
||||||
UnsecureServer.Disconnect(clientIndex);
|
|
||||||
HeartbeatTimerDictionary.Remove(clientIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Methods - Socket Status Changed Callbacks
|
|
||||||
/// <summary>
|
|
||||||
/// Secure Server Socket Status Changed Callback
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mySecureTCPServer"></param>
|
|
||||||
/// <param name="clientIndex"></param>
|
|
||||||
/// <param name="serverSocketStatus"></param>
|
|
||||||
void SecureServer_SocketStatusChange(SecureTCPServer server, uint clientIndex, SocketStatus serverSocketStatus)
|
|
||||||
{
|
|
||||||
Debug.Console(2, "Client at {0} ServerSocketStatus {1}",
|
|
||||||
server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex), serverSocketStatus.ToString());
|
|
||||||
if (server.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
|
|
||||||
{
|
|
||||||
if (SharedKeyRequired && !WaitingForSharedKey.Contains(clientIndex))
|
|
||||||
WaitingForSharedKey.Add(clientIndex);
|
|
||||||
if (!ConnectedClientsIndexes.Contains(clientIndex))
|
|
||||||
ConnectedClientsIndexes.Add(clientIndex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (ConnectedClientsIndexes.Contains(clientIndex))
|
|
||||||
ConnectedClientsIndexes.Remove(clientIndex);
|
|
||||||
if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))
|
|
||||||
HeartbeatTimerDictionary.Remove(clientIndex);
|
|
||||||
}
|
|
||||||
if(SecureServer.ServerSocketStatus.ToString() != Status)
|
|
||||||
onConnectionChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// TCP Server (Unsecure) Socket Status Change Callback
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mySecureTCPServer"></param>
|
|
||||||
/// <param name="clientIndex"></param>
|
|
||||||
/// <param name="serverSocketStatus"></param>
|
|
||||||
void UnsecureServer_SocketStatusChange(TCPServer server, uint clientIndex, SocketStatus serverSocketStatus)
|
|
||||||
{
|
|
||||||
Debug.Console(2, "Client at {0} ServerSocketStatus {1}",
|
|
||||||
server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex), serverSocketStatus.ToString());
|
|
||||||
if (server.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
|
|
||||||
{
|
|
||||||
if (SharedKeyRequired && !WaitingForSharedKey.Contains(clientIndex))
|
|
||||||
WaitingForSharedKey.Add(clientIndex);
|
|
||||||
if (!ConnectedClientsIndexes.Contains(clientIndex))
|
|
||||||
ConnectedClientsIndexes.Add(clientIndex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (ConnectedClientsIndexes.Contains(clientIndex))
|
|
||||||
ConnectedClientsIndexes.Remove(clientIndex);
|
|
||||||
if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))
|
|
||||||
HeartbeatTimerDictionary.Remove(clientIndex);
|
|
||||||
}
|
|
||||||
if (UnsecureServer.ServerSocketStatus.ToString() != Status)
|
|
||||||
onConnectionChange();
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Methods Connected Callbacks
|
|
||||||
/// <summary>
|
|
||||||
/// Secure TCP Client Connected to Secure Server Callback
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mySecureTCPServer"></param>
|
|
||||||
/// <param name="clientIndex"></param>
|
|
||||||
void SecureConnectCallback(SecureTCPServer mySecureTCPServer, uint clientIndex)
|
|
||||||
{
|
|
||||||
if (mySecureTCPServer.ClientConnected(clientIndex))
|
|
||||||
{
|
|
||||||
if (SharedKeyRequired)
|
|
||||||
{
|
|
||||||
byte[] b = Encoding.GetEncoding(28591).GetBytes(SharedKey + "\n");
|
|
||||||
mySecureTCPServer.SendDataAsync(clientIndex, b, b.Length, SecureSendDataAsyncCallback);
|
|
||||||
Debug.Console(2, "Sent Shared Key to client at {0}", mySecureTCPServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
|
|
||||||
}
|
|
||||||
if (HeartbeatRequired)
|
|
||||||
{
|
|
||||||
CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
|
|
||||||
HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
|
|
||||||
}
|
|
||||||
mySecureTCPServer.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback);
|
|
||||||
if (mySecureTCPServer.State != ServerState.SERVER_LISTENING && MaxClients > 1 && !ServerStopped)
|
|
||||||
mySecureTCPServer.WaitForConnectionAsync(IPAddress.Any, SecureConnectCallback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unsecure TCP Client Connected to Unsecure Server Callback
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="myTCPServer"></param>
|
|
||||||
/// <param name="clientIndex"></param>
|
|
||||||
void UnsecureConnectCallback(TCPServer myTCPServer, uint clientIndex)
|
|
||||||
{
|
|
||||||
if (myTCPServer.ClientConnected(clientIndex))
|
|
||||||
{
|
|
||||||
if (SharedKeyRequired)
|
|
||||||
{
|
|
||||||
byte[] b = Encoding.GetEncoding(28591).GetBytes(SharedKey + "\n");
|
|
||||||
myTCPServer.SendDataAsync(clientIndex, b, b.Length, UnsecureSendDataAsyncCallback);
|
|
||||||
Debug.Console(2, "Sent Shared Key to client at {0}", myTCPServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
|
|
||||||
}
|
|
||||||
if (HeartbeatRequired)
|
|
||||||
{
|
|
||||||
CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
|
|
||||||
HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
|
|
||||||
}
|
|
||||||
myTCPServer.ReceiveDataAsync(clientIndex, UnsecureReceivedDataAsyncCallback);
|
|
||||||
if (myTCPServer.State != ServerState.SERVER_LISTENING && MaxClients > 1 && !ServerStopped)
|
|
||||||
myTCPServer.WaitForConnectionAsync(IPAddress.Any, UnsecureConnectCallback);
|
|
||||||
}
|
|
||||||
if (myTCPServer.State != ServerState.SERVER_LISTENING && MaxClients > 1 && !ServerStopped)
|
|
||||||
myTCPServer.WaitForConnectionAsync(IPAddress.Any, UnsecureConnectCallback);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Methods - Send/Receive Callbacks
|
|
||||||
/// <summary>
|
|
||||||
/// Secure Send Data Async Callback
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mySecureTCPServer"></param>
|
|
||||||
/// <param name="clientIndex"></param>
|
|
||||||
/// <param name="numberOfBytesSent"></param>
|
|
||||||
void SecureSendDataAsyncCallback(SecureTCPServer mySecureTCPServer, uint clientIndex, int numberOfBytesSent)
|
|
||||||
{
|
|
||||||
//Seems there is nothing to do here
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unsecure Send Data Asyc Callback
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="myTCPServer"></param>
|
|
||||||
/// <param name="clientIndex"></param>
|
|
||||||
/// <param name="numberOfBytesSent"></param>
|
|
||||||
void UnsecureSendDataAsyncCallback(TCPServer myTCPServer, uint clientIndex, int numberOfBytesSent)
|
|
||||||
{
|
|
||||||
//Seems there is nothing to do here
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Secure Received Data Async Callback
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mySecureTCPServer"></param>
|
|
||||||
/// <param name="clientIndex"></param>
|
|
||||||
/// <param name="numberOfBytesReceived"></param>
|
|
||||||
void SecureReceivedDataAsyncCallback(SecureTCPServer mySecureTCPServer, uint clientIndex, int numberOfBytesReceived)
|
|
||||||
{
|
|
||||||
if (numberOfBytesReceived > 0)
|
|
||||||
{
|
|
||||||
string received = "Nothing";
|
|
||||||
byte[] bytes = mySecureTCPServer.GetIncomingDataBufferForSpecificClient(clientIndex);
|
|
||||||
received = System.Text.Encoding.GetEncoding(28591).GetString(bytes, 0, numberOfBytesReceived);
|
|
||||||
if (WaitingForSharedKey.Contains(clientIndex))
|
|
||||||
{
|
|
||||||
received = received.Replace("\r", "");
|
|
||||||
received = received.Replace("\n", "");
|
|
||||||
if (received != SharedKey)
|
|
||||||
{
|
|
||||||
byte[] b = Encoding.GetEncoding(28591).GetBytes("Shared key did not match server. Disconnecting");
|
|
||||||
Debug.Console(2, "Client at index {0} Shared key did not match the server, disconnecting client", clientIndex);
|
|
||||||
ErrorLog.Error("Client at index {0} Shared key did not match the server, disconnecting client", clientIndex);
|
|
||||||
mySecureTCPServer.SendDataAsync(clientIndex, b, b.Length, null);
|
|
||||||
mySecureTCPServer.Disconnect(clientIndex);
|
|
||||||
}
|
|
||||||
if (mySecureTCPServer.NumberOfClientsConnected > 0)
|
|
||||||
mySecureTCPServer.ReceiveDataAsync(SecureReceivedDataAsyncCallback);
|
|
||||||
WaitingForSharedKey.Remove(clientIndex);
|
|
||||||
byte[] skResponse = Encoding.GetEncoding(28591).GetBytes("Shared Key Match, Connected and ready for communication");
|
|
||||||
mySecureTCPServer.SendDataAsync(clientIndex, skResponse, skResponse.Length, null);
|
|
||||||
mySecureTCPServer.ReceiveDataAsync(SecureReceivedDataAsyncCallback);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mySecureTCPServer.ReceiveDataAsync(SecureReceivedDataAsyncCallback);
|
|
||||||
Debug.Console(2, "Secure Server Listening on Port: {0}, client IP: {1}, NumberOfBytesReceived: {2}, Received: {3}\r\n",
|
|
||||||
mySecureTCPServer.PortNumber, mySecureTCPServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex), numberOfBytesReceived, received);
|
|
||||||
onTextReceived(received);
|
|
||||||
}
|
|
||||||
checkHeartbeat(clientIndex, received);
|
|
||||||
}
|
|
||||||
if (mySecureTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
|
|
||||||
mySecureTCPServer.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unsecure Received Data Async Callback
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="myTCPServer"></param>
|
|
||||||
/// <param name="clientIndex"></param>
|
|
||||||
/// <param name="numberOfBytesReceived"></param>
|
|
||||||
void UnsecureReceivedDataAsyncCallback(TCPServer myTCPServer, uint clientIndex, int numberOfBytesReceived)
|
|
||||||
{
|
|
||||||
if (numberOfBytesReceived > 0)
|
|
||||||
{
|
|
||||||
string received = "Nothing";
|
|
||||||
byte[] bytes = myTCPServer.GetIncomingDataBufferForSpecificClient(clientIndex);
|
|
||||||
received = System.Text.Encoding.GetEncoding(28591).GetString(bytes, 0, numberOfBytesReceived);
|
|
||||||
if (WaitingForSharedKey.Contains(clientIndex))
|
|
||||||
{
|
|
||||||
received = received.Replace("\r", "");
|
|
||||||
received = received.Replace("\n", "");
|
|
||||||
if (received != SharedKey)
|
|
||||||
{
|
|
||||||
byte[] b = Encoding.GetEncoding(28591).GetBytes("Shared key did not match server. Disconnecting");
|
|
||||||
Debug.Console(2, "Client at index {0} Shared key did not match the server, disconnecting client", clientIndex);
|
|
||||||
ErrorLog.Error("Client at index {0} Shared key did not match the server, disconnecting client", clientIndex);
|
|
||||||
myTCPServer.SendDataAsync(clientIndex, b, b.Length, null);
|
|
||||||
myTCPServer.Disconnect(clientIndex);
|
|
||||||
}
|
|
||||||
if (myTCPServer.NumberOfClientsConnected > 0)
|
|
||||||
myTCPServer.ReceiveDataAsync(UnsecureReceivedDataAsyncCallback);
|
|
||||||
WaitingForSharedKey.Remove(clientIndex);
|
|
||||||
byte[] skResponse = Encoding.GetEncoding(28591).GetBytes("Shared Key Match, Connected and ready for communication");
|
|
||||||
myTCPServer.SendDataAsync(clientIndex, skResponse, skResponse.Length, null);
|
|
||||||
myTCPServer.ReceiveDataAsync(UnsecureReceivedDataAsyncCallback);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
myTCPServer.ReceiveDataAsync(UnsecureReceivedDataAsyncCallback);
|
|
||||||
Debug.Console(2, "Secure Server Listening on Port: {0}, client IP: {1}, NumberOfBytesReceived: {2}, Received: {3}\r\n",
|
|
||||||
myTCPServer.PortNumber, myTCPServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex), numberOfBytesReceived, received);
|
|
||||||
onTextReceived(received);
|
|
||||||
}
|
|
||||||
checkHeartbeat(clientIndex, received);
|
|
||||||
}
|
|
||||||
if (myTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
|
|
||||||
myTCPServer.ReceiveDataAsync(clientIndex, UnsecureReceivedDataAsyncCallback);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Methods - EventHelpers/Callbacks
|
|
||||||
//Private Helper method to call the Connection Change Event
|
|
||||||
void onConnectionChange()
|
|
||||||
{
|
|
||||||
var handler = ClientConnectionChange;
|
|
||||||
if (handler != null)
|
|
||||||
{
|
|
||||||
if (Secure)
|
|
||||||
handler(this, new DynamicTCPSocketStatusChangeEventArgs(SecureServer, Secure));
|
|
||||||
else
|
|
||||||
handler(this, new DynamicTCPSocketStatusChangeEventArgs(UnsecureServer, Secure));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Private Helper Method to call the Text Received Event
|
|
||||||
void onTextReceived(string text)
|
|
||||||
{
|
|
||||||
var handler = TextReceived;
|
|
||||||
if (handler != null)
|
|
||||||
handler(this, new CopyCoreForSimplpGenericCommMethodReceiveTextArgs(text));
|
|
||||||
}
|
|
||||||
|
|
||||||
//Private Helper Method to call the Server State Change Event
|
|
||||||
void onServerStateChange()
|
|
||||||
{
|
|
||||||
var handler = ServerStateChange;
|
|
||||||
if(handler != null)
|
|
||||||
{
|
|
||||||
if(Secure)
|
|
||||||
handler(this, new DynamicTCPServerStateChangedEventArgs(SecureServer, Secure));
|
|
||||||
else
|
|
||||||
handler(this, new DynamicTCPServerStateChangedEventArgs(UnsecureServer, Secure));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Private Event Handler method to handle the closing of connections when the program stops
|
|
||||||
void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
|
|
||||||
{
|
|
||||||
if (programEventType == eProgramStatusEventType.Stopping)
|
|
||||||
{
|
|
||||||
Debug.Console(1, this, "Program stopping. Closing server");
|
|
||||||
DisconnectAllClients();
|
|
||||||
StopListening();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
|
|
||||||
namespace PepperDash.Core
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Background class that manages debug features for sockets
|
|
||||||
/// </summary>
|
|
||||||
public static class CommStatic
|
|
||||||
{
|
|
||||||
static List<ISocketStatus> Sockets = new List<ISocketStatus>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets up the backing class. Adds console commands for S#Pro programs
|
|
||||||
/// </summary>
|
|
||||||
static CommStatic()
|
|
||||||
{
|
|
||||||
if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro)
|
|
||||||
{
|
|
||||||
CrestronConsole.AddNewConsoleCommand(SocketCommand, "socket", "socket commands: list, send, connect, disco",
|
|
||||||
ConsoleAccessLevelEnum.AccessOperator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SocketCommand(string s)
|
|
||||||
{
|
|
||||||
// 0 1 2
|
|
||||||
//socket command number/key/all param
|
|
||||||
//socket list
|
|
||||||
//socket send 4 ver -v\n
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(s))
|
|
||||||
return;
|
|
||||||
var tokens = s.Split(' ');
|
|
||||||
if (tokens.Length == 0)
|
|
||||||
return;
|
|
||||||
var command = tokens[0].ToLower();
|
|
||||||
if(command == "connect")
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
else if(command == "disco")
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
else if(command =="list")
|
|
||||||
{
|
|
||||||
CrestronConsole.ConsoleCommandResponse("{0} sockets", Sockets.Count);
|
|
||||||
if(Sockets.Count == 0)
|
|
||||||
return;
|
|
||||||
// get the longest key name, for formatting
|
|
||||||
var longestLength = Sockets.Aggregate("",
|
|
||||||
(max, cur) => max.Length > cur.Key.Length ? max : cur.Key).Length;
|
|
||||||
for(int i = 0; i < Sockets.Count; i++)
|
|
||||||
{
|
|
||||||
var sock = Sockets[i];
|
|
||||||
CrestronConsole.ConsoleCommandResponse("{0} {1} {2} {3}",
|
|
||||||
i, sock.Key, GetSocketType(sock), sock.ClientStatus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(command == "send")
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Helper for socket list, to show types
|
|
||||||
/// </summary>
|
|
||||||
static string GetSocketType(ISocketStatus sock)
|
|
||||||
{
|
|
||||||
if (sock is GenericSshClient)
|
|
||||||
return "SSH";
|
|
||||||
else if (sock is GenericTcpIpClient)
|
|
||||||
return "TCP-IP";
|
|
||||||
else
|
|
||||||
return "?";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="socket"></param>
|
|
||||||
public static void AddSocket(ISocketStatus socket)
|
|
||||||
{
|
|
||||||
if(!Sockets.Contains(socket))
|
|
||||||
Sockets.Add(socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="socket"></param>
|
|
||||||
public static void RemoveSocket(ISocketStatus socket)
|
|
||||||
{
|
|
||||||
if (Sockets.Contains(socket))
|
|
||||||
Sockets.Remove(socket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,314 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
using Crestron.SimplSharp.CrestronIO;
|
|
||||||
using Crestron.SimplSharp.Net.Http;
|
|
||||||
|
|
||||||
namespace PepperDash.Core
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Client for communicating with an HTTP Server Side Event pattern
|
|
||||||
/// </summary>
|
|
||||||
public class GenericHttpSseClient : ICommunicationReceiver
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Notifies when bytes have been received
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
|
|
||||||
/// <summary>
|
|
||||||
/// Notifies when text has been received
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates connection status
|
|
||||||
/// </summary>
|
|
||||||
public bool IsConnected
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
private set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unique identifier for the instance
|
|
||||||
/// </summary>
|
|
||||||
public string Key
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
private set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Name for the instance
|
|
||||||
/// </summary>
|
|
||||||
public string Name
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
private set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// URL of the server
|
|
||||||
/// </summary>
|
|
||||||
public string Url { get; set; }
|
|
||||||
|
|
||||||
HttpClient Client;
|
|
||||||
HttpClientRequest Request;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="name"></param>
|
|
||||||
public GenericHttpSseClient(string key, string name)
|
|
||||||
{
|
|
||||||
Key = key;
|
|
||||||
Name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Connects to the server. Requires Url to be set first.
|
|
||||||
/// </summary>
|
|
||||||
public void Connect()
|
|
||||||
{
|
|
||||||
InitiateConnection(Url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Disconnects from the server
|
|
||||||
/// </summary>
|
|
||||||
public void Disconnect()
|
|
||||||
{
|
|
||||||
CloseConnection(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initiates connection to the server
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="url"></param>
|
|
||||||
public void InitiateConnection(string url)
|
|
||||||
{
|
|
||||||
CrestronInvoke.BeginInvoke(o =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if(string.IsNullOrEmpty(url))
|
|
||||||
{
|
|
||||||
Debug.Console(0, this, "Error connecting to Server. No URL specified");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Client = new HttpClient();
|
|
||||||
Request = new HttpClientRequest();
|
|
||||||
Client.Verbose = true;
|
|
||||||
Client.KeepAlive = true;
|
|
||||||
Request.Url.Parse(url);
|
|
||||||
Request.RequestType = RequestType.Get;
|
|
||||||
Request.Header.SetHeaderValue("Accept", "text/event-stream");
|
|
||||||
|
|
||||||
// In order to get a handle on the response stream, we have to get
|
|
||||||
// the request stream first. Boo
|
|
||||||
Client.BeginGetRequestStream(GetRequestStreamCallback, Request, null);
|
|
||||||
CrestronConsole.PrintLine("Request made!");
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
ErrorLog.Notice("Exception occured in AsyncWebPostHttps(): " + e.ToString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Closes the connection to the server
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="s"></param>
|
|
||||||
public void CloseConnection(string s)
|
|
||||||
{
|
|
||||||
if (Client != null)
|
|
||||||
{
|
|
||||||
Client.Abort();
|
|
||||||
IsConnected = false;
|
|
||||||
|
|
||||||
Debug.Console(1, this, "Client Disconnected");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GetRequestStreamCallback(HttpClientRequest request, HTTP_CALLBACK_ERROR error, object status)
|
|
||||||
{
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// End the the async request operation and return the data stream
|
|
||||||
Stream requestStream = request.ThisClient.EndGetRequestStream(request, null);
|
|
||||||
// If this were something other than a GET we could write to the stream here
|
|
||||||
|
|
||||||
// Closing makes the request happen
|
|
||||||
requestStream.Close();
|
|
||||||
|
|
||||||
// Get a handle on the response stream.
|
|
||||||
request.ThisClient.BeginGetResponseStream(GetResponseStreamCallback, request, status);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
ErrorLog.Notice("Exception occured in GetSecureRequestStreamCallback(): " + e.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="error"></param>
|
|
||||||
/// <param name="status"></param>
|
|
||||||
private void GetResponseStreamCallback(HttpClientRequest request, HTTP_CALLBACK_ERROR error, object status)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// This closes up the GetResponseStream async
|
|
||||||
var response = request.ThisClient.EndGetResponseStream(request);
|
|
||||||
|
|
||||||
response.DataConnection.OnBytesReceived += new EventHandler(DataConnection_OnBytesReceived);
|
|
||||||
|
|
||||||
IsConnected = true;
|
|
||||||
|
|
||||||
Debug.Console(1, this, "Client Disconnected");
|
|
||||||
|
|
||||||
Stream streamResponse = response.ContentStream;
|
|
||||||
// Object containing various states to be passed back to async callback below
|
|
||||||
RequestState asyncState = new RequestState();
|
|
||||||
asyncState.Request = request;
|
|
||||||
asyncState.Response = response;
|
|
||||||
asyncState.StreamResponse = streamResponse;
|
|
||||||
asyncState.HttpClient = request.ThisClient;
|
|
||||||
|
|
||||||
// This processes the ongoing data stream
|
|
||||||
Crestron.SimplSharp.CrestronIO.IAsyncResult asyncResult = null;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
asyncResult = streamResponse.BeginRead(asyncState.BufferRead, 0, RequestState.BUFFER_SIZE,
|
|
||||||
new Crestron.SimplSharp.CrestronIO.AsyncCallback(ReadCallBack), asyncState);
|
|
||||||
}
|
|
||||||
while (asyncResult.CompletedSynchronously && !asyncState.Done);
|
|
||||||
|
|
||||||
//Console.WriteLine("\r\nExit Response Callback\r\n");
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
ErrorLog.Notice("Exception occured in GetSecureRequestStreamCallback(): " + e.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataConnection_OnBytesReceived(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
Debug.Console(1, this, "DataConnection OnBytesReceived Fired");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReadCallBack(Crestron.SimplSharp.CrestronIO.IAsyncResult asyncResult)
|
|
||||||
{
|
|
||||||
//we are getting back everything here, so cast the state from the call
|
|
||||||
RequestState requestState = asyncResult.AsyncState as RequestState;
|
|
||||||
Stream responseStream = requestState.StreamResponse;
|
|
||||||
|
|
||||||
int read = responseStream.EndRead(asyncResult);
|
|
||||||
// Read the HTML page and then print it to the console.
|
|
||||||
if (read > 0)
|
|
||||||
{
|
|
||||||
var bytes = requestState.BufferRead;
|
|
||||||
|
|
||||||
var bytesHandler = BytesReceived;
|
|
||||||
if (bytesHandler != null)
|
|
||||||
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
|
|
||||||
var textHandler = TextReceived;
|
|
||||||
if (textHandler != null)
|
|
||||||
{
|
|
||||||
var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
|
|
||||||
textHandler(this, new GenericCommMethodReceiveTextArgs(str));
|
|
||||||
}
|
|
||||||
|
|
||||||
//requestState.RequestData.Append(Encoding.ASCII.GetString(requestState.BufferRead, 0, read));
|
|
||||||
//CrestronConsole.PrintLine(requestState.RequestData.ToString());
|
|
||||||
|
|
||||||
//clear the byte array buffer used.
|
|
||||||
Array.Clear(requestState.BufferRead, 0, requestState.BufferRead.Length);
|
|
||||||
|
|
||||||
if (asyncResult.CompletedSynchronously)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Crestron.SimplSharp.CrestronIO.IAsyncResult asynchronousResult;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
asynchronousResult = responseStream.BeginRead(requestState.BufferRead, 0, RequestState.BUFFER_SIZE,
|
|
||||||
new Crestron.SimplSharp.CrestronIO.AsyncCallback(ReadCallBack), requestState);
|
|
||||||
}
|
|
||||||
while (asynchronousResult.CompletedSynchronously && !requestState.Done);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
requestState.Done = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stores the state of the request
|
|
||||||
/// </summary>
|
|
||||||
public class RequestState
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public const int BUFFER_SIZE = 10000;
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public byte[] BufferRead;
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public HttpClient HttpClient;
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public HttpClientRequest Request;
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public HttpClientResponse Response;
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public Stream StreamResponse;
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public bool Done;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor
|
|
||||||
/// </summary>
|
|
||||||
public RequestState()
|
|
||||||
{
|
|
||||||
BufferRead = new byte[BUFFER_SIZE];
|
|
||||||
HttpClient = null;
|
|
||||||
Request = null;
|
|
||||||
Response = null;
|
|
||||||
StreamResponse = null;
|
|
||||||
Done = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Waithandle for main thread.
|
|
||||||
/// </summary>
|
|
||||||
public class StreamAsyncTest
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public CEvent wait_for_response = new CEvent(true, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,351 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
|
|
||||||
namespace PepperDash.Core
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Allows for two simultaneous TCP clients to connect to a redundant pair of QSC Core DSPs and manages
|
|
||||||
/// </summary>
|
|
||||||
public class QscCoreDoubleTcpIpClient : IKeyed
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Key to uniquely identify the instance of the class
|
|
||||||
/// </summary>
|
|
||||||
public string Key { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Fires when a bool value changes to notify the S+ module
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<BoolChangeEventArgs> BoolChange;
|
|
||||||
/// <summary>
|
|
||||||
/// Fires when a ushort value changes to notify the S+ module
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<UshrtChangeEventArgs> UshortChange;
|
|
||||||
/// <summary>
|
|
||||||
/// Fires when a string value changes to notify the S+ module
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<StringChangeEventArgs> StringChange;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The client for the master DSP unit
|
|
||||||
/// </summary>
|
|
||||||
public GenericTcpIpClient MasterClient { get; private set; }
|
|
||||||
/// <summary>
|
|
||||||
/// The client for the slave DSP unit
|
|
||||||
/// </summary>
|
|
||||||
public GenericTcpIpClient SlaveClient { get; private set; }
|
|
||||||
|
|
||||||
string Username;
|
|
||||||
string Password;
|
|
||||||
string LineEnding;
|
|
||||||
|
|
||||||
CommunicationGather MasterGather;
|
|
||||||
CommunicationGather SlaveGather;
|
|
||||||
|
|
||||||
bool IsPolling;
|
|
||||||
int PollingIntervalSeconds;
|
|
||||||
CTimer PollTimer;
|
|
||||||
|
|
||||||
bool SlaveIsActive;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default constuctor for S+
|
|
||||||
/// </summary>
|
|
||||||
public QscCoreDoubleTcpIpClient()
|
|
||||||
{
|
|
||||||
MasterClient = new GenericTcpIpClient("temp-master");
|
|
||||||
MasterClient.AutoReconnect = true;
|
|
||||||
MasterClient.AutoReconnectIntervalMs = 2000;
|
|
||||||
SlaveClient = new GenericTcpIpClient("temp-slave");
|
|
||||||
SlaveClient.AutoReconnect = true;
|
|
||||||
SlaveClient.AutoReconnectIntervalMs = 2000;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Connects to both DSP units
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="masterAddress"></param>
|
|
||||||
/// <param name="masterPort"></param>
|
|
||||||
/// <param name="slaveAddress"></param>
|
|
||||||
/// <param name="slavePort"></param>
|
|
||||||
/// <param name="username"></param>
|
|
||||||
/// <param name="password"></param>
|
|
||||||
/// <param name="pollingIntervalSeconds"></param>
|
|
||||||
/// <param name="lineEnding"></param>
|
|
||||||
public void Connect(string key, string masterAddress, int masterPort,
|
|
||||||
string slaveAddress, int slavePort, string username, string password,
|
|
||||||
int pollingIntervalSeconds, string lineEnding)
|
|
||||||
{
|
|
||||||
Key = key;
|
|
||||||
|
|
||||||
PollingIntervalSeconds = pollingIntervalSeconds;
|
|
||||||
Username = username;
|
|
||||||
Password = password;
|
|
||||||
LineEnding = lineEnding;
|
|
||||||
|
|
||||||
MasterClient.Initialize(key + "-master");
|
|
||||||
SlaveClient.Initialize(key + "-slave");
|
|
||||||
|
|
||||||
MasterClient.Hostname = masterAddress;
|
|
||||||
MasterClient.Port = masterPort;
|
|
||||||
|
|
||||||
if (MasterClient != null)
|
|
||||||
{
|
|
||||||
MasterClient.Disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SlaveClient != null)
|
|
||||||
{
|
|
||||||
SlaveClient.Disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MasterGather == null)
|
|
||||||
{
|
|
||||||
MasterGather = new CommunicationGather(MasterClient, lineEnding);
|
|
||||||
MasterGather.IncludeDelimiter = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
MasterGather.LineReceived -= MasterGather_LineReceived;
|
|
||||||
MasterGather.LineReceived += new EventHandler<GenericCommMethodReceiveTextArgs>(MasterGather_LineReceived);
|
|
||||||
|
|
||||||
MasterClient.ConnectionChange -= MasterClient_SocketStatusChange;
|
|
||||||
MasterClient.ConnectionChange += MasterClient_SocketStatusChange;
|
|
||||||
|
|
||||||
SlaveClient.Hostname = slaveAddress;
|
|
||||||
SlaveClient.Port = slavePort;
|
|
||||||
|
|
||||||
if (SlaveGather == null)
|
|
||||||
{
|
|
||||||
SlaveGather = new CommunicationGather(SlaveClient, lineEnding);
|
|
||||||
SlaveGather.IncludeDelimiter = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
SlaveGather.LineReceived -= MasterGather_LineReceived;
|
|
||||||
SlaveGather.LineReceived += new EventHandler<GenericCommMethodReceiveTextArgs>(SlaveGather_LineReceived);
|
|
||||||
|
|
||||||
SlaveClient.ConnectionChange -= SlaveClient_SocketStatusChange;
|
|
||||||
SlaveClient.ConnectionChange += SlaveClient_SocketStatusChange;
|
|
||||||
|
|
||||||
MasterClient.Connect();
|
|
||||||
SlaveClient.Connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public void Disconnect()
|
|
||||||
{
|
|
||||||
if (MasterClient != null)
|
|
||||||
{
|
|
||||||
MasterGather.LineReceived -= MasterGather_LineReceived;
|
|
||||||
MasterClient.Disconnect();
|
|
||||||
}
|
|
||||||
if (SlaveClient != null)
|
|
||||||
{
|
|
||||||
SlaveGather.LineReceived -= SlaveGather_LineReceived;
|
|
||||||
SlaveClient.Disconnect();
|
|
||||||
}
|
|
||||||
if (PollTimer != null)
|
|
||||||
{
|
|
||||||
IsPolling = false;
|
|
||||||
|
|
||||||
PollTimer.Stop();
|
|
||||||
PollTimer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Does not include line feed
|
|
||||||
/// </summary>
|
|
||||||
public void SendText(string s)
|
|
||||||
{
|
|
||||||
if (SlaveIsActive)
|
|
||||||
{
|
|
||||||
if (SlaveClient != null)
|
|
||||||
{
|
|
||||||
Debug.Console(2, this, "Sending to Slave: {0}", s);
|
|
||||||
SlaveClient.SendText(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (MasterClient != null)
|
|
||||||
{
|
|
||||||
Debug.Console(2, this, "Sending to Master: {0}", s);
|
|
||||||
MasterClient.SendText(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MasterClient_SocketStatusChange(object sender, GenericSocketStatusChageEventArgs args)
|
|
||||||
{
|
|
||||||
OnUshortChange((ushort)args.Client.ClientStatus, MasterClientStatusId);
|
|
||||||
|
|
||||||
if (args.Client.IsConnected)
|
|
||||||
{
|
|
||||||
MasterGather.LineReceived += MasterGather_LineReceived;
|
|
||||||
|
|
||||||
StartPolling();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
MasterGather.LineReceived -= MasterGather_LineReceived;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SlaveClient_SocketStatusChange(object sender, GenericSocketStatusChageEventArgs args)
|
|
||||||
{
|
|
||||||
OnUshortChange((ushort)args.Client.ClientStatus, SlaveClientStatusId);
|
|
||||||
|
|
||||||
if (args.Client.IsConnected)
|
|
||||||
{
|
|
||||||
SlaveGather.LineReceived += SlaveGather_LineReceived;
|
|
||||||
|
|
||||||
StartPolling();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SlaveGather.LineReceived -= SlaveGather_LineReceived;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void MasterGather_LineReceived(object sender, GenericCommMethodReceiveTextArgs e)
|
|
||||||
{
|
|
||||||
if (e.Text.Contains("login_required"))
|
|
||||||
{
|
|
||||||
MasterClient.SendText(string.Format("login {0} {1} \x0d\x0a", Username, Password));
|
|
||||||
}
|
|
||||||
else if (e.Text.Contains("login_success"))
|
|
||||||
{
|
|
||||||
// START THE POLLING, YO!
|
|
||||||
}
|
|
||||||
else if (e.Text.StartsWith("sr"))
|
|
||||||
{
|
|
||||||
// example response "sr "MyDesign" "NIEC2bxnVZ6a" 1 1"
|
|
||||||
|
|
||||||
var split = e.Text.Trim().Split(' ');
|
|
||||||
if (split[split.Length - 1] == "1")
|
|
||||||
{
|
|
||||||
SlaveIsActive = false;
|
|
||||||
OnBoolChange(false, SlaveIsActiveId);
|
|
||||||
OnBoolChange(true, MasterIsActiveId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!SlaveIsActive)
|
|
||||||
OnStringChange(e.Text, LineReceivedId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SlaveGather_LineReceived(object sender, GenericCommMethodReceiveTextArgs e)
|
|
||||||
{
|
|
||||||
if (e.Text.Contains("login_required"))
|
|
||||||
{
|
|
||||||
SlaveClient.SendText(string.Format("login {0} {1} \x0d\x0a", Username, Password));
|
|
||||||
}
|
|
||||||
else if (e.Text.Contains("login_success"))
|
|
||||||
{
|
|
||||||
// START THE POLLING, YO!
|
|
||||||
}
|
|
||||||
else if (e.Text.StartsWith("sr"))
|
|
||||||
{
|
|
||||||
var split = e.Text.Trim().Split(' ');
|
|
||||||
if (split[split.Length - 1] == "1")
|
|
||||||
{
|
|
||||||
SlaveIsActive = true;
|
|
||||||
OnBoolChange(true, SlaveIsActiveId);
|
|
||||||
OnBoolChange(false, MasterIsActiveId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (SlaveIsActive)
|
|
||||||
OnStringChange(e.Text, LineReceivedId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StartPolling()
|
|
||||||
{
|
|
||||||
if (!IsPolling)
|
|
||||||
{
|
|
||||||
IsPolling = true;
|
|
||||||
|
|
||||||
Poll();
|
|
||||||
if (PollTimer != null)
|
|
||||||
PollTimer.Stop();
|
|
||||||
|
|
||||||
PollTimer = new CTimer(o => Poll(), null, PollingIntervalSeconds * 1000, PollingIntervalSeconds * 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Poll()
|
|
||||||
{
|
|
||||||
if (MasterClient != null && MasterClient.IsConnected)
|
|
||||||
{
|
|
||||||
Debug.Console(2, this, "Polling Master.");
|
|
||||||
MasterClient.SendText("sg\x0d\x0a");
|
|
||||||
|
|
||||||
}
|
|
||||||
if (SlaveClient != null && SlaveClient.IsConnected)
|
|
||||||
{
|
|
||||||
Debug.Console(2, this, "Polling Slave.");
|
|
||||||
SlaveClient.SendText("sg\x0d\x0a");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// login NAME PIN ---> login_success, login_failed
|
|
||||||
|
|
||||||
// status get
|
|
||||||
// sg --> sr DESIGN_NAME DESIGN_ID IS_PRIMARY IS_ACTIVE
|
|
||||||
|
|
||||||
// CRLF
|
|
||||||
|
|
||||||
void OnBoolChange(bool state, ushort type)
|
|
||||||
{
|
|
||||||
var handler = BoolChange;
|
|
||||||
if (handler != null)
|
|
||||||
handler(this, new BoolChangeEventArgs(state, type));
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnUshortChange(ushort state, ushort type)
|
|
||||||
{
|
|
||||||
var handler = UshortChange;
|
|
||||||
if (handler != null)
|
|
||||||
handler(this, new UshrtChangeEventArgs(state, type));
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnStringChange(string value, ushort type)
|
|
||||||
{
|
|
||||||
var handler = StringChange;
|
|
||||||
if (handler != null)
|
|
||||||
handler(this, new StringChangeEventArgs(value, type));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public const ushort MasterIsActiveId = 3;
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public const ushort SlaveIsActiveId = 4;
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public const ushort MainModuleInitiailzeId = 5;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public const ushort MasterClientStatusId = 101;
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public const ushort SlaveClientStatusId = 102;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public const ushort LineReceivedId = 201;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
//using System;
|
|
||||||
////using System.IO;
|
|
||||||
//using System.Collections.Generic;
|
|
||||||
//using System.Linq;
|
|
||||||
//using System.Text;
|
|
||||||
//using Crestron.SimplSharp;
|
|
||||||
//using Crestron.SimplSharp.CrestronIO;
|
|
||||||
//using Newtonsoft.Json;
|
|
||||||
//using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
//namespace PepperDash.Core.JsonToSimpl
|
|
||||||
//{
|
|
||||||
|
|
||||||
// public class JsonToSimplFixedPathObject : JsonToSimplChildObjectBase
|
|
||||||
// {
|
|
||||||
// public JsonToSimplFixedPathObject()
|
|
||||||
// {
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
@ -1,149 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
|
|
||||||
namespace PepperDash.Core.PasswordManagement
|
|
||||||
{
|
|
||||||
// Example JSON password array configuration object
|
|
||||||
//{
|
|
||||||
// "global":{
|
|
||||||
// "passwords":[
|
|
||||||
// {
|
|
||||||
// "key": "Password01",
|
|
||||||
// "name": "Technician Password",
|
|
||||||
// "enabled": true,
|
|
||||||
// "password": "1988"
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// JSON password array configuration object
|
|
||||||
/// </summary>
|
|
||||||
//public class PasswordConfig
|
|
||||||
//{
|
|
||||||
// /// <summary>
|
|
||||||
// /// Key used to search for object in JSON array
|
|
||||||
// /// </summary>
|
|
||||||
// public string key { get; set; }
|
|
||||||
// /// <summary>
|
|
||||||
// /// Friendly name of password object
|
|
||||||
// /// </summary>
|
|
||||||
// public string name { get; set; }
|
|
||||||
// /// <summary>
|
|
||||||
// /// Password object enabled
|
|
||||||
// /// </summary>
|
|
||||||
// public bool enabled { get; set; }
|
|
||||||
// /// <summary>
|
|
||||||
// ///
|
|
||||||
// /// </summary>
|
|
||||||
// public ushort simplEnabled
|
|
||||||
// {
|
|
||||||
// get { return (ushort)(enabled ? 1 : 0); }
|
|
||||||
// set { enabled = Convert.ToBoolean(value); }
|
|
||||||
// }
|
|
||||||
// /// <summary>
|
|
||||||
// /// Password object configured password
|
|
||||||
// /// </summary>
|
|
||||||
// public string password { get; set; }
|
|
||||||
// /// <summary>
|
|
||||||
// /// Password type
|
|
||||||
// /// </summary>
|
|
||||||
// private int type { get; set; }
|
|
||||||
// /// <summary>
|
|
||||||
// /// Password Type for S+
|
|
||||||
// /// </summary>
|
|
||||||
// public ushort simplType
|
|
||||||
// {
|
|
||||||
// get { return Convert.ToUInt16(type); }
|
|
||||||
// set { type = value; }
|
|
||||||
// }
|
|
||||||
// /// <summary>
|
|
||||||
// /// Password path
|
|
||||||
// /// **FUTURE** implementation of saving passwords recieved from Fusion or other external sources back to config
|
|
||||||
// /// </summary>
|
|
||||||
// public string path { get; set; }
|
|
||||||
// /// <summary>
|
|
||||||
// /// Constructor
|
|
||||||
// /// </summary>
|
|
||||||
// public PasswordConfig()
|
|
||||||
// {
|
|
||||||
// simplEnabled = 0;
|
|
||||||
// simplType = 0;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Example JSON password collections configuration object
|
|
||||||
//{
|
|
||||||
// "global": {
|
|
||||||
// "passwords": {
|
|
||||||
// "1": {
|
|
||||||
// "name": "Technician Password",
|
|
||||||
// "password": "2468"
|
|
||||||
// },
|
|
||||||
// "2": {
|
|
||||||
// "name": "System Password",
|
|
||||||
// "password": "123456"
|
|
||||||
// },
|
|
||||||
// "3": {
|
|
||||||
// "name": "Master Password",
|
|
||||||
// "password": "abc123"
|
|
||||||
// },
|
|
||||||
// "5": {
|
|
||||||
// "name": "Backdoor Password",
|
|
||||||
// "password": "1988"
|
|
||||||
// },
|
|
||||||
// "10": {
|
|
||||||
// "name": "Backdoor Password",
|
|
||||||
// "password": "1988"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// JSON password array configuration object
|
|
||||||
/// </summary>
|
|
||||||
public class PasswordConfig
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Password object configured password
|
|
||||||
/// </summary>
|
|
||||||
public string password { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor
|
|
||||||
/// </summary>
|
|
||||||
public PasswordConfig()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Global JSON object
|
|
||||||
/// </summary>
|
|
||||||
//public class GlobalConfig
|
|
||||||
//{
|
|
||||||
// //public List<PasswordConfig> passwords { get; set; }
|
|
||||||
// public Dictionary<uint, PasswordConfig> passwords { get; set; }
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Constructor
|
|
||||||
// /// </summary>
|
|
||||||
// public GlobalConfig()
|
|
||||||
// {
|
|
||||||
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Root JSON object
|
|
||||||
/// </summary>
|
|
||||||
//public class RootObject
|
|
||||||
//{
|
|
||||||
// public GlobalConfig global { get; set; }
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
@ -1,207 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
|
|
||||||
namespace PepperDash.Core.PasswordManagement
|
|
||||||
{
|
|
||||||
public class PasswordClient
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Password Client
|
|
||||||
/// </summary>
|
|
||||||
public PasswordConfig Client { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Used to build the password entered by the user
|
|
||||||
/// </summary>
|
|
||||||
public string PasswordToValidate { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Boolean event
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<BoolChangeEventArgs> BoolChange;
|
|
||||||
/// <summary>
|
|
||||||
/// Ushort event
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<UshrtChangeEventArgs> UshrtChange;
|
|
||||||
/// <summary>
|
|
||||||
/// String event
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<StringChangeEventArgs> StringChange;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor
|
|
||||||
/// </summary>
|
|
||||||
public PasswordClient()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialize method
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
public void Initialize(string key)
|
|
||||||
{
|
|
||||||
OnBoolChange(false, 0, PasswordManagementConstants.BoolEvaluatedChange);
|
|
||||||
|
|
||||||
Client = new PasswordConfig();
|
|
||||||
PasswordToValidate = "";
|
|
||||||
|
|
||||||
// there has to be a better way to get the index of the current index of password
|
|
||||||
ushort i = 0;
|
|
||||||
foreach (var password in PasswordManager.Passwords)
|
|
||||||
{
|
|
||||||
i++;
|
|
||||||
OnUshrtChange((ushort)password.Key, (ushort)password.Key, PasswordManagementConstants.PasswordKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
OnBoolChange(true, 0, PasswordManagementConstants.BoolEvaluatedChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves password by key
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
//public void GetPasswordByKey(string key)
|
|
||||||
//{
|
|
||||||
// if (string.IsNullOrEmpty(key))
|
|
||||||
// {
|
|
||||||
// Debug.Console(1, "PassowrdClient.GetPasswordByKey failed:\rKey {0} is null or empty", key);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// PasswordConfig password = PasswordManager.Passwords.FirstOrDefault(p => p.key.Equals(key));
|
|
||||||
// if (password == null)
|
|
||||||
// {
|
|
||||||
// OnUshrtChange(0, 0, PasswordManagementConstants.SelectedPasswordLength);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Client = password;
|
|
||||||
// OnUshrtChange((ushort)Client.password.Length, 0, PasswordManagementConstants.SelectedPasswordLength);
|
|
||||||
// OnStringChange(Client.key, 0, PasswordManagementConstants.PasswordKeySelected);
|
|
||||||
//}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieve password by index
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index"></param>
|
|
||||||
public void GetPasswordByIndex(ushort key)
|
|
||||||
{
|
|
||||||
PasswordConfig pw = PasswordManager.Passwords[key];
|
|
||||||
if (pw == null)
|
|
||||||
{
|
|
||||||
OnUshrtChange(0, 0, PasswordManagementConstants.SelectedPasswordLength);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Client = pw;
|
|
||||||
OnUshrtChange((ushort)Client.password.Length, 0, PasswordManagementConstants.SelectedPasswordLength);
|
|
||||||
OnUshrtChange(key, 0, PasswordManagementConstants.PasswordKeySelected);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Password validation method
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="password"></param>
|
|
||||||
public void ValidatePassword(string password)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(password))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (string.Equals(Client.password, password))
|
|
||||||
{
|
|
||||||
OnBoolChange(true, 0, PasswordManagementConstants.PasswordIsValid);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OnBoolChange(true, 0, PasswordManagementConstants.PasswordIsInvalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
OnBoolChange(false, 0, PasswordManagementConstants.PasswordIsValid);
|
|
||||||
OnBoolChange(false, 0, PasswordManagementConstants.PasswordIsInvalid);
|
|
||||||
|
|
||||||
ClearPassword();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Builds the user entered passwrod string, will attempt to validate the user entered
|
|
||||||
/// password against the selected password when the length of the 2 are equal
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data"></param>
|
|
||||||
public void BuildPassword(string data)
|
|
||||||
{
|
|
||||||
PasswordToValidate = String.Concat(PasswordToValidate, data);
|
|
||||||
OnBoolChange(true, (ushort)PasswordToValidate.Length, PasswordManagementConstants.PasswordLedChange);
|
|
||||||
|
|
||||||
if (PasswordToValidate.Length == Client.password.Length)
|
|
||||||
ValidatePassword(PasswordToValidate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clears the user entered password and resets the LEDs
|
|
||||||
/// </summary>
|
|
||||||
public void ClearPassword()
|
|
||||||
{
|
|
||||||
PasswordToValidate = "";
|
|
||||||
OnBoolChange(true, (ushort)PasswordToValidate.Length, PasswordManagementConstants.PasswordLedChange);
|
|
||||||
|
|
||||||
for(var i = 1; i <= Client.password.Length; i++)
|
|
||||||
OnBoolChange(false, (ushort)i, PasswordManagementConstants.PasswordLedChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Protected boolean change event handler
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="state"></param>
|
|
||||||
/// <param name="index"></param>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
protected void OnBoolChange(bool state, ushort index, ushort type)
|
|
||||||
{
|
|
||||||
var handler = BoolChange;
|
|
||||||
if (handler != null)
|
|
||||||
{
|
|
||||||
var args = new BoolChangeEventArgs(state, type);
|
|
||||||
args.Index = index;
|
|
||||||
BoolChange(this, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Protected ushort change event handler
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="index"></param>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
protected void OnUshrtChange(ushort value, ushort index, ushort type)
|
|
||||||
{
|
|
||||||
var handler = UshrtChange;
|
|
||||||
if (handler != null)
|
|
||||||
{
|
|
||||||
var args = new UshrtChangeEventArgs(value, type);
|
|
||||||
args.Index = index;
|
|
||||||
UshrtChange(this, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Protected string change event handler
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="index"></param>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
protected void OnStringChange(string value, ushort index, ushort type)
|
|
||||||
{
|
|
||||||
var handler = StringChange;
|
|
||||||
if (handler != null)
|
|
||||||
{
|
|
||||||
var args = new StringChangeEventArgs(value, type);
|
|
||||||
args.Index = index;
|
|
||||||
StringChange(this, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,233 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
using PepperDash.Core.JsonToSimpl;
|
|
||||||
using PepperDash.Core.JsonStandardObjects;
|
|
||||||
|
|
||||||
namespace PepperDash.Core.PasswordManagement
|
|
||||||
{
|
|
||||||
public class PasswordManager
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// List of passwords configured
|
|
||||||
/// </summary>
|
|
||||||
public static Dictionary<uint, PasswordConfig> Passwords = new Dictionary<uint, PasswordConfig>();
|
|
||||||
private Dictionary<uint, PasswordConfig> TempPasswords = new Dictionary<uint, PasswordConfig>();
|
|
||||||
|
|
||||||
CTimer UpdateTimer;
|
|
||||||
public long UpdateTimerElapsedMs = 5000;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Boolean event
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<BoolChangeEventArgs> BoolChange;
|
|
||||||
/// <summary>
|
|
||||||
/// Ushort event
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<UshrtChangeEventArgs> UshrtChange;
|
|
||||||
/// <summary>
|
|
||||||
/// String event
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<StringChangeEventArgs> StringChange;
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor
|
|
||||||
/// </summary>
|
|
||||||
public PasswordManager()
|
|
||||||
{
|
|
||||||
Passwords.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialize method
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="uniqueId"></param>
|
|
||||||
//public void Initialize(string uniqueId, string key)
|
|
||||||
//{
|
|
||||||
// OnBoolChange(false, 0, PasswordManagementConstants.BoolEvaluatedChange);
|
|
||||||
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// if(string.IsNullOrEmpty(uniqueId) || string.IsNullOrEmpty(key))
|
|
||||||
// {
|
|
||||||
// Debug.Console(1, "PasswordManager.Initialize({0}, {1}) null or empty parameters", uniqueId, key);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// JsonToSimplMaster master = J2SGlobal.GetMasterByFile(uniqueId);
|
|
||||||
// if(master == null)
|
|
||||||
// {
|
|
||||||
// Debug.Console(1, "PassowrdManager.Initialize failed:\rCould not find JSON file with uniqueID {0}", uniqueId);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var global = master.JsonObject.ToObject<RootObject>().global;
|
|
||||||
// var passwords = global.passwords;
|
|
||||||
// if(passwords == null)
|
|
||||||
// {
|
|
||||||
// Debug.Console(1, "PasswordManager.Initialize failed:\rCould not find password object");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// foreach(var password in passwords)
|
|
||||||
// {
|
|
||||||
// if (password != null)
|
|
||||||
// {
|
|
||||||
// var index = passwords.IndexOf(password);
|
|
||||||
|
|
||||||
// password.path = string.Format("global.passwords[{0}]", index);
|
|
||||||
// Debug.Console(1, "PasswordManager.Initialize: {0}, {1}, {2}, {3}, {4}, {5}", password.key, password.name, password.simplEnabled, password.simplType, password.password, password.path);
|
|
||||||
// //AddPassword(password);
|
|
||||||
|
|
||||||
// OnStringChange(password.path, (ushort)index, PasswordManagementConstants.FullPathToPassword);
|
|
||||||
// OnStringChange(password.key, (ushort)index, PasswordManagementConstants.PasswordKey);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// OnUshrtChange(Convert.ToUInt16(Passwords.Count), 0, PasswordManagementConstants.PasswordListCount);
|
|
||||||
// }
|
|
||||||
// catch(Exception e)
|
|
||||||
// {
|
|
||||||
// var msg = string.Format("PasswordManager.Initialize({0}, {1}) failed:\r{2}", uniqueId, key, e.Message);
|
|
||||||
// CrestronConsole.PrintLine(msg);
|
|
||||||
// ErrorLog.Error(msg);
|
|
||||||
// }
|
|
||||||
// finally
|
|
||||||
// {
|
|
||||||
// OnBoolChange(true, 0, PasswordManagementConstants.BoolEvaluatedChange);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds password to the list
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="password"></param>
|
|
||||||
//private void AddPassword(PasswordConfig password)
|
|
||||||
//{
|
|
||||||
// if (password == null)
|
|
||||||
// return;
|
|
||||||
|
|
||||||
// var item = Passwords.FirstOrDefault(i => i.key.Equals(password.key));
|
|
||||||
// if (item != null)
|
|
||||||
// Passwords.Remove(item);
|
|
||||||
// Passwords.Add(password);
|
|
||||||
|
|
||||||
// Passwords.Sort((x, y) => string.Compare(x.key, y.key));
|
|
||||||
//}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes password from the list
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="password"></param>
|
|
||||||
//private void RemovePassword(PasswordConfig password)
|
|
||||||
//{
|
|
||||||
// if (password == null)
|
|
||||||
// return;
|
|
||||||
|
|
||||||
// var item = Passwords.FirstOrDefault(i => i.key.Equals(password.key));
|
|
||||||
// if (item != null)
|
|
||||||
// Passwords.Remove(item);
|
|
||||||
//}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates password stored in the dictonary
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uniqueId"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="password"></param>
|
|
||||||
public void UpdatePassword(ushort key, string password)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(password))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var pw = TempPasswords[key];
|
|
||||||
if (pw == null)
|
|
||||||
{
|
|
||||||
pw = new PasswordConfig();
|
|
||||||
}
|
|
||||||
pw.password = password;
|
|
||||||
|
|
||||||
if (UpdateTimer == null)
|
|
||||||
{
|
|
||||||
// (o) => SavePasswords removes the need to create a callback method that takes in an object
|
|
||||||
UpdateTimer = new CTimer((o) => StorePassword(), UpdateTimerElapsedMs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UpdateTimer.Reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stores the updated passwords in TempPassword in the Passwords dictionary
|
|
||||||
/// </summary>
|
|
||||||
private void StorePassword()
|
|
||||||
{
|
|
||||||
UpdateTimer.Stop();
|
|
||||||
|
|
||||||
foreach (var tempPw in TempPasswords)
|
|
||||||
{
|
|
||||||
Passwords[tempPw.Key] = tempPw.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
TempPasswords.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Protected boolean change event handler
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="state"></param>
|
|
||||||
/// <param name="index"></param>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
protected void OnBoolChange(bool state, ushort index, ushort type)
|
|
||||||
{
|
|
||||||
var handler = BoolChange;
|
|
||||||
if (handler != null)
|
|
||||||
{
|
|
||||||
var args = new BoolChangeEventArgs(state, type);
|
|
||||||
args.Index = index;
|
|
||||||
BoolChange(this, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Protected ushort change event handler
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="state"></param>
|
|
||||||
/// <param name="index"></param>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
protected void OnUshrtChange(ushort value, ushort index, ushort type)
|
|
||||||
{
|
|
||||||
var handler = UshrtChange;
|
|
||||||
if (handler != null)
|
|
||||||
{
|
|
||||||
var args = new UshrtChangeEventArgs(value, type);
|
|
||||||
args.Index = index;
|
|
||||||
UshrtChange(this, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Protected string change event handler
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="index"></param>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
protected void OnStringChange(string value, ushort index, ushort type)
|
|
||||||
{
|
|
||||||
var handler = StringChange;
|
|
||||||
if (handler != null)
|
|
||||||
{
|
|
||||||
var args = new StringChangeEventArgs(value, type);
|
|
||||||
args.Index = index;
|
|
||||||
StringChange(this, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,156 +0,0 @@
|
||||||
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup>
|
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
|
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProductVersion>9.0.30729</ProductVersion>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<ProjectGuid>{87E29B4C-569B-4368-A4ED-984AC1440C96}</ProjectGuid>
|
|
||||||
<OutputType>Library</OutputType>
|
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
|
||||||
<RootNamespace>PepperDash.Core</RootNamespace>
|
|
||||||
<AssemblyName>PepperDash_Core</AssemblyName>
|
|
||||||
<ProjectTypeGuids>{0B4745B0-194B-4BB6-8E21-E9057CA92500};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
|
||||||
<PlatformFamilyName>WindowsCE</PlatformFamilyName>
|
|
||||||
<PlatformID>E2BECB1F-8C8C-41ba-B736-9BE7D946A398</PlatformID>
|
|
||||||
<OSVersion>5.0</OSVersion>
|
|
||||||
<DeployDirSuffix>SmartDeviceProject1</DeployDirSuffix>
|
|
||||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
|
||||||
<NativePlatformName>Windows CE</NativePlatformName>
|
|
||||||
<FormFactorID>
|
|
||||||
</FormFactorID>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
|
||||||
<AllowedReferenceRelatedFileExtensions>.allowedReferenceRelatedFileExtensions</AllowedReferenceRelatedFileExtensions>
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;TRACE;</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<FileAlignment>512</FileAlignment>
|
|
||||||
<NoStdLib>true</NoStdLib>
|
|
||||||
<NoConfig>true</NoConfig>
|
|
||||||
<DocumentationFile>bin\PepperDash_Core.xml</DocumentationFile>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
|
||||||
<AllowedReferenceRelatedFileExtensions>.allowedReferenceRelatedFileExtensions</AllowedReferenceRelatedFileExtensions>
|
|
||||||
<DebugType>none</DebugType>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<OutputPath>bin\</OutputPath>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<FileAlignment>512</FileAlignment>
|
|
||||||
<NoStdLib>true</NoStdLib>
|
|
||||||
<NoConfig>true</NoConfig>
|
|
||||||
<DocumentationFile>bin\PepperDash_Core.xml</DocumentationFile>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="mscorlib" />
|
|
||||||
<Reference Include="SimplSharpCustomAttributesInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
|
|
||||||
<SpecificVersion>False</SpecificVersion>
|
|
||||||
<HintPath>..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="SimplSharpCWSHelperInterface, Version=2.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
|
|
||||||
<SpecificVersion>False</SpecificVersion>
|
|
||||||
<HintPath>..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCWSHelperInterface.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="SimplSharpHelperInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
|
|
||||||
<SpecificVersion>False</SpecificVersion>
|
|
||||||
<HintPath>..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="SimplSharpNewtonsoft, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
|
|
||||||
<SpecificVersion>False</SpecificVersion>
|
|
||||||
<HintPath>..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="SimplSharpReflectionInterface, Version=1.0.5583.25238, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
|
|
||||||
<SpecificVersion>False</SpecificVersion>
|
|
||||||
<HintPath>..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System" />
|
|
||||||
<Reference Include="System.Core" />
|
|
||||||
<Reference Include="System.Data" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Include="CommunicationExtras.cs" />
|
|
||||||
<Compile Include="Comm\CommunicationStreamDebugging.cs" />
|
|
||||||
<Compile Include="Comm\ControlPropertiesConfig.cs" />
|
|
||||||
<Compile Include="Comm\GenericSecureTcpIpClient.cs" />
|
|
||||||
<Compile Include="Comm\GenericTcpIpClient_ForServer.cs" />
|
|
||||||
<Compile Include="Comm\GenericHttpSseClient.cs" />
|
|
||||||
<Compile Include="Comm\GenericSecureTcpIpServer.cs" />
|
|
||||||
<Compile Include="Comm\GenericSecureTcpIpClient_ForServer.cs">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Comm\eControlMethods.cs" />
|
|
||||||
<Compile Include="Comm\FINISH CommStatic.cs" />
|
|
||||||
<Compile Include="Comm\CommunicationGather.cs" />
|
|
||||||
<Compile Include="Comm\EventArgs.cs" />
|
|
||||||
<Compile Include="Comm\GenericSshClient.cs" />
|
|
||||||
<Compile Include="Comm\GenericUdpServer.cs" />
|
|
||||||
<Compile Include="Comm\QscCoreDoubleTcpIpClient.cs" />
|
|
||||||
<Compile Include="Comm\TcpClientConfigObject.cs" />
|
|
||||||
<Compile Include="Comm\TcpServerConfigObject.cs" />
|
|
||||||
<Compile Include="Config\PortalConfigReader.cs" />
|
|
||||||
<Compile Include="CoreInterfaces.cs" />
|
|
||||||
<Compile Include="Web\RequestHandlers\DefaultRequestHandler.cs" />
|
|
||||||
<Compile Include="Web\RequestHandlers\WebApiBaseRequestHandler.cs" />
|
|
||||||
<Compile Include="Web\WebApiServer.cs" />
|
|
||||||
<Compile Include="EventArgs.cs" />
|
|
||||||
<Compile Include="GenericRESTfulCommunications\Constants.cs" />
|
|
||||||
<Compile Include="GenericRESTfulCommunications\GenericRESTfulClient.cs" />
|
|
||||||
<Compile Include="JsonStandardObjects\EventArgs and Constants.cs" />
|
|
||||||
<Compile Include="JsonStandardObjects\JsonToSimplDeviceConfig.cs" />
|
|
||||||
<Compile Include="JsonStandardObjects\JsonToSimplDevice.cs" />
|
|
||||||
<Compile Include="JsonToSimpl\JsonToSimplPortalFileMaster.cs" />
|
|
||||||
<Compile Include="Logging\Debug.cs" />
|
|
||||||
<Compile Include="Logging\DebugContext.cs" />
|
|
||||||
<Compile Include="Logging\DebugMemory.cs" />
|
|
||||||
<Compile Include="Device.cs" />
|
|
||||||
<Compile Include="Comm\GenericTcpIpServer.cs" />
|
|
||||||
<Compile Include="EthernetHelper.cs" />
|
|
||||||
<Compile Include="Comm\GenericTcpIpClient.cs" />
|
|
||||||
<Compile Include="JsonToSimpl\Constants.cs" />
|
|
||||||
<Compile Include="JsonToSimpl\Global.cs" />
|
|
||||||
<Compile Include="JsonToSimpl\JsonToSimplArrayLookupChild.cs" />
|
|
||||||
<Compile Include="JsonToSimpl\JsonToSimplChildObjectBase.cs" />
|
|
||||||
<Compile Include="JsonToSimpl\JsonToSimplFileMaster.cs" />
|
|
||||||
<Compile Include="JsonToSimpl\JsonToSimplFixedPathObject.cs" />
|
|
||||||
<Compile Include="JsonToSimpl\REMOVE JsonToSimplFixedPathObject.cs" />
|
|
||||||
<Compile Include="JsonToSimpl\JsonToSimplGenericMaster.cs" />
|
|
||||||
<Compile Include="JsonToSimpl\JsonToSimplMaster.cs" />
|
|
||||||
<Compile Include="Network\DiscoveryThings.cs" />
|
|
||||||
<Compile Include="PasswordManagement\Config.cs" />
|
|
||||||
<Compile Include="PasswordManagement\Constants.cs" />
|
|
||||||
<Compile Include="PasswordManagement\PasswordClient.cs" />
|
|
||||||
<Compile Include="PasswordManagement\PasswordManager.cs" />
|
|
||||||
<Compile Include="SystemInfo\EventArgs and Constants.cs" />
|
|
||||||
<Compile Include="SystemInfo\SystemInfoConfig.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
|
||||||
<Compile Include="SystemInfo\SystemInfoToSimpl.cs" />
|
|
||||||
<Compile Include="WebApi\Presets\Preset.cs" />
|
|
||||||
<Compile Include="WebApi\Presets\User.cs" />
|
|
||||||
<Compile Include="WebApi\Presets\WebApiPasscodeClient.cs" />
|
|
||||||
<Compile Include="XSigUtility\Serialization\IXSigSerialization.cs" />
|
|
||||||
<Compile Include="XSigUtility\Serialization\XSigSerializationException.cs" />
|
|
||||||
<Compile Include="XSigUtility\Tokens\XSigAnalogToken.cs" />
|
|
||||||
<Compile Include="XSigUtility\Tokens\XSigDigitalToken.cs" />
|
|
||||||
<Compile Include="XSigUtility\Tokens\XSigSerialToken.cs" />
|
|
||||||
<Compile Include="XSigUtility\Tokens\XSigToken.cs" />
|
|
||||||
<Compile Include="XSigUtility\Tokens\XSigTokenType.cs" />
|
|
||||||
<Compile Include="XSigUtility\XSigHelpers.cs" />
|
|
||||||
<Compile Include="XSigUtility\XSigTokenStreamReader.cs" />
|
|
||||||
<Compile Include="XSigUtility\XSigTokenStreamWriter.cs" />
|
|
||||||
<None Include="Properties\ControlSystem.cfg" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildBinPath)\Microsoft.CompactFramework.CSharp.targets" />
|
|
||||||
<ProjectExtensions>
|
|
||||||
<VisualStudio>
|
|
||||||
</VisualStudio>
|
|
||||||
</ProjectExtensions>
|
|
||||||
<PropertyGroup>
|
|
||||||
<PostBuildEvent>rem S# preparation will execute after these operations</PostBuildEvent>
|
|
||||||
<PreBuildEvent>del "$(TargetDir)PepperDash_Core.*" /q
|
|
||||||
</PreBuildEvent>
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=CrestronWebServer/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=Web/@EntryIndexedValue">False</s:Boolean></wpf:ResourceDictionary>
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<package >
|
|
||||||
<metadata>
|
|
||||||
<id>PepperDashCore</id>
|
|
||||||
<version>1.0.37</version>
|
|
||||||
<title>PepperDash Core</title>
|
|
||||||
<authors>PepperDash Technologies</authors>
|
|
||||||
<owners>pepperdash</owners>
|
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
|
||||||
<license type="expression">MIT</license>
|
|
||||||
<projectUrl>https://github.com/PepperDash/PepperDashCore</projectUrl>
|
|
||||||
<copyright>Copyright 2020</copyright>
|
|
||||||
<description>PepperDash Core is an open source Crestron SIMPL# library that can be used in SIMPL# Pro applications such as Essentials or as a standalone library with SIMPL+ wrappers to expose functionality in SIMPL Windows programs.</description>
|
|
||||||
<tags>crestron 3series 4series</tags>
|
|
||||||
<repository type="git" url="https://github.com/PepperDash/PepperDashCore"/>
|
|
||||||
</metadata>
|
|
||||||
<files>
|
|
||||||
<file src="**" target="lib\net35"/>
|
|
||||||
<file src="**" target="lib\net47"/>
|
|
||||||
</files>
|
|
||||||
</package>
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
[assembly: System.Reflection.AssemblyTitle("Pepperdash_Core")]
|
|
||||||
[assembly: System.Reflection.AssemblyCompany("PepperDash Technology Corp")]
|
|
||||||
[assembly: System.Reflection.AssemblyProduct("Pepperdash_Core")]
|
|
||||||
[assembly: System.Reflection.AssemblyCopyright("Copyright © PepperDash 2019")]
|
|
||||||
[assembly: System.Reflection.AssemblyVersion("1.0.0.*")]
|
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersion("0.0.0-buildType-buildNumber")]
|
|
||||||
[assembly: Crestron.SimplSharp.Reflection.AssemblyInformationalVersion("0.0.0-buildType-buildNumber")]
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
[assembly: AssemblyTitle("Pepperdash_Core")]
|
|
||||||
[assembly: AssemblyCompany("")]
|
|
||||||
[assembly: AssemblyProduct("Pepperdash_Core")]
|
|
||||||
[assembly: AssemblyCopyright("Copyright © PepperDash 2016")]
|
|
||||||
<<<<<<< HEAD
|
|
||||||
[assembly: AssemblyVersion("1.0.2.*")]
|
|
||||||
=======
|
|
||||||
[assembly: AssemblyVersion("1.0.3.*")]
|
|
||||||
>>>>>>> e16c2cbee87c631bf84ad9634bfbc25bbec78a31
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
function Update-SourceVersion
|
|
||||||
{
|
|
||||||
Param ([string]$Version)
|
|
||||||
$NewVersion = ‘AssemblyVersion("‘ + $Version + ‘.*")’;
|
|
||||||
foreach ($o in $input)
|
|
||||||
{
|
|
||||||
Write-output $o.FullName
|
|
||||||
$TmpFile = $o.FullName + “.tmp”
|
|
||||||
get-content $o.FullName |
|
|
||||||
%{$_ -replace ‘AssemblyVersion\("(\d+\.\d+\.\d+)\.\*"\)’, $NewVersion } > $TmpFile
|
|
||||||
move-item $TmpFile $o.FullName -force
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Update-AllAssemblyInfoFiles ( $version )
|
|
||||||
{
|
|
||||||
foreach ($file in “AssemblyInfo.cs”, “AssemblyInfo.vb” )
|
|
||||||
{
|
|
||||||
get-childitem -recurse |? {$_.Name -eq $file} | Update-SourceVersion $version ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# validate arguments
|
|
||||||
$r= [System.Text.RegularExpressions.Regex]::Match($args[0], "^\d+\.\d+\.\d+$");
|
|
||||||
if ($r.Success)
|
|
||||||
{
|
|
||||||
echo "Updating Assembly Version...";
|
|
||||||
Update-AllAssemblyInfoFiles $args[0];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
echo ” “;
|
|
||||||
echo “Error: Input version does not match x.y.z format!”
|
|
||||||
echo ” “;
|
|
||||||
echo "Unable to apply version to AssemblyInfo.cs files";
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
|
|
||||||
namespace PepperDash.Core
|
namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
|
|
@ -15,38 +13,44 @@ namespace PepperDash.Core
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The method of control
|
/// The method of control
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("method")]
|
||||||
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
public eControlMethod Method { get; set; }
|
public eControlMethod Method { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The key of the device that contains the control port
|
/// The key of the device that contains the control port
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("controlPortDevKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
public string ControlPortDevKey { get; set; }
|
public string ControlPortDevKey { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The number of the control port on the device specified by ControlPortDevKey
|
/// The number of the control port on the device specified by ControlPortDevKey
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] // In case "null" is present in config on this value
|
[JsonProperty("controlPortNumber", NullValueHandling = NullValueHandling.Ignore)] // In case "null" is present in config on this value
|
||||||
public uint ControlPortNumber { get; set; }
|
public uint? ControlPortNumber { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the control port on the device specified by ControlPortDevKey
|
/// The name of the control port on the device specified by ControlPortDevKey
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] // In case "null" is present in config on this value
|
[JsonProperty("controlPortName", NullValueHandling = NullValueHandling.Ignore)] // In case "null" is present in config on this value
|
||||||
public string ControlPortName { get; set; }
|
public string ControlPortName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Properties for ethernet based communications
|
/// Properties for ethernet based communications
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("tcpSshProperties", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
public TcpSshPropertiesConfig TcpSshProperties { get; set; }
|
public TcpSshPropertiesConfig TcpSshProperties { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The filename and path for the IR file
|
/// The filename and path for the IR file
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("irFile", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
public string IrFile { get; set; }
|
public string IrFile { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The IpId of a Crestron device
|
/// The IpId of a Crestron device
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("ipId", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
public string IpId { get; set; }
|
public string IpId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -58,21 +62,25 @@ namespace PepperDash.Core
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Char indicating end of line
|
/// Char indicating end of line
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("endOfLineChar", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
public char EndOfLineChar { get; set; }
|
public char EndOfLineChar { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defaults to Environment.NewLine;
|
/// Defaults to Environment.NewLine;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("endOfLineString", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
public string EndOfLineString { get; set; }
|
public string EndOfLineString { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates
|
/// Indicates
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("deviceReadyResponsePattern", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
public string DeviceReadyResponsePattern { get; set; }
|
public string DeviceReadyResponsePattern { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used when communcating to programs running in VC-4
|
/// Used when communcating to programs running in VC-4
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("roomId", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
public string RoomId { get; set; }
|
public string RoomId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -80,7 +88,6 @@ namespace PepperDash.Core
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ControlPropertiesConfig()
|
public ControlPropertiesConfig()
|
||||||
{
|
{
|
||||||
EndOfLineString = CrestronEnvironment.NewLine;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -15,8 +15,6 @@ using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Crestron.SimplSharp.CrestronSockets;
|
using Crestron.SimplSharp.CrestronSockets;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
namespace PepperDash.Core
|
namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
|
|
@ -1,148 +1,151 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Crestron.SimplSharp.CrestronSockets;
|
using Crestron.SimplSharp.CrestronSockets;
|
||||||
using Crestron.SimplSharp.Ssh;
|
using Org.BouncyCastle.Utilities;
|
||||||
using Crestron.SimplSharp.Ssh.Common;
|
using PepperDash.Core.Logging;
|
||||||
|
using Renci.SshNet;
|
||||||
|
using Renci.SshNet.Common;
|
||||||
|
|
||||||
namespace PepperDash.Core
|
namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
|
public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
|
||||||
{
|
{
|
||||||
private const string SPlusKey = "Uninitialized SshClient";
|
private const string SPlusKey = "Uninitialized SshClient";
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Object to enable stream debugging
|
/// Object to enable stream debugging
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public CommunicationStreamDebugging StreamDebugging { get; private set; }
|
public CommunicationStreamDebugging StreamDebugging { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event that fires when data is received. Delivers args with byte array
|
/// Event that fires when data is received. Delivers args with byte array
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
|
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event that fires when data is received. Delivered as text.
|
/// Event that fires when data is received. Delivered as text.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
|
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event when the connection status changes.
|
/// Event when the connection status changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
|
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
|
||||||
|
|
||||||
///// <summary>
|
///// <summary>
|
||||||
/////
|
/////
|
||||||
///// </summary>
|
///// </summary>
|
||||||
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
|
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Address of server
|
/// Address of server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Hostname { get; set; }
|
public string Hostname { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Port on server
|
/// Port on server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Username for server
|
/// Username for server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// And... Password for server. That was worth documenting!
|
/// And... Password for server. That was worth documenting!
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// True when the server is connected - when status == 2.
|
/// True when the server is connected - when status == 2.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsConnected
|
public bool IsConnected
|
||||||
{
|
{
|
||||||
// returns false if no client or not connected
|
// 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>
|
/// <summary>
|
||||||
/// S+ helper for IsConnected
|
/// S+ helper for IsConnected
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort UIsConnected
|
public ushort UIsConnected
|
||||||
{
|
{
|
||||||
get { return (ushort)(IsConnected ? 1 : 0); }
|
get { return (ushort)(IsConnected ? 1 : 0); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SocketStatus ClientStatus
|
public SocketStatus ClientStatus
|
||||||
{
|
{
|
||||||
get { return _ClientStatus; }
|
get { return _ClientStatus; }
|
||||||
private set
|
private set
|
||||||
{
|
{
|
||||||
if (_ClientStatus == value)
|
if (_ClientStatus == value)
|
||||||
return;
|
return;
|
||||||
_ClientStatus = value;
|
_ClientStatus = value;
|
||||||
OnConnectionChange();
|
OnConnectionChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SocketStatus _ClientStatus;
|
SocketStatus _ClientStatus;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
|
/// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
|
||||||
/// and IsConnected with be true when this == 2.
|
/// and IsConnected with be true when this == 2.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort UStatus
|
public ushort UStatus
|
||||||
{
|
{
|
||||||
get { return (ushort)_ClientStatus; }
|
get { return (ushort)_ClientStatus; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether client will attempt reconnection on failure. Default is true
|
/// Determines whether client will attempt reconnection on failure. Default is true
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AutoReconnect { get; set; }
|
public bool AutoReconnect { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Will be set and unset by connect and disconnect only
|
/// Will be set and unset by connect and disconnect only
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool ConnectEnabled { get; private set; }
|
public bool ConnectEnabled { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// S+ helper for AutoReconnect
|
/// S+ helper for AutoReconnect
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort UAutoReconnect
|
public ushort UAutoReconnect
|
||||||
{
|
{
|
||||||
get { return (ushort)(AutoReconnect ? 1 : 0); }
|
get { return (ushort)(AutoReconnect ? 1 : 0); }
|
||||||
set { AutoReconnect = value == 1; }
|
set { AutoReconnect = value == 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Millisecond value, determines the timeout period in between reconnect attempts.
|
/// Millisecond value, determines the timeout period in between reconnect attempts.
|
||||||
/// Set to 5000 by default
|
/// Set to 5000 by default
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int AutoReconnectIntervalMs { get; set; }
|
public int AutoReconnectIntervalMs { get; set; }
|
||||||
|
|
||||||
SshClient Client;
|
SshClient Client;
|
||||||
|
|
||||||
ShellStream TheStream;
|
ShellStream TheStream;
|
||||||
|
|
||||||
CTimer ReconnectTimer;
|
CTimer ReconnectTimer;
|
||||||
|
|
||||||
//Lock object to prevent simulatneous connect/disconnect operations
|
//Lock object to prevent simulatneous connect/disconnect operations
|
||||||
private CCriticalSection connectLock = new CCriticalSection();
|
//private CCriticalSection connectLock = new CCriticalSection();
|
||||||
|
private SemaphoreSlim connectLock = new SemaphoreSlim(1);
|
||||||
|
|
||||||
private bool DisconnectLogged = false;
|
private bool DisconnectLogged = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Typical constructor.
|
/// Typical constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public GenericSshClient(string key, string hostname, int port, string username, string password) :
|
public GenericSshClient(string key, string hostname, int port, string username, string password) :
|
||||||
base(key)
|
base(key)
|
||||||
{
|
{
|
||||||
StreamDebugging = new CommunicationStreamDebugging(key);
|
StreamDebugging = new CommunicationStreamDebugging(key);
|
||||||
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
||||||
Key = key;
|
Key = key;
|
||||||
|
|
@ -158,7 +161,7 @@ namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
Connect();
|
Connect();
|
||||||
}
|
}
|
||||||
}, Timeout.Infinite);
|
}, System.Threading.Timeout.Infinite);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -176,15 +179,7 @@ namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
Connect();
|
Connect();
|
||||||
}
|
}
|
||||||
}, Timeout.Infinite);
|
}, System.Threading.Timeout.Infinite);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Just to help S+ set the key
|
|
||||||
/// </summary>
|
|
||||||
public void Initialize(string key)
|
|
||||||
{
|
|
||||||
Key = key;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -196,22 +191,22 @@ namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
if (Client != null)
|
if (Client != null)
|
||||||
{
|
{
|
||||||
Debug.Console(1, this, "Program stopping. Closing connection");
|
this.LogDebug("Program stopping. Closing connection");
|
||||||
Disconnect();
|
Disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Connect to the server, using the provided properties.
|
/// Connect to the server, using the provided properties.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Connect()
|
public void Connect()
|
||||||
{
|
{
|
||||||
// Don't go unless everything is here
|
// Don't go unless everything is here
|
||||||
if (string.IsNullOrEmpty(Hostname) || Port < 1 || Port > 65535
|
if (string.IsNullOrEmpty(Hostname) || Port < 1 || Port > 65535
|
||||||
|| Username == null || Password == null)
|
|| Username == null || Password == null)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Connect failed. Check hostname, port, username and password are set or not null");
|
this.LogError("Connect failed. Check hostname, port, username and password are set or not null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,14 +214,14 @@ namespace PepperDash.Core
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
connectLock.Enter();
|
connectLock.Wait();
|
||||||
if (IsConnected)
|
if (IsConnected)
|
||||||
{
|
{
|
||||||
Debug.Console(1, this, "Connection already connected. Exiting Connect()");
|
this.LogDebug("Connection already connected. Exiting Connect");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.Console(1, this, "Attempting connect");
|
this.LogDebug("Attempting connect");
|
||||||
|
|
||||||
// Cancel reconnect if running.
|
// Cancel reconnect if running.
|
||||||
if (ReconnectTimer != null)
|
if (ReconnectTimer != null)
|
||||||
|
|
@ -237,7 +232,7 @@ namespace PepperDash.Core
|
||||||
// Cleanup the old client if it already exists
|
// Cleanup the old client if it already exists
|
||||||
if (Client != null)
|
if (Client != null)
|
||||||
{
|
{
|
||||||
Debug.Console(1, this, "Cleaning up disconnected client");
|
this.LogDebug("Cleaning up disconnected client");
|
||||||
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
|
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,7 +241,7 @@ namespace PepperDash.Core
|
||||||
kauth.AuthenticationPrompt += new EventHandler<AuthenticationPromptEventArgs>(kauth_AuthenticationPrompt);
|
kauth.AuthenticationPrompt += new EventHandler<AuthenticationPromptEventArgs>(kauth_AuthenticationPrompt);
|
||||||
PasswordAuthenticationMethod pauth = new PasswordAuthenticationMethod(Username, Password);
|
PasswordAuthenticationMethod pauth = new PasswordAuthenticationMethod(Username, Password);
|
||||||
|
|
||||||
Debug.Console(1, this, "Creating new SshClient");
|
this.LogDebug("Creating new SshClient");
|
||||||
ConnectionInfo connectionInfo = new ConnectionInfo(Hostname, Port, Username, pauth, kauth);
|
ConnectionInfo connectionInfo = new ConnectionInfo(Hostname, Port, Username, pauth, kauth);
|
||||||
Client = new SshClient(connectionInfo);
|
Client = new SshClient(connectionInfo);
|
||||||
Client.ErrorOccurred += Client_ErrorOccurred;
|
Client.ErrorOccurred += Client_ErrorOccurred;
|
||||||
|
|
@ -257,8 +252,13 @@ namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
Client.Connect();
|
Client.Connect();
|
||||||
TheStream = Client.CreateShellStream("PDTShell", 100, 80, 100, 200, 65534);
|
TheStream = Client.CreateShellStream("PDTShell", 100, 80, 100, 200, 65534);
|
||||||
|
if (TheStream.DataAvailable)
|
||||||
|
{
|
||||||
|
// empty the buffer if there is data
|
||||||
|
string str = TheStream.Read();
|
||||||
|
}
|
||||||
TheStream.DataReceived += Stream_DataReceived;
|
TheStream.DataReceived += Stream_DataReceived;
|
||||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Connected");
|
this.LogInformation("Connected");
|
||||||
ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED;
|
ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED;
|
||||||
DisconnectLogged = false;
|
DisconnectLogged = false;
|
||||||
}
|
}
|
||||||
|
|
@ -268,35 +268,52 @@ namespace PepperDash.Core
|
||||||
var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
|
var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
|
||||||
|
|
||||||
if (ie is SocketException)
|
if (ie is SocketException)
|
||||||
Debug.Console(1, this, errorLogLevel, "'{0}' CONNECTION failure: Cannot reach host, ({1})", Key, ie.Message);
|
|
||||||
else if (ie is System.Net.Sockets.SocketException)
|
|
||||||
Debug.Console(1, this, errorLogLevel, "'{0}' Connection failure: Cannot reach host '{1}' on port {2}, ({3})",
|
|
||||||
Key, Hostname, Port, ie.GetType());
|
|
||||||
else if (ie is SshAuthenticationException)
|
|
||||||
{
|
{
|
||||||
Debug.Console(1, this, errorLogLevel, "Authentication failure for username '{0}', ({1})",
|
this.LogException(ie, "CONNECTION failure: Cannot reach host, ({1})", Key, ie.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ie is System.Net.Sockets.SocketException socketException)
|
||||||
|
{
|
||||||
|
this.LogException(ie, "'{0}' Connection failure: Cannot reach host '{1}' on port {2}, ({3})",
|
||||||
|
Key, Hostname, Port, ie.GetType());
|
||||||
|
}
|
||||||
|
if (ie is SshAuthenticationException)
|
||||||
|
{
|
||||||
|
this.LogException(ie, "Authentication failure for username '{0}', ({1})", this,
|
||||||
Username, ie.Message);
|
Username, ie.Message);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
Debug.Console(1, this, errorLogLevel, "Error on connect:\r({0})", ie.Message);
|
this.LogException(ie, "Error on connect");
|
||||||
|
|
||||||
DisconnectLogged = true;
|
DisconnectLogged = true;
|
||||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||||
if (AutoReconnect)
|
if (AutoReconnect)
|
||||||
{
|
{
|
||||||
Debug.Console(1, this, "Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
|
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
|
||||||
|
ReconnectTimer.Reset(AutoReconnectIntervalMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(SshOperationTimeoutException ex)
|
||||||
|
{
|
||||||
|
this.LogWarning("Connection attempt timed out: {message}", ex.Message);
|
||||||
|
|
||||||
|
DisconnectLogged = true;
|
||||||
|
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||||
|
if (AutoReconnect)
|
||||||
|
{
|
||||||
|
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
|
||||||
ReconnectTimer.Reset(AutoReconnectIntervalMs);
|
ReconnectTimer.Reset(AutoReconnectIntervalMs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
|
var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
|
||||||
Debug.Console(1, this, errorLogLevel, "Unhandled exception on connect:\r({0})", e.Message);
|
this.LogException(e, "Unhandled exception on connect");
|
||||||
DisconnectLogged = true;
|
DisconnectLogged = true;
|
||||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||||
if (AutoReconnect)
|
if (AutoReconnect)
|
||||||
{
|
{
|
||||||
Debug.Console(1, this, "Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
|
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
|
||||||
ReconnectTimer.Reset(AutoReconnectIntervalMs);
|
ReconnectTimer.Reset(AutoReconnectIntervalMs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -304,7 +321,7 @@ namespace PepperDash.Core
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
connectLock.Leave();
|
connectLock.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -322,7 +339,7 @@ namespace PepperDash.Core
|
||||||
}
|
}
|
||||||
|
|
||||||
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
|
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Kills the stream, cleans up the client and sets it to null
|
/// Kills the stream, cleans up the client and sets it to null
|
||||||
|
|
@ -340,43 +357,15 @@ namespace PepperDash.Core
|
||||||
Client.Dispose();
|
Client.Dispose();
|
||||||
Client = null;
|
Client = null;
|
||||||
ClientStatus = status;
|
ClientStatus = status;
|
||||||
Debug.Console(1, this, "Disconnected");
|
this.LogDebug("Disconnected");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception in Kill Client:{0}", ex);
|
this.LogException(ex,"Exception in Kill Client");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Anything to do with reestablishing connection on failures
|
|
||||||
/// </summary>
|
|
||||||
void HandleConnectionFailure()
|
|
||||||
{
|
|
||||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
|
||||||
|
|
||||||
Debug.Console(1, this, "Client nulled due to connection failure. AutoReconnect: {0}, ConnectEnabled: {1}", AutoReconnect, ConnectEnabled);
|
|
||||||
if (AutoReconnect && ConnectEnabled)
|
|
||||||
{
|
|
||||||
Debug.Console(1, this, "Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
|
|
||||||
if (ReconnectTimer == null)
|
|
||||||
{
|
|
||||||
ReconnectTimer = new CTimer(o =>
|
|
||||||
{
|
|
||||||
Connect();
|
|
||||||
}, AutoReconnectIntervalMs);
|
|
||||||
Debug.Console(1, this, "Attempting connection in {0} seconds",
|
|
||||||
(float) (AutoReconnectIntervalMs/1000));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug.Console(1, this, "{0} second reconnect cycle running",
|
|
||||||
(float) (AutoReconnectIntervalMs/1000));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Kills the stream
|
/// Kills the stream
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -390,12 +379,12 @@ namespace PepperDash.Core
|
||||||
TheStream.Close();
|
TheStream.Close();
|
||||||
TheStream.Dispose();
|
TheStream.Dispose();
|
||||||
TheStream = null;
|
TheStream = null;
|
||||||
Debug.Console(1, this, "Disconnected stream");
|
this.LogDebug("Disconnected stream");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception in Kill Stream:{0}", ex);
|
this.LogException(ex, "Exception in Kill Stream:{0}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -412,31 +401,35 @@ namespace PepperDash.Core
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handler for data receive on ShellStream. Passes data across to queue for line parsing.
|
/// Handler for data receive on ShellStream. Passes data across to queue for line parsing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Stream_DataReceived(object sender, Crestron.SimplSharp.Ssh.Common.ShellDataEventArgs e)
|
void Stream_DataReceived(object sender, ShellDataEventArgs e)
|
||||||
{
|
{
|
||||||
var bytes = e.Data;
|
if (((ShellStream)sender).Length <= 0L)
|
||||||
if (bytes.Length > 0)
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var response = ((ShellStream)sender).Read();
|
||||||
|
|
||||||
|
var bytesHandler = BytesReceived;
|
||||||
|
|
||||||
|
if (bytesHandler != null)
|
||||||
|
{
|
||||||
|
var bytes = Encoding.UTF8.GetBytes(response);
|
||||||
|
if (StreamDebugging.RxStreamDebuggingIsEnabled)
|
||||||
|
{
|
||||||
|
this.LogInformation("Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
|
||||||
|
}
|
||||||
|
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
var textHandler = TextReceived;
|
||||||
|
if (textHandler != null)
|
||||||
{
|
{
|
||||||
var bytesHandler = BytesReceived;
|
if (StreamDebugging.RxStreamDebuggingIsEnabled)
|
||||||
if (bytesHandler != null)
|
this.LogInformation("Received: '{0}'", ComTextHelper.GetDebugText(response));
|
||||||
{
|
|
||||||
if (StreamDebugging.RxStreamDebuggingIsEnabled)
|
|
||||||
{
|
|
||||||
Debug.Console(0, this, "Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
|
|
||||||
}
|
|
||||||
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
var textHandler = TextReceived;
|
textHandler(this, new GenericCommMethodReceiveTextArgs(response));
|
||||||
if (textHandler != null)
|
}
|
||||||
{
|
|
||||||
var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
|
|
||||||
if (StreamDebugging.RxStreamDebuggingIsEnabled)
|
|
||||||
Debug.Console(0, this, "Received: '{0}'", ComTextHelper.GetDebugText(str));
|
|
||||||
|
|
||||||
textHandler(this, new GenericCommMethodReceiveTextArgs(str));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -444,42 +437,41 @@ namespace PepperDash.Core
|
||||||
/// Error event handler for client events - disconnect, etc. Will forward those events via ConnectionChange
|
/// Error event handler for client events - disconnect, etc. Will forward those events via ConnectionChange
|
||||||
/// event
|
/// event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Client_ErrorOccurred(object sender, Crestron.SimplSharp.Ssh.Common.ExceptionEventArgs e)
|
void Client_ErrorOccurred(object sender, ExceptionEventArgs e)
|
||||||
{
|
{
|
||||||
CrestronInvoke.BeginInvoke(o =>
|
CrestronInvoke.BeginInvoke(o =>
|
||||||
{
|
{
|
||||||
if (e.Exception is SshConnectionException || e.Exception is System.Net.Sockets.SocketException)
|
if (e.Exception is SshConnectionException || e.Exception is System.Net.Sockets.SocketException)
|
||||||
Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Disconnected by remote");
|
this.LogError("Disconnected by remote");
|
||||||
else
|
else
|
||||||
Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Unhandled SSH client error: {0}", e.Exception);
|
this.LogException(e.Exception, "Unhandled SSH client error");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
connectLock.Enter();
|
connectLock.Wait();
|
||||||
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_REMOTELY);
|
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_REMOTELY);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
connectLock.Leave();
|
connectLock.Release();
|
||||||
}
|
}
|
||||||
if (AutoReconnect && ConnectEnabled)
|
if (AutoReconnect && ConnectEnabled)
|
||||||
{
|
{
|
||||||
Debug.Console(1, this, "Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
|
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
|
||||||
ReconnectTimer.Reset(AutoReconnectIntervalMs);
|
ReconnectTimer.Reset(AutoReconnectIntervalMs);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper for ConnectionChange event
|
/// Helper for ConnectionChange event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnConnectionChange()
|
void OnConnectionChange()
|
||||||
{
|
{
|
||||||
if (ConnectionChange != null)
|
if (ConnectionChange != null)
|
||||||
ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
|
ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
#region IBasicCommunication Members
|
#region IBasicCommunication Members
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sends text to the server
|
/// Sends text to the server
|
||||||
|
|
@ -492,9 +484,8 @@ namespace PepperDash.Core
|
||||||
if (Client != null && TheStream != null && IsConnected)
|
if (Client != null && TheStream != null && IsConnected)
|
||||||
{
|
{
|
||||||
if (StreamDebugging.TxStreamDebuggingIsEnabled)
|
if (StreamDebugging.TxStreamDebuggingIsEnabled)
|
||||||
Debug.Console(0,
|
this.LogInformation(
|
||||||
this,
|
"Sending {length} characters of text: '{text}'",
|
||||||
"Sending {0} characters of text: '{1}'",
|
|
||||||
text.Length,
|
text.Length,
|
||||||
ComTextHelper.GetDebugText(text));
|
ComTextHelper.GetDebugText(text));
|
||||||
|
|
||||||
|
|
@ -503,23 +494,19 @@ namespace PepperDash.Core
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.Console(1, this, "Client is null or disconnected. Cannot Send Text");
|
this.LogDebug("Client is null or disconnected. Cannot Send Text");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ObjectDisposedException ex)
|
catch (ObjectDisposedException ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception: {0}", ex.Message);
|
this.LogException(ex, "ObjectDisposedException sending {message}", text);
|
||||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Stack Trace: {0}", ex.StackTrace);
|
|
||||||
|
|
||||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||||
ReconnectTimer.Reset();
|
ReconnectTimer.Reset();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception: {0}", ex.Message);
|
this.LogException(ex, "Exception sending text: {message}", text);
|
||||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Stack Trace: {0}", ex.StackTrace);
|
|
||||||
|
|
||||||
Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Stream write failed");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -529,47 +516,43 @@ namespace PepperDash.Core
|
||||||
/// <param name="bytes"></param>
|
/// <param name="bytes"></param>
|
||||||
public void SendBytes(byte[] bytes)
|
public void SendBytes(byte[] bytes)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (Client != null && TheStream != null && IsConnected)
|
if (Client != null && TheStream != null && IsConnected)
|
||||||
{
|
{
|
||||||
if (StreamDebugging.TxStreamDebuggingIsEnabled)
|
if (StreamDebugging.TxStreamDebuggingIsEnabled)
|
||||||
Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
|
this.LogInformation("Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
|
||||||
|
|
||||||
TheStream.Write(bytes, 0, bytes.Length);
|
TheStream.Write(bytes, 0, bytes.Length);
|
||||||
TheStream.Flush();
|
TheStream.Flush();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.Console(1, this, "Client is null or disconnected. Cannot Send Bytes");
|
this.LogDebug("Client is null or disconnected. Cannot Send Bytes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ObjectDisposedException ex)
|
catch (ObjectDisposedException ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception: {0}", ex.Message);
|
this.LogException(ex, "ObjectDisposedException sending {message}", ComTextHelper.GetEscapedText(bytes));
|
||||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Stack Trace: {0}", ex.StackTrace);
|
|
||||||
|
|
||||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||||
ReconnectTimer.Reset();
|
ReconnectTimer.Reset();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception: {0}", ex.Message);
|
this.LogException(ex, "Exception sending {message}", ComTextHelper.GetEscapedText(bytes));
|
||||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Stack Trace: {0}", ex.StackTrace);
|
}
|
||||||
|
|
||||||
Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Stream write failed");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
#endregion
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//*****************************************************************************************************
|
//*****************************************************************************************************
|
||||||
//*****************************************************************************************************
|
//*****************************************************************************************************
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired when connection changes
|
/// Fired when connection changes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SshConnectionChangeEventArgs : EventArgs
|
public class SshConnectionChangeEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Connection State
|
/// Connection State
|
||||||
|
|
@ -591,10 +574,10 @@ namespace PepperDash.Core
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort Status { get { return Client.UStatus; } }
|
public ushort Status { get { return Client.UStatus; } }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// S+ Constructor
|
/// S+ Constructor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SshConnectionChangeEventArgs() { }
|
public SshConnectionChangeEventArgs() { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// EventArgs class
|
/// EventArgs class
|
||||||
|
|
@ -602,9 +585,9 @@ namespace PepperDash.Core
|
||||||
/// <param name="isConnected">Connection State</param>
|
/// <param name="isConnected">Connection State</param>
|
||||||
/// <param name="client">The Client</param>
|
/// <param name="client">The Client</param>
|
||||||
public SshConnectionChangeEventArgs(bool isConnected, GenericSshClient client)
|
public SshConnectionChangeEventArgs(bool isConnected, GenericSshClient client)
|
||||||
{
|
{
|
||||||
IsConnected = isConnected;
|
IsConnected = isConnected;
|
||||||
Client = client;
|
Client = client;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
@ -15,8 +15,6 @@ using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Crestron.SimplSharp.CrestronSockets;
|
using Crestron.SimplSharp.CrestronSockets;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
namespace PepperDash.Core
|
namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
|
|
@ -1,17 +1,11 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Crestron.SimplSharp.CrestronSockets;
|
using Crestron.SimplSharp.CrestronSockets;
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace PepperDash.Core
|
namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
|
|
@ -6,7 +6,6 @@ using Crestron.SimplSharp;
|
||||||
using Crestron.SimplSharp.CrestronSockets;
|
using Crestron.SimplSharp.CrestronSockets;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
namespace PepperDash.Core
|
namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
|
|
@ -27,6 +26,7 @@ namespace PepperDash.Core
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates connection status
|
/// Indicates connection status
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("isConnected")]
|
||||||
bool IsConnected { get; }
|
bool IsConnected { get; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Connect to the device
|
/// Connect to the device
|
||||||
|
|
@ -72,6 +72,7 @@ namespace PepperDash.Core
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Object to enable stream debugging
|
/// Object to enable stream debugging
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("streamDebugging")]
|
||||||
CommunicationStreamDebugging StreamDebugging { get; }
|
CommunicationStreamDebugging StreamDebugging { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,7 +90,9 @@ namespace PepperDash.Core
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current socket status of the client
|
/// The current socket status of the client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
SocketStatus ClientStatus { get; }
|
[JsonProperty("clientStatus")]
|
||||||
|
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
||||||
|
SocketStatus ClientStatus { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -108,10 +111,12 @@ namespace PepperDash.Core
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enable automatic recconnect
|
/// Enable automatic recconnect
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("autoReconnect")]
|
||||||
bool AutoReconnect { get; set; }
|
bool AutoReconnect { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interval in ms to attempt automatic recconnections
|
/// Interval in ms to attempt automatic recconnections
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("autoReconnectIntervalMs")]
|
||||||
int AutoReconnectIntervalMs { get; set; }
|
int AutoReconnectIntervalMs { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1,13 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Crestron.SimplSharp.CrestronIO;
|
using Crestron.SimplSharp.CrestronIO;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using PepperDash.Core;
|
using PepperDash.Core;
|
||||||
|
|
||||||
namespace PepperDash.Core.Config
|
namespace PepperDash.Core.Config
|
||||||
|
|
@ -83,7 +79,7 @@ namespace PepperDash.Core.Config
|
||||||
merged.Add("info", template["info"]);
|
merged.Add("info", template["info"]);
|
||||||
|
|
||||||
merged.Add("devices", MergeArraysOnTopLevelProperty(template["devices"] as JArray,
|
merged.Add("devices", MergeArraysOnTopLevelProperty(template["devices"] as JArray,
|
||||||
system["devices"] as JArray, "uid", "devices"));
|
system["devices"] as JArray, "key", "devices"));
|
||||||
|
|
||||||
if (system["rooms"] == null)
|
if (system["rooms"] == null)
|
||||||
merged.Add("rooms", template["rooms"]);
|
merged.Add("rooms", template["rooms"]);
|
||||||
|
|
@ -102,9 +98,22 @@ namespace PepperDash.Core.Config
|
||||||
merged.Add("destinationLists",
|
merged.Add("destinationLists",
|
||||||
Merge(template["destinationLists"], system["destinationLists"], "destinationLists"));
|
Merge(template["destinationLists"], system["destinationLists"], "destinationLists"));
|
||||||
|
|
||||||
// Template tie lines take precedence. Config tool doesn't do them at system
|
|
||||||
// level anyway...
|
if (system["cameraLists"] == null)
|
||||||
if (template["tieLines"] != null)
|
merged.Add("cameraLists", template["cameraLists"]);
|
||||||
|
else
|
||||||
|
merged.Add("cameraLists", Merge(template["cameraLists"], system["cameraLists"], "cameraLists"));
|
||||||
|
|
||||||
|
if (system["audioControlPointLists"] == null)
|
||||||
|
merged.Add("audioControlPointLists", template["audioControlPointLists"]);
|
||||||
|
else
|
||||||
|
merged.Add("audioControlPointLists",
|
||||||
|
Merge(template["audioControlPointLists"], system["audioControlPointLists"], "audioControlPointLists"));
|
||||||
|
|
||||||
|
|
||||||
|
// Template tie lines take precedence. Config tool doesn't do them at system
|
||||||
|
// level anyway...
|
||||||
|
if (template["tieLines"] != null)
|
||||||
merged.Add("tieLines", template["tieLines"]);
|
merged.Add("tieLines", template["tieLines"]);
|
||||||
else if (system["tieLines"] != null)
|
else if (system["tieLines"] != null)
|
||||||
merged.Add("tieLines", system["tieLines"]);
|
merged.Add("tieLines", system["tieLines"]);
|
||||||
|
|
@ -121,7 +130,7 @@ namespace PepperDash.Core.Config
|
||||||
else
|
else
|
||||||
merged.Add("global", template["global"]);
|
merged.Add("global", template["global"]);
|
||||||
|
|
||||||
Debug.Console(2, "MERGED CONFIG RESULT: \x0d\x0a{0}", merged);
|
//Debug.Console(2, "MERGED CONFIG RESULT: \x0d\x0a{0}", merged);
|
||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3,6 +3,8 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
namespace PepperDash.Core
|
namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
|
|
@ -14,17 +16,20 @@ namespace PepperDash.Core
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unique Key
|
/// Unique Key
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("key")]
|
||||||
string Key { get; }
|
string Key { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Named Keyed device interface. Forces the devie to have a Unique Key and a name.
|
/// Named Keyed device interface. Forces the device to have a Unique Key and a name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IKeyName : IKeyed
|
public interface IKeyName : IKeyed
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Isn't it obvious :)
|
/// Isn't it obvious :)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("name")]
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using Serilog.Events;
|
||||||
|
|
||||||
namespace PepperDash.Core
|
namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
|
|
@ -10,6 +10,7 @@ namespace PepperDash.Core
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Device : IKeyName
|
public class Device : IKeyName
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unique Key
|
/// Unique Key
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -49,9 +50,8 @@ namespace PepperDash.Core
|
||||||
public Device(string key)
|
public Device(string key)
|
||||||
{
|
{
|
||||||
Key = key;
|
Key = key;
|
||||||
if (key.Contains('.')) Debug.Console(0, this, "WARNING: Device name's should not include '.'");
|
if (key.Contains(".")) Debug.LogMessage(LogEventLevel.Information, "WARNING: Device key should not include '.'", this);
|
||||||
Name = "";
|
Name = "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -99,7 +99,15 @@ namespace PepperDash.Core
|
||||||
public void PreActivate()
|
public void PreActivate()
|
||||||
{
|
{
|
||||||
if (_PreActivationActions != null)
|
if (_PreActivationActions != null)
|
||||||
_PreActivationActions.ForEach(a => a.Invoke());
|
_PreActivationActions.ForEach(a => {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
a.Invoke();
|
||||||
|
} catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.LogMessage(e, "Error in PreActivationAction: " + e.Message, this);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -123,7 +131,16 @@ namespace PepperDash.Core
|
||||||
public void PostActivate()
|
public void PostActivate()
|
||||||
{
|
{
|
||||||
if (_PostActivationActions != null)
|
if (_PostActivationActions != null)
|
||||||
_PostActivationActions.ForEach(a => a.Invoke());
|
_PostActivationActions.ForEach(a => {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
a.Invoke();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.LogMessage(e, "Error in PostActivationAction: " + e.Message, this);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
19
src/Directory.build.props
Normal file
19
src/Directory.build.props
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<Version>2.0.0-local</Version>
|
||||||
|
<Authors>PepperDash Technologies</Authors>
|
||||||
|
<Company>PepperDash Technologies</Company>
|
||||||
|
<Product>PepperDash Essentials</Product>
|
||||||
|
<Copyright>Copyright © 2025</Copyright>
|
||||||
|
<RepositoryType>git</RepositoryType>
|
||||||
|
<PackageTags>Crestron; 4series</PackageTags>
|
||||||
|
<PackageOutputPath>../output</PackageOutputPath>
|
||||||
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
|
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
|
||||||
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\LICENSE.md" Pack="true" PackagePath=""/>
|
||||||
|
<None Include="..\README.md" Pack="true" PackagePath=""/>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
43
src/Directory.build.targets
Normal file
43
src/Directory.build.targets
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
<Project>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="$(TargetDir)\$(TargetName).$(Version).cpz" Condition="$(ProjectType) == 'Program'">
|
||||||
|
<Pack>true</Pack>
|
||||||
|
<PackagePath>content;</PackagePath>
|
||||||
|
</None>
|
||||||
|
<None Include="$(PackageOutputPath)\$(TargetName).$(Version).cplz" Condition="$(ProjectType) == 'ProgramLibrary'">
|
||||||
|
<Pack>true</Pack>
|
||||||
|
<PackagePath>content;</PackagePath>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<Target Name="Create CPLZ" AfterTargets="Build; Rebuild" Condition="$(ProjectType) == 'ProgramLibrary'">
|
||||||
|
<Message Text="Creating CPLZ"></Message>
|
||||||
|
<MakeDir Directories="$(PackageOutputPath)" Condition="!Exists($(PackageOutputPath))"></MakeDir>
|
||||||
|
<ZipDirectory SourceDirectory="$(TargetDir)" DestinationFile="$(PackageOutputPath)\$(TargetName).$(Version).cplz" Overwrite="true"/>
|
||||||
|
</Target>
|
||||||
|
<Target Name="Clean CPLZ" AfterTargets="AfterClean" Condition="$(ProjectType) == 'ProgramLibrary'">
|
||||||
|
<Delete Files="$(PackageOutputPath)\$(TargetName).$(Version).cplz"/>
|
||||||
|
</Target>
|
||||||
|
<Target Name="Copy CPZ" AfterTargets="SimplSharpPostProcess" Condition="$(ProjectType) == 'Program'">
|
||||||
|
<Message Text="Copying CPZ"></Message>
|
||||||
|
<Move SourceFiles="$(TargetDir)\$(TargetName).cpz" DestinationFiles="$(TargetDir)\$(TargetName).$(Version).cpz"/>
|
||||||
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).$(Version).cpz" DestinationFiles="$(PackageOutputPath)\$(TargetName).$(Version).cpz"/>
|
||||||
|
</Target>
|
||||||
|
<Target Name="Clean CPZ" AfterTargets="AfterClean" Condition="$(ProjectType) == 'Program'">
|
||||||
|
<Delete Files="$(PackageOutputPath)\$(TargetName).$(Version).cpz"/>
|
||||||
|
</Target>
|
||||||
|
<Target Name="Copy CLZ" AfterTargets="SimplSharpPostProcess">
|
||||||
|
<Message Text="Copying CLZ"></Message>
|
||||||
|
<Move SourceFiles="$(TargetDir)\$(TargetName).clz" DestinationFiles="$(TargetDir)\$(TargetName).$(Version).clz"/>
|
||||||
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).$(Version).clz" DestinationFiles="$(PackageOutputPath)\$(TargetName).$(Version).clz"/>
|
||||||
|
</Target>
|
||||||
|
<Target Name="Clean CLZ" AfterTargets="AfterClean">
|
||||||
|
<Delete Files="$(PackageOutputPath)\$(TargetName).$(Version).clz"/>
|
||||||
|
</Target>
|
||||||
|
<Target Name="SimplSharpNewtonsoft" BeforeTargets="FindReferenceAssembliesForReferences;ResolveReferences">
|
||||||
|
<ItemGroup>
|
||||||
|
<ReferencePath Condition="'%(FileName)' == 'Newtonsoft.Json.Compact'">
|
||||||
|
<Aliases>doNotUse</Aliases>
|
||||||
|
</ReferencePath>
|
||||||
|
</ItemGroup>
|
||||||
|
</Target>
|
||||||
|
</Project>
|
||||||
21
src/EssentialsPlugins-builds-4-series-caller.yml
Normal file
21
src/EssentialsPlugins-builds-4-series-caller.yml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
name: Build Essentials Plugin
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
getVersion:
|
||||||
|
uses: PepperDash/workflow-templates/.github/workflows/essentialsplugins-getversion.yml@main
|
||||||
|
secrets: inherit
|
||||||
|
build-4Series:
|
||||||
|
uses: PepperDash/workflow-templates/.github/workflows/essentialsplugins-4Series-builds.yml@main
|
||||||
|
secrets: inherit
|
||||||
|
needs: getVersion
|
||||||
|
if: needs.getVersion.outputs.newVersion == 'true'
|
||||||
|
with:
|
||||||
|
newVersion: ${{ needs.getVersion.outputs.newVersion }}
|
||||||
|
version: ${{ needs.getVersion.outputs.version }}
|
||||||
|
tag: ${{ needs.getVersion.outputs.tag }}
|
||||||
|
channel: ${{ needs.getVersion.outputs.channel }}
|
||||||
|
|
@ -1,9 +1,4 @@
|
||||||
using System;
|
using Crestron.SimplSharp;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace PepperDash.Core
|
namespace PepperDash.Core
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using PepperDash.Core.JsonToSimpl;
|
using PepperDash.Core.JsonToSimpl;
|
||||||
|
|
||||||
namespace PepperDash.Core.JsonStandardObjects
|
namespace PepperDash.Core.JsonStandardObjects
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace PepperDash.Core.JsonToSimpl
|
namespace PepperDash.Core.JsonToSimpl
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace PepperDash.Core.JsonToSimpl
|
namespace PepperDash.Core.JsonToSimpl
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
//using System.IO;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Crestron.SimplSharp.CrestronIO;
|
using Crestron.SimplSharp.CrestronIO;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace PepperDash.Core.JsonToSimpl
|
namespace PepperDash.Core.JsonToSimpl
|
||||||
|
|
@ -196,7 +194,7 @@ namespace PepperDash.Core.JsonToSimpl
|
||||||
/// Sets the debug level
|
/// Sets the debug level
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="level"></param>
|
/// <param name="level"></param>
|
||||||
public void setDebugLevel(int level)
|
public void setDebugLevel(uint level)
|
||||||
{
|
{
|
||||||
Debug.SetDebugLevel(level);
|
Debug.SetDebugLevel(level);
|
||||||
}
|
}
|
||||||
|
|
@ -1,12 +1,4 @@
|
||||||
using System;
|
|
||||||
//using System.IO;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
using Crestron.SimplSharp.CrestronIO;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
namespace PepperDash.Core.JsonToSimpl
|
namespace PepperDash.Core.JsonToSimpl
|
||||||
{
|
{
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
//using System.IO;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.IO;
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Crestron.SimplSharp.CrestronIO;
|
using Crestron.SimplSharp.CrestronIO;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
@ -162,7 +160,11 @@ namespace PepperDash.Core.JsonToSimpl
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static JObject ParseObject(string json)
|
public static JObject ParseObject(string json)
|
||||||
{
|
{
|
||||||
using (var reader = new JsonTextReader(new Crestron.SimplSharp.CrestronIO.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 startDepth = reader.Depth;
|
||||||
var obj = JObject.Load(reader);
|
var obj = JObject.Load(reader);
|
||||||
|
|
@ -179,7 +181,11 @@ namespace PepperDash.Core.JsonToSimpl
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static JArray ParseArray(string json)
|
public static JArray ParseArray(string json)
|
||||||
{
|
{
|
||||||
using (var reader = new JsonTextReader(new Crestron.SimplSharp.CrestronIO.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 startDepth = reader.Depth;
|
||||||
var obj = JArray.Load(reader);
|
var obj = JArray.Load(reader);
|
||||||
|
|
@ -1,13 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
//using System.IO;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Crestron.SimplSharp.CrestronIO;
|
using Crestron.SimplSharp.CrestronIO;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
using PepperDash.Core.Config;
|
using PepperDash.Core.Config;
|
||||||
|
|
||||||
namespace PepperDash.Core.JsonToSimpl
|
namespace PepperDash.Core.JsonToSimpl
|
||||||
|
|
@ -132,7 +128,7 @@ namespace PepperDash.Core.JsonToSimpl
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="level"></param>
|
/// <param name="level"></param>
|
||||||
public void setDebugLevel(int level)
|
public void setDebugLevel(uint level)
|
||||||
{
|
{
|
||||||
Debug.SetDebugLevel(level);
|
Debug.SetDebugLevel(level);
|
||||||
}
|
}
|
||||||
37
src/Logging/CrestronEnricher.cs
Normal file
37
src/Logging/CrestronEnricher.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
using Crestron.SimplSharp;
|
||||||
|
using Serilog.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace PepperDash.Core.Logging
|
||||||
|
{
|
||||||
|
public class CrestronEnricher : ILogEventEnricher
|
||||||
|
{
|
||||||
|
static readonly string _appName;
|
||||||
|
|
||||||
|
static CrestronEnricher()
|
||||||
|
{
|
||||||
|
switch (CrestronEnvironment.DevicePlatform)
|
||||||
|
{
|
||||||
|
case eDevicePlatform.Appliance:
|
||||||
|
_appName = $"App {InitialParametersClass.ApplicationNumber}";
|
||||||
|
break;
|
||||||
|
case eDevicePlatform.Server:
|
||||||
|
_appName = $"{InitialParametersClass.RoomId}";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
|
||||||
|
{
|
||||||
|
var property = propertyFactory.CreateProperty("App", _appName);
|
||||||
|
|
||||||
|
logEvent.AddOrUpdateProperty(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,20 @@
|
||||||
using System;
|
using Crestron.SimplSharp;
|
||||||
using System.Collections.Generic;
|
using Crestron.SimplSharp.CrestronDataStore;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
using Crestron.SimplSharp.Reflection;
|
|
||||||
using Crestron.SimplSharp.CrestronLogger;
|
|
||||||
using Crestron.SimplSharp.CrestronIO;
|
using Crestron.SimplSharp.CrestronIO;
|
||||||
|
using Crestron.SimplSharp.CrestronLogger;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using PepperDash.Core.DebugThings;
|
using PepperDash.Core.Logging;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Context;
|
||||||
|
using Serilog.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
using Serilog.Formatting.Compact;
|
||||||
|
using Serilog.Formatting.Json;
|
||||||
|
using Serilog.Templates;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace PepperDash.Core
|
namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
|
|
@ -16,6 +23,43 @@ namespace PepperDash.Core
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Debug
|
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 Dictionary<uint, LogEventLevel> _logLevels = new Dictionary<uint, LogEventLevel>()
|
||||||
|
{
|
||||||
|
{0, LogEventLevel.Information },
|
||||||
|
{3, LogEventLevel.Warning },
|
||||||
|
{4, LogEventLevel.Error },
|
||||||
|
{5, LogEventLevel.Fatal },
|
||||||
|
{1, LogEventLevel.Debug },
|
||||||
|
{2, LogEventLevel.Verbose },
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ILogger _logger;
|
||||||
|
|
||||||
|
private static readonly LoggingLevelSwitch _consoleLoggingLevelSwitch;
|
||||||
|
|
||||||
|
private static readonly LoggingLevelSwitch _websocketLoggingLevelSwitch;
|
||||||
|
|
||||||
|
private static readonly LoggingLevelSwitch _errorLogLevelSwitch;
|
||||||
|
|
||||||
|
private static readonly LoggingLevelSwitch _fileLevelSwitch;
|
||||||
|
|
||||||
|
public static LogEventLevel WebsocketMinimumLogLevel
|
||||||
|
{
|
||||||
|
get { return _websocketLoggingLevelSwitch.MinimumLevel; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly DebugWebsocketSink _websocketSink;
|
||||||
|
|
||||||
|
public static DebugWebsocketSink WebsocketSink
|
||||||
|
{
|
||||||
|
get { return _websocketSink; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Describes the folder location where a given program stores it's debug level memory. By default, the
|
/// 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.
|
/// file written will be named appNdebug where N is 1-10.
|
||||||
|
|
@ -41,12 +85,14 @@ namespace PepperDash.Core
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When this is true, the configuration file will NOT be loaded until triggered by either a console command or a signal
|
/// When this is true, the configuration file will NOT be loaded until triggered by either a console command or a signal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool DoNotLoadOnNextBoot { get; private set; }
|
public static bool DoNotLoadConfigOnNextBoot { get; private set; }
|
||||||
|
|
||||||
private static DebugContextCollection _contexts;
|
private static DebugContextCollection _contexts;
|
||||||
|
|
||||||
private const int SaveTimeoutMs = 30000;
|
private const int SaveTimeoutMs = 30000;
|
||||||
|
|
||||||
|
public static bool IsRunningOnAppliance = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version for the currently loaded PepperDashCore dll
|
/// Version for the currently loaded PepperDashCore dll
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -64,29 +110,93 @@ namespace PepperDash.Core
|
||||||
|
|
||||||
private static readonly Dictionary<string, object> IncludedExcludedKeys;
|
private static readonly Dictionary<string, object> IncludedExcludedKeys;
|
||||||
|
|
||||||
|
private static readonly LoggerConfiguration _defaultLoggerConfiguration;
|
||||||
|
|
||||||
|
private static LoggerConfiguration _loggerConfiguration;
|
||||||
|
|
||||||
|
public static LoggerConfiguration LoggerConfiguration => _loggerConfiguration;
|
||||||
|
|
||||||
static Debug()
|
static Debug()
|
||||||
{
|
{
|
||||||
|
CrestronDataStoreStatic.InitCrestronDataStore();
|
||||||
|
|
||||||
|
var defaultConsoleLevel = GetStoredLogEventLevel(LevelStoreKey);
|
||||||
|
|
||||||
|
var defaultWebsocketLevel = GetStoredLogEventLevel(WebSocketLevelStoreKey);
|
||||||
|
|
||||||
|
var defaultErrorLogLevel = GetStoredLogEventLevel(ErrorLogLevelStoreKey);
|
||||||
|
|
||||||
|
var defaultFileLogLevel = GetStoredLogEventLevel(FileLevelStoreKey);
|
||||||
|
|
||||||
|
_consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel);
|
||||||
|
|
||||||
|
_websocketLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel);
|
||||||
|
|
||||||
|
_errorLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultErrorLogLevel);
|
||||||
|
|
||||||
|
_fileLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultFileLogLevel);
|
||||||
|
|
||||||
|
_websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true));
|
||||||
|
|
||||||
|
var logFilePath = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ?
|
||||||
|
$@"{Directory.GetApplicationRootDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}app{InitialParametersClass.ApplicationNumber}{Path.DirectorySeparatorChar}global-log.log" :
|
||||||
|
$@"{Directory.GetApplicationRootDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}room{InitialParametersClass.RoomId}{Path.DirectorySeparatorChar}global-log.log";
|
||||||
|
|
||||||
|
CrestronConsole.PrintLine($"Saving log files to {logFilePath}");
|
||||||
|
|
||||||
|
var errorLogTemplate = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance
|
||||||
|
? "{@t:fff}ms [{@l:u4}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}"
|
||||||
|
: "[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}";
|
||||||
|
|
||||||
|
_defaultLoggerConfiguration = new LoggerConfiguration()
|
||||||
|
.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 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
|
||||||
|
);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
_logger = _loggerConfiguration.CreateLogger();
|
||||||
// Get the assembly version and print it to console and the log
|
// Get the assembly version and print it to console and the log
|
||||||
GetVersion();
|
GetVersion();
|
||||||
|
|
||||||
string msg = "";
|
string msg = $"[App {InitialParametersClass.ApplicationNumber}] Using PepperDash_Core v{PepperDashCoreVersion}";
|
||||||
|
|
||||||
if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)
|
if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server)
|
||||||
{
|
{
|
||||||
msg = string.Format("[App {0}] Using PepperDash_Core v{1}", InitialParametersClass.ApplicationNumber, PepperDashCoreVersion);
|
msg = $"[Room {InitialParametersClass.RoomId}] Using PepperDash_Core v{PepperDashCoreVersion}";
|
||||||
}
|
|
||||||
else if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server)
|
|
||||||
{
|
|
||||||
msg = string.Format("[Room {0}] Using PepperDash_Core v{1}", InitialParametersClass.RoomId, PepperDashCoreVersion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CrestronConsole.PrintLine(msg);
|
CrestronConsole.PrintLine(msg);
|
||||||
|
|
||||||
LogError(ErrorLogLevel.Notice, msg);
|
LogMessage(LogEventLevel.Information,msg);
|
||||||
|
|
||||||
IncludedExcludedKeys = new Dictionary<string, object>();
|
IncludedExcludedKeys = new Dictionary<string, object>();
|
||||||
|
|
||||||
//CrestronDataStoreStatic.InitCrestronDataStore();
|
|
||||||
if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro)
|
if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro)
|
||||||
{
|
{
|
||||||
// Add command to console
|
// Add command to console
|
||||||
|
|
@ -94,7 +204,7 @@ namespace PepperDash.Core
|
||||||
"donotloadonnextboot:P [true/false]: Should the application load on next boot", ConsoleAccessLevelEnum.AccessOperator);
|
"donotloadonnextboot:P [true/false]: Should the application load on next boot", ConsoleAccessLevelEnum.AccessOperator);
|
||||||
|
|
||||||
CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, "appdebug",
|
CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, "appdebug",
|
||||||
"appdebug:P [0-2]: Sets the application's console debug message level",
|
"appdebug:P [0-5]: Sets the application's console debug message level",
|
||||||
ConsoleAccessLevelEnum.AccessOperator);
|
ConsoleAccessLevelEnum.AccessOperator);
|
||||||
CrestronConsole.AddNewConsoleCommand(ShowDebugLog, "appdebuglog",
|
CrestronConsole.AddNewConsoleCommand(ShowDebugLog, "appdebuglog",
|
||||||
"appdebuglog:P [all] Use \"all\" for full log.",
|
"appdebuglog:P [all] Use \"all\" for full log.",
|
||||||
|
|
@ -112,25 +222,54 @@ namespace PepperDash.Core
|
||||||
|
|
||||||
var context = _contexts.GetOrCreateItem("DEFAULT");
|
var context = _contexts.GetOrCreateItem("DEFAULT");
|
||||||
Level = context.Level;
|
Level = context.Level;
|
||||||
DoNotLoadOnNextBoot = context.DoNotLoadOnNextBoot;
|
DoNotLoadConfigOnNextBoot = context.DoNotLoadOnNextBoot;
|
||||||
|
|
||||||
if(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));
|
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) =>
|
||||||
|
{
|
||||||
|
Console(0, "Console debug level set to {0}", _consoleLoggingLevelSwitch.MinimumLevel);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UpdateLoggerConfiguration(LoggerConfiguration config)
|
||||||
|
{
|
||||||
|
_loggerConfiguration = config;
|
||||||
|
|
||||||
|
_logger = config.CreateLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ResetLoggerConfiguration()
|
||||||
|
{
|
||||||
|
_loggerConfiguration = _defaultLoggerConfiguration;
|
||||||
|
|
||||||
|
_logger = _loggerConfiguration.CreateLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LogEventLevel GetStoredLogEventLevel(string levelStoreKey)
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (InitialParametersClass.NumberOfRemovableDrives > 0)
|
var result = CrestronDataStoreStatic.GetLocalIntValue(levelStoreKey, out int logLevel);
|
||||||
{
|
|
||||||
CrestronConsole.PrintLine("{0} RM Drive(s) Present.", InitialParametersClass.NumberOfRemovableDrives);
|
|
||||||
CrestronLogger.Initialize(2, LoggerModeEnum.DEFAULT); // Use RM instead of DEFAULT as not to double-up console messages.
|
|
||||||
}
|
|
||||||
else
|
|
||||||
CrestronConsole.PrintLine("No RM Drive(s) Present.");
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
|
|
||||||
CrestronConsole.PrintLine("Initializing of CrestronLogger failed: {0}", e);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(logLevel < 0 || logLevel > 5)
|
||||||
|
{
|
||||||
|
CrestronConsole.PrintLine($"Stored Log level not valid for {levelStoreKey}: {logLevel}. Setting level to {LogEventLevel.Information}");
|
||||||
|
return LogEventLevel.Information;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (LogEventLevel)logLevel;
|
||||||
|
} catch (Exception ex)
|
||||||
|
{
|
||||||
|
CrestronConsole.PrintLine($"Exception retrieving log level for {levelStoreKey}: {ex.Message}");
|
||||||
|
return LogEventLevel.Information;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,9 +282,7 @@ namespace PepperDash.Core
|
||||||
|
|
||||||
if (ver != null && ver.Length > 0)
|
if (ver != null && ver.Length > 0)
|
||||||
{
|
{
|
||||||
var verAttribute = ver[0] as AssemblyInformationalVersionAttribute;
|
if (ver[0] is AssemblyInformationalVersionAttribute verAttribute)
|
||||||
|
|
||||||
if (verAttribute != null)
|
|
||||||
{
|
{
|
||||||
PepperDashCoreVersion = verAttribute.InformationalVersion;
|
PepperDashCoreVersion = verAttribute.InformationalVersion;
|
||||||
}
|
}
|
||||||
|
|
@ -153,8 +290,7 @@ namespace PepperDash.Core
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var version = assembly.GetName().Version;
|
var version = assembly.GetName().Version;
|
||||||
PepperDashCoreVersion = string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build,
|
PepperDashCoreVersion = version.ToString();
|
||||||
version.Revision);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,8 +300,11 @@ namespace PepperDash.Core
|
||||||
/// <param name="programEventType"></param>
|
/// <param name="programEventType"></param>
|
||||||
static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
|
static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (programEventType == eProgramStatusEventType.Stopping)
|
if (programEventType == eProgramStatusEventType.Stopping)
|
||||||
{
|
{
|
||||||
|
Log.CloseAndFlush();
|
||||||
|
|
||||||
if (_saveTimer != null)
|
if (_saveTimer != null)
|
||||||
{
|
{
|
||||||
_saveTimer.Stop();
|
_saveTimer.Stop();
|
||||||
|
|
@ -184,20 +323,121 @@ namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(levelString.Trim()))
|
if (levelString.Trim() == "?")
|
||||||
{
|
{
|
||||||
CrestronConsole.PrintLine("AppDebug level = {0}", Level);
|
CrestronConsole.ConsoleCommandResponse(
|
||||||
|
$@"Used to set the minimum level of debug messages to be printed to the console:
|
||||||
|
{_logLevels[0]} = 0
|
||||||
|
{_logLevels[1]} = 1
|
||||||
|
{_logLevels[2]} = 2
|
||||||
|
{_logLevels[3]} = 3
|
||||||
|
{_logLevels[4]} = 4
|
||||||
|
{_logLevels[5]} = 5");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetDebugLevel(Convert.ToInt32(levelString));
|
if (string.IsNullOrEmpty(levelString.Trim()))
|
||||||
|
{
|
||||||
|
CrestronConsole.ConsoleCommandResponse("AppDebug level = {0}", _consoleLoggingLevelSwitch.MinimumLevel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(int.TryParse(levelString, 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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Enum.TryParse<LogEventLevel>(levelString, out var levelEnum))
|
||||||
|
{
|
||||||
|
SetDebugLevel(levelEnum);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelString} to valid log level");
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
CrestronConsole.PrintLine("Usage: appdebug:P [0-2]");
|
CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [0-5]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the debug level
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="level"> Valid values 0-5</param>
|
||||||
|
public static void SetDebugLevel(uint level)
|
||||||
|
{
|
||||||
|
if(!_logLevels.TryGetValue(level, out var logLevel))
|
||||||
|
{
|
||||||
|
logLevel = LogEventLevel.Information;
|
||||||
|
|
||||||
|
CrestronConsole.ConsoleCommandResponse($"{level} not valid. Setting level to {logLevel}");
|
||||||
|
|
||||||
|
SetDebugLevel(logLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetDebugLevel(logLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetDebugLevel(LogEventLevel level)
|
||||||
|
{
|
||||||
|
_consoleLoggingLevelSwitch.MinimumLevel = level;
|
||||||
|
|
||||||
|
CrestronConsole.ConsoleCommandResponse("[Application {0}], Debug level set to {1}\r\n",
|
||||||
|
InitialParametersClass.ApplicationNumber, _consoleLoggingLevelSwitch.MinimumLevel);
|
||||||
|
|
||||||
|
CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int) level}");
|
||||||
|
|
||||||
|
var err = CrestronDataStoreStatic.SetLocalIntValue(LevelStoreKey, (int) level);
|
||||||
|
|
||||||
|
CrestronConsole.ConsoleCommandResponse($"Store result: {err}:{(int)level}");
|
||||||
|
|
||||||
|
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
|
||||||
|
CrestronConsole.PrintLine($"Error saving console debug level setting: {err}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetWebSocketMinimumDebugLevel(LogEventLevel level)
|
||||||
|
{
|
||||||
|
_websocketLoggingLevelSwitch.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetErrorLogMinimumDebugLevel(LogEventLevel level)
|
||||||
|
{
|
||||||
|
_errorLogLevelSwitch.MinimumLevel = level;
|
||||||
|
|
||||||
|
var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetFileMinimumDebugLevel(LogEventLevel level)
|
||||||
|
{
|
||||||
|
_errorLogLevelSwitch.MinimumLevel = level;
|
||||||
|
|
||||||
|
var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)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);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Callback for console command
|
/// Callback for console command
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -208,15 +448,15 @@ namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(stateString.Trim()))
|
if (string.IsNullOrEmpty(stateString.Trim()))
|
||||||
{
|
{
|
||||||
CrestronConsole.PrintLine("DoNotLoadOnNextBoot = {0}", DoNotLoadOnNextBoot);
|
CrestronConsole.ConsoleCommandResponse("DoNotLoadOnNextBoot = {0}", DoNotLoadConfigOnNextBoot);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetDoNotLoadOnNextBoot(Boolean.Parse(stateString));
|
SetDoNotLoadConfigOnNextBoot(bool.Parse(stateString));
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
CrestronConsole.PrintLine("Usage: donotloadonnextboot:P [true/false]");
|
CrestronConsole.ConsoleCommandResponse("Usage: donotloadonnextboot:P [true/false]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -232,7 +472,7 @@ namespace PepperDash.Core
|
||||||
CrestronConsole.ConsoleCommandResponse("Usage:\r APPDEBUGFILTER key1 key2 key3....\r " +
|
CrestronConsole.ConsoleCommandResponse("Usage:\r APPDEBUGFILTER key1 key2 key3....\r " +
|
||||||
"+all: at beginning puts filter into 'default include' mode\r" +
|
"+all: at beginning puts filter into 'default include' mode\r" +
|
||||||
" All keys that follow will be excluded from output.\r" +
|
" All keys that follow will be excluded from output.\r" +
|
||||||
"-all: at beginning puts filter into 'default excluse all' mode.\r" +
|
"-all: at beginning puts filter into 'default exclude all' mode.\r" +
|
||||||
" All keys that follow will be the only keys that are shown\r" +
|
" All keys that follow will be the only keys that are shown\r" +
|
||||||
"+nokey: Enables messages with no key (default)\r" +
|
"+nokey: Enables messages with no key (default)\r" +
|
||||||
"-nokey: Disables messages with no key.\r" +
|
"-nokey: Disables messages with no key.\r" +
|
||||||
|
|
@ -300,26 +540,7 @@ namespace PepperDash.Core
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the debug level
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="level"> Valid values 0 (no debug), 1 (critical), 2 (all messages)</param>
|
|
||||||
public static void SetDebugLevel(int level)
|
|
||||||
{
|
|
||||||
if (level <= 2)
|
|
||||||
{
|
|
||||||
Level = level;
|
|
||||||
_contexts.GetOrCreateItem("DEFAULT").Level = level;
|
|
||||||
SaveMemoryOnTimeout();
|
|
||||||
|
|
||||||
CrestronConsole.PrintLine("[Application {0}], Debug level set to {1}",
|
|
||||||
InitialParametersClass.ApplicationNumber, Level);
|
|
||||||
|
|
||||||
//var err = CrestronDataStoreStatic.SetLocalUintValue("DebugLevel", level);
|
|
||||||
//if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
|
|
||||||
// CrestronConsole.PrintLine("Error saving console debug level setting: {0}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// sets the settings for a device or creates a new entry
|
/// sets the settings for a device or creates a new entry
|
||||||
|
|
@ -347,14 +568,14 @@ namespace PepperDash.Core
|
||||||
/// Sets the flag to prevent application starting on next boot
|
/// Sets the flag to prevent application starting on next boot
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state"></param>
|
/// <param name="state"></param>
|
||||||
public static void SetDoNotLoadOnNextBoot(bool state)
|
public static void SetDoNotLoadConfigOnNextBoot(bool state)
|
||||||
{
|
{
|
||||||
DoNotLoadOnNextBoot = state;
|
DoNotLoadConfigOnNextBoot = state;
|
||||||
_contexts.GetOrCreateItem("DEFAULT").DoNotLoadOnNextBoot = state;
|
_contexts.GetOrCreateItem("DEFAULT").DoNotLoadOnNextBoot = state;
|
||||||
SaveMemoryOnTimeout();
|
SaveMemoryOnTimeout();
|
||||||
|
|
||||||
CrestronConsole.PrintLine("[Application {0}], Do Not Start on Next Boot set to {1}",
|
CrestronConsole.ConsoleCommandResponse("[Application {0}], Do Not Load Config on Next Boot set to {1}",
|
||||||
InitialParametersClass.ApplicationNumber, DoNotLoadOnNextBoot);
|
InitialParametersClass.ApplicationNumber, DoNotLoadConfigOnNextBoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -367,6 +588,66 @@ namespace PepperDash.Core
|
||||||
CrestronConsole.ConsoleCommandResponse(l + CrestronEnvironment.NewLine);
|
CrestronConsole.ConsoleCommandResponse(l + CrestronEnvironment.NewLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log an Exception using Serilog's default Exception logging mechanism
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ex">Exception to log</param>
|
||||||
|
/// <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>
|
||||||
|
public static void LogMessage(Exception ex, string message, IKeyed device = null, params object[] args)
|
||||||
|
{
|
||||||
|
using (LogContext.PushProperty("Key", device?.Key))
|
||||||
|
{
|
||||||
|
_logger.Error(ex, message, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="level">Level to log at</param>
|
||||||
|
/// <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>
|
||||||
|
public static void LogMessage(LogEventLevel level, string message, IKeyed device=null, params object[] args)
|
||||||
|
{
|
||||||
|
using (LogContext.PushProperty("Key", device?.Key))
|
||||||
|
{
|
||||||
|
_logger.Write(level, message, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogMessage(LogEventLevel level, string message, params object[] args)
|
||||||
|
{
|
||||||
|
LogMessage(level, message, null, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogMessage(LogEventLevel level, IKeyed keyed, string message, params object[] args)
|
||||||
|
{
|
||||||
|
LogMessage(level, message, keyed, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void LogMessage(uint level, string format, params object[] items)
|
||||||
|
{
|
||||||
|
if (!_logLevels.ContainsKey(level)) return;
|
||||||
|
|
||||||
|
var logLevel = _logLevels[level];
|
||||||
|
|
||||||
|
LogMessage(logLevel, format, items);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LogMessage(uint level, IKeyed keyed, string format, params object[] items)
|
||||||
|
{
|
||||||
|
if (!_logLevels.ContainsKey(level)) return;
|
||||||
|
|
||||||
|
var logLevel = _logLevels[level];
|
||||||
|
|
||||||
|
LogMessage(logLevel, keyed, format, items);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prints message to console if current debug level is equal to or higher than the level of this message.
|
/// Prints message to console if current debug level is equal to or higher than the level of this message.
|
||||||
/// Uses CrestronConsole.PrintLine.
|
/// Uses CrestronConsole.PrintLine.
|
||||||
|
|
@ -374,67 +655,52 @@ namespace PepperDash.Core
|
||||||
/// <param name="level"></param>
|
/// <param name="level"></param>
|
||||||
/// <param name="format">Console format string</param>
|
/// <param name="format">Console format string</param>
|
||||||
/// <param name="items">Object parameters</param>
|
/// <param name="items">Object parameters</param>
|
||||||
|
[Obsolete("Use LogMessage methods")]
|
||||||
public static void Console(uint level, string format, params object[] items)
|
public static void Console(uint level, string format, params object[] items)
|
||||||
{
|
{
|
||||||
if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server)
|
|
||||||
{
|
|
||||||
var logString = string.Format("[level {0}] {1}", level, string.Format(format, items));
|
|
||||||
|
|
||||||
LogError(ErrorLogLevel.Notice, logString);
|
LogMessage(level, format, items);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(Level < level)
|
//if (IsRunningOnAppliance)
|
||||||
{
|
//{
|
||||||
return;
|
// CrestronConsole.PrintLine("[{0}]App {1} Lvl {2}:{3}", DateTime.Now.ToString("HH:mm:ss.fff"),
|
||||||
}
|
// InitialParametersClass.ApplicationNumber,
|
||||||
|
// level,
|
||||||
CrestronConsole.PrintLine("[{0}]App {1}:{2}", DateTime.Now.ToString("HH:mm:ss.fff"), InitialParametersClass.ApplicationNumber,
|
// string.Format(format, items));
|
||||||
string.Format(format, items));
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs to Console when at-level, and all messages to error log, including device key
|
/// Logs to Console when at-level, and all messages to error log, including device key
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Use LogMessage methods")]
|
||||||
public static void Console(uint level, IKeyed dev, string format, params object[] items)
|
public static void Console(uint level, IKeyed dev, string format, params object[] items)
|
||||||
{
|
{
|
||||||
if (Level >= level)
|
LogMessage(level, dev, format, items);
|
||||||
Console(level, "[{0}] {1}", dev.Key, string.Format(format, items));
|
|
||||||
|
//if (Level >= level)
|
||||||
|
// Console(level, "[{0}] {1}", dev.Key, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prints message to console if current debug level is equal to or higher than the level of this message. Always sends message to Error Log.
|
/// Prints message to console if current debug level is equal to or higher than the level of this message. Always sends message to Error Log.
|
||||||
/// Uses CrestronConsole.PrintLine.
|
/// Uses CrestronConsole.PrintLine.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Use LogMessage methods")]
|
||||||
public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel,
|
public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel,
|
||||||
string format, params object[] items)
|
string format, params object[] items)
|
||||||
{
|
{
|
||||||
var str = string.Format("[{0}] {1}", dev.Key, string.Format(format, items));
|
LogMessage(level, dev, format, items);
|
||||||
if (errorLogLevel != ErrorLogLevel.None)
|
|
||||||
{
|
|
||||||
LogError(errorLogLevel, str);
|
|
||||||
}
|
|
||||||
if (Level >= level)
|
|
||||||
{
|
|
||||||
Console(level, str);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs to Console when at-level, and all messages to error log
|
/// Logs to Console when at-level, and all messages to error log
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Use LogMessage methods")]
|
||||||
public static void Console(uint level, ErrorLogLevel errorLogLevel,
|
public static void Console(uint level, ErrorLogLevel errorLogLevel,
|
||||||
string format, params object[] items)
|
string format, params object[] items)
|
||||||
{
|
{
|
||||||
var str = string.Format(format, items);
|
LogMessage(level, format, items);
|
||||||
if (errorLogLevel != ErrorLogLevel.None)
|
|
||||||
{
|
|
||||||
LogError(errorLogLevel, str);
|
|
||||||
}
|
|
||||||
if (Level >= level)
|
|
||||||
{
|
|
||||||
Console(level, str);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -442,12 +708,15 @@ namespace PepperDash.Core
|
||||||
/// or above the level provided, then the output will be written to both console and the log. Otherwise
|
/// or above the level provided, then the output will be written to both console and the log. Otherwise
|
||||||
/// it will only be written to the log.
|
/// it will only be written to the log.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Use LogMessage methods")]
|
||||||
public static void ConsoleWithLog(uint level, string format, params object[] items)
|
public static void ConsoleWithLog(uint level, string format, params object[] items)
|
||||||
{
|
{
|
||||||
var str = string.Format(format, items);
|
LogMessage(level, format, items);
|
||||||
if (Level >= level)
|
|
||||||
CrestronConsole.PrintLine("App {0}:{1}", InitialParametersClass.ApplicationNumber, str);
|
// var str = string.Format(format, items);
|
||||||
CrestronLogger.WriteToLog(str, level);
|
//if (Level >= level)
|
||||||
|
// CrestronConsole.PrintLine("App {0}:{1}", InitialParametersClass.ApplicationNumber, str);
|
||||||
|
// CrestronLogger.WriteToLog(str, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -455,11 +724,13 @@ namespace PepperDash.Core
|
||||||
/// or above the level provided, then the output will be written to both console and the log. Otherwise
|
/// or above the level provided, then the output will be written to both console and the log. Otherwise
|
||||||
/// it will only be written to the log.
|
/// it will only be written to the log.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Use LogMessage methods")]
|
||||||
public static void ConsoleWithLog(uint level, IKeyed dev, string format, params object[] items)
|
public static void ConsoleWithLog(uint level, IKeyed dev, string format, params object[] items)
|
||||||
{
|
{
|
||||||
var str = string.Format(format, items);
|
LogMessage(level, dev, format, items);
|
||||||
if (Level >= level)
|
|
||||||
ConsoleWithLog(level, "[{0}] {1}", dev.Key, str);
|
// var str = string.Format(format, items);
|
||||||
|
// CrestronLogger.WriteToLog(string.Format("[{0}] {1}", dev.Key, str), level);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -467,20 +738,19 @@ namespace PepperDash.Core
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="errorLogLevel"></param>
|
/// <param name="errorLogLevel"></param>
|
||||||
/// <param name="str"></param>
|
/// <param name="str"></param>
|
||||||
|
[Obsolete("Use LogMessage methods")]
|
||||||
public static void LogError(ErrorLogLevel errorLogLevel, string str)
|
public static void LogError(ErrorLogLevel errorLogLevel, string str)
|
||||||
{
|
{
|
||||||
|
|
||||||
var msg = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? string.Format("App {0}:{1}", InitialParametersClass.ApplicationNumber, str) : string.Format("Room {0}:{1}", InitialParametersClass.RoomId, str);
|
|
||||||
switch (errorLogLevel)
|
switch (errorLogLevel)
|
||||||
{
|
{
|
||||||
case ErrorLogLevel.Error:
|
case ErrorLogLevel.Error:
|
||||||
ErrorLog.Error(msg);
|
LogMessage(LogEventLevel.Error, str);
|
||||||
break;
|
break;
|
||||||
case ErrorLogLevel.Warning:
|
case ErrorLogLevel.Warning:
|
||||||
ErrorLog.Warn(msg);
|
LogMessage(LogEventLevel.Warning, str);
|
||||||
break;
|
break;
|
||||||
case ErrorLogLevel.Notice:
|
case ErrorLogLevel.Notice:
|
||||||
ErrorLog.Notice(msg);
|
LogMessage(LogEventLevel.Information, str);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -553,7 +823,7 @@ namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)
|
if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)
|
||||||
{
|
{
|
||||||
CheckForMigration();
|
// CheckForMigration();
|
||||||
return string.Format(@"\user\debugSettings\program{0}", InitialParametersClass.ApplicationNumber);
|
return string.Format(@"\user\debugSettings\program{0}", InitialParametersClass.ApplicationNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
55
src/Logging/DebugConsoleSink.cs
Normal file
55
src/Logging/DebugConsoleSink.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
using Crestron.SimplSharp;
|
||||||
|
using Serilog.Configuration;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
using Serilog.Formatting;
|
||||||
|
using Serilog.Formatting.Json;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
|
||||||
|
namespace PepperDash.Core
|
||||||
|
{
|
||||||
|
public class DebugConsoleSink : ILogEventSink
|
||||||
|
{
|
||||||
|
private readonly ITextFormatter _textFormatter;
|
||||||
|
|
||||||
|
public void Emit(LogEvent logEvent)
|
||||||
|
{
|
||||||
|
if (!Debug.IsRunningOnAppliance) return;
|
||||||
|
|
||||||
|
/*string message = $"[{logEvent.Timestamp}][{logEvent.Level}][App {InitialParametersClass.ApplicationNumber}]{logEvent.RenderMessage()}";
|
||||||
|
|
||||||
|
if(logEvent.Properties.TryGetValue("Key",out var value) && value is ScalarValue sv && sv.Value is string rawValue)
|
||||||
|
{
|
||||||
|
message = $"[{logEvent.Timestamp}][{logEvent.Level}][App {InitialParametersClass.ApplicationNumber}][{rawValue,3}]: {logEvent.RenderMessage()}";
|
||||||
|
}*/
|
||||||
|
|
||||||
|
var buffer = new StringWriter(new StringBuilder(256));
|
||||||
|
|
||||||
|
_textFormatter.Format(logEvent, buffer);
|
||||||
|
|
||||||
|
var message = buffer.ToString();
|
||||||
|
|
||||||
|
CrestronConsole.PrintLine(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DebugConsoleSink(ITextFormatter formatProvider )
|
||||||
|
{
|
||||||
|
_textFormatter = formatProvider ?? new JsonFormatter();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DebugConsoleSinkExtensions
|
||||||
|
{
|
||||||
|
public static LoggerConfiguration DebugConsoleSink(
|
||||||
|
this LoggerSinkConfiguration loggerConfiguration,
|
||||||
|
ITextFormatter formatProvider = null)
|
||||||
|
{
|
||||||
|
return loggerConfiguration.Sink(new DebugConsoleSink(formatProvider));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Crestron.SimplSharp.CrestronDataStore;
|
|
||||||
using Crestron.SimplSharp.CrestronIO;
|
using Crestron.SimplSharp.CrestronIO;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using PepperDash.Core.DebugThings;
|
|
||||||
|
|
||||||
|
|
||||||
namespace PepperDash.Core
|
namespace PepperDash.Core
|
||||||
|
|
@ -101,7 +98,7 @@ namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(levelString.Trim()))
|
if (string.IsNullOrEmpty(levelString.Trim()))
|
||||||
{
|
{
|
||||||
CrestronConsole.PrintLine("AppDebug level = {0}", SaveData.Level);
|
CrestronConsole.ConsoleCommandResponse("AppDebug level = {0}", SaveData.Level);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
29
src/Logging/DebugCrestronLoggerSink.cs
Normal file
29
src/Logging/DebugCrestronLoggerSink.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
using Crestron.SimplSharp;
|
||||||
|
using Crestron.SimplSharp.CrestronLogger;
|
||||||
|
using Serilog.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
|
namespace PepperDash.Core.Logging
|
||||||
|
{
|
||||||
|
public class DebugCrestronLoggerSink : ILogEventSink
|
||||||
|
{
|
||||||
|
public void Emit(LogEvent logEvent)
|
||||||
|
{
|
||||||
|
if (!Debug.IsRunningOnAppliance) return;
|
||||||
|
|
||||||
|
string message = $"[{logEvent.Timestamp}][{logEvent.Level}][App {InitialParametersClass.ApplicationNumber}]{logEvent.RenderMessage()}";
|
||||||
|
|
||||||
|
if (logEvent.Properties.TryGetValue("Key", out var value) && value is ScalarValue sv && sv.Value is string rawValue)
|
||||||
|
{
|
||||||
|
message = $"[{logEvent.Timestamp}][{logEvent.Level}][App {InitialParametersClass.ApplicationNumber}][{rawValue}]: {logEvent.RenderMessage()}";
|
||||||
|
}
|
||||||
|
|
||||||
|
CrestronLogger.WriteToLog(message, (uint)logEvent.Level);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DebugCrestronLoggerSink()
|
||||||
|
{
|
||||||
|
CrestronLogger.Initialize(1, LoggerModeEnum.RM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
65
src/Logging/DebugErrorLogSink.cs
Normal file
65
src/Logging/DebugErrorLogSink.cs
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
using Crestron.SimplSharp;
|
||||||
|
using Serilog.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
using Serilog.Formatting;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace PepperDash.Core.Logging
|
||||||
|
{
|
||||||
|
public class DebugErrorLogSink : ILogEventSink
|
||||||
|
{
|
||||||
|
private ITextFormatter _formatter;
|
||||||
|
|
||||||
|
private Dictionary<LogEventLevel, Action<string>> _errorLogMap = new Dictionary<LogEventLevel, Action<string>>
|
||||||
|
{
|
||||||
|
{ LogEventLevel.Verbose, (msg) => ErrorLog.Notice(msg) },
|
||||||
|
{LogEventLevel.Debug, (msg) => ErrorLog.Notice(msg) },
|
||||||
|
{LogEventLevel.Information, (msg) => ErrorLog.Notice(msg) },
|
||||||
|
{LogEventLevel.Warning, (msg) => ErrorLog.Warn(msg) },
|
||||||
|
{LogEventLevel.Error, (msg) => ErrorLog.Error(msg) },
|
||||||
|
{LogEventLevel.Fatal, (msg) => ErrorLog.Error(msg) }
|
||||||
|
};
|
||||||
|
public void Emit(LogEvent logEvent)
|
||||||
|
{
|
||||||
|
string message;
|
||||||
|
|
||||||
|
if (_formatter == null)
|
||||||
|
{
|
||||||
|
var programId = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance
|
||||||
|
? $"App {InitialParametersClass.ApplicationNumber}"
|
||||||
|
: $"Room {InitialParametersClass.RoomId}";
|
||||||
|
|
||||||
|
message = $"[{logEvent.Timestamp}][{logEvent.Level}][{programId}]{logEvent.RenderMessage()}";
|
||||||
|
|
||||||
|
if (logEvent.Properties.TryGetValue("Key", out var value) && value is ScalarValue sv && sv.Value is string rawValue)
|
||||||
|
{
|
||||||
|
message = $"[{logEvent.Timestamp}][{logEvent.Level}][{programId}][{rawValue}]: {logEvent.RenderMessage()}";
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
var buffer = new StringWriter(new StringBuilder(256));
|
||||||
|
|
||||||
|
_formatter.Format(logEvent, buffer);
|
||||||
|
|
||||||
|
message = buffer.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!_errorLogMap.TryGetValue(logEvent.Level, out var handler))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handler(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DebugErrorLogSink(ITextFormatter formatter = null)
|
||||||
|
{
|
||||||
|
_formatter = formatter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/Logging/DebugExtensions.cs
Normal file
44
src/Logging/DebugExtensions.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Events;
|
||||||
|
using System;
|
||||||
|
using Log = PepperDash.Core.Debug;
|
||||||
|
|
||||||
|
namespace PepperDash.Core.Logging
|
||||||
|
{
|
||||||
|
public static class DebugExtensions
|
||||||
|
{
|
||||||
|
public static void LogException(this IKeyed device, Exception ex, string message, params object[] args)
|
||||||
|
{
|
||||||
|
Log.LogMessage(ex, message, device, args);
|
||||||
|
}
|
||||||
|
public static void LogVerbose(this IKeyed device, string message, params object[] args)
|
||||||
|
{
|
||||||
|
Log.LogMessage(LogEventLevel.Verbose, device, message, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogDebug(this IKeyed device, string message, params object[] args)
|
||||||
|
{
|
||||||
|
Log.LogMessage(LogEventLevel.Debug, device, message, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogInformation(this IKeyed device, string message, params object[] args)
|
||||||
|
{
|
||||||
|
Log.LogMessage(LogEventLevel.Information, device, message, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogWarning(this IKeyed device, string message, params object[] args)
|
||||||
|
{
|
||||||
|
Log.LogMessage(LogEventLevel.Warning, device, message, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogError(this IKeyed device, string message, params object[] args)
|
||||||
|
{
|
||||||
|
Log.LogMessage(LogEventLevel.Error, device, message, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogFatal(this IKeyed device, string message, params object[] args)
|
||||||
|
{
|
||||||
|
Log.LogMessage(LogEventLevel.Fatal, device, message, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace PepperDash.Core.DebugThings
|
namespace PepperDash.Core.Logging
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class to persist current Debug settings across program restarts
|
/// Class to persist current Debug settings across program restarts
|
||||||
271
src/Logging/DebugWebsocketSink.cs
Normal file
271
src/Logging/DebugWebsocketSink.cs
Normal file
|
|
@ -0,0 +1,271 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
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
|
||||||
|
{
|
||||||
|
public class DebugWebsocketSink : ILogEventSink
|
||||||
|
{
|
||||||
|
private HttpServer _httpsServer;
|
||||||
|
|
||||||
|
private string _path = "/debug/join/";
|
||||||
|
private const string _certificateName = "selfCres";
|
||||||
|
private const string _certificatePassword = "cres12345";
|
||||||
|
|
||||||
|
public int Port
|
||||||
|
{ get
|
||||||
|
{
|
||||||
|
|
||||||
|
if(_httpsServer == null) return 0;
|
||||||
|
return _httpsServer.Port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Url
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_httpsServer == null) return "";
|
||||||
|
return $"wss://{CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)}:{_httpsServer.Port}{_httpsServer.WebSocketServices[_path].Path}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsRunning { get => _httpsServer?.IsListening ?? false; }
|
||||||
|
|
||||||
|
|
||||||
|
private readonly ITextFormatter _textFormatter;
|
||||||
|
|
||||||
|
public DebugWebsocketSink(ITextFormatter formatProvider)
|
||||||
|
{
|
||||||
|
|
||||||
|
_textFormatter = formatProvider ?? new JsonFormatter();
|
||||||
|
|
||||||
|
if (!File.Exists($"\\user\\{_certificateName}.pfx"))
|
||||||
|
CreateCert(null);
|
||||||
|
|
||||||
|
CrestronEnvironment.ProgramStatusEventHandler += type =>
|
||||||
|
{
|
||||||
|
if (type == eProgramStatusEventType.Stopping)
|
||||||
|
{
|
||||||
|
StopServer();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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), new[] { string.Format("{0}.{1}", hostName, domainName), ipAddress }, new[] { KeyPurposeID.IdKPServerAuth, KeyPurposeID.IdKPClientAuth });
|
||||||
|
//Crestron fails to let us do this...perhaps it should be done through their Dll's but haven't tested
|
||||||
|
//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, @"\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(string.Format("WSS CreateCert Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Emit(LogEvent logEvent)
|
||||||
|
{
|
||||||
|
if (_httpsServer == null || !_httpsServer.IsListening) return;
|
||||||
|
|
||||||
|
var sw = new StringWriter();
|
||||||
|
_textFormatter.Format(logEvent, sw);
|
||||||
|
|
||||||
|
_httpsServer.WebSocketServices.Broadcast(sw.ToString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartServerAndSetPort(int port)
|
||||||
|
{
|
||||||
|
Debug.Console(0, "Starting Websocket Server on port: {0}", port);
|
||||||
|
|
||||||
|
|
||||||
|
Start(port, $"\\user\\{_certificateName}.pfx", _certificatePassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Start(int port, string certPath = "", string certPassword = "")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_httpsServer = new HttpServer(port, true);
|
||||||
|
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(certPath))
|
||||||
|
{
|
||||||
|
Debug.Console(0, "Assigning SSL Configuration");
|
||||||
|
_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
|
||||||
|
ClientCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
|
||||||
|
{
|
||||||
|
Debug.Console(0, "HTTPS ClientCerticateValidation Callback triggered");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Debug.Console(0, "Adding Debug Client Service");
|
||||||
|
_httpsServer.AddWebSocketService<DebugClient>(_path);
|
||||||
|
Debug.Console(0, "Assigning Log Info");
|
||||||
|
_httpsServer.Log.Level = LogLevel.Trace;
|
||||||
|
_httpsServer.Log.Output = (d, s) =>
|
||||||
|
{
|
||||||
|
uint level;
|
||||||
|
|
||||||
|
switch(d.Level)
|
||||||
|
{
|
||||||
|
case WebSocketSharp.LogLevel.Fatal:
|
||||||
|
level = 3;
|
||||||
|
break;
|
||||||
|
case WebSocketSharp.LogLevel.Error:
|
||||||
|
level = 2;
|
||||||
|
break;
|
||||||
|
case WebSocketSharp.LogLevel.Warn:
|
||||||
|
level = 1;
|
||||||
|
break;
|
||||||
|
case WebSocketSharp.LogLevel.Info:
|
||||||
|
level = 0;
|
||||||
|
break;
|
||||||
|
case WebSocketSharp.LogLevel.Debug:
|
||||||
|
level = 4;
|
||||||
|
break;
|
||||||
|
case WebSocketSharp.LogLevel.Trace:
|
||||||
|
level = 5;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
level = 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Console(level, "{1} {0}\rCaller:{2}\rMessage:{3}\rs:{4}", d.Level.ToString(), d.Date.ToString(), d.Caller.ToString(), d.Message, s);
|
||||||
|
};
|
||||||
|
Debug.Console(0, "Starting");
|
||||||
|
|
||||||
|
_httpsServer.Start();
|
||||||
|
Debug.Console(0, "Ready");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.Console(0, "WebSocket Failed to start {0}", ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopServer()
|
||||||
|
{
|
||||||
|
Debug.Console(0, "Stopping Websocket Server");
|
||||||
|
_httpsServer?.Stop();
|
||||||
|
|
||||||
|
_httpsServer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DebugWebsocketSinkExtensions
|
||||||
|
{
|
||||||
|
public static LoggerConfiguration DebugWebsocketSink(
|
||||||
|
this LoggerSinkConfiguration loggerConfiguration,
|
||||||
|
ITextFormatter formatProvider = null)
|
||||||
|
{
|
||||||
|
return loggerConfiguration.Sink(new DebugWebsocketSink(formatProvider));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DebugClient : WebSocketBehavior
|
||||||
|
{
|
||||||
|
private DateTime _connectionTime;
|
||||||
|
|
||||||
|
public TimeSpan ConnectedDuration
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Context.WebSocket.IsAlive)
|
||||||
|
{
|
||||||
|
return DateTime.Now - _connectionTime;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new TimeSpan(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DebugClient()
|
||||||
|
{
|
||||||
|
Debug.Console(0, "DebugClient Created");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnOpen()
|
||||||
|
{
|
||||||
|
base.OnOpen();
|
||||||
|
|
||||||
|
var url = Context.WebSocket.Url;
|
||||||
|
Debug.Console(0, Debug.ErrorLogLevel.Notice, "New WebSocket Connection from: {0}", url);
|
||||||
|
|
||||||
|
_connectionTime = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnMessage(MessageEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnMessage(e);
|
||||||
|
|
||||||
|
Debug.Console(0, "WebSocket UiClient Message: {0}", e.Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnClose(CloseEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnClose(e);
|
||||||
|
|
||||||
|
Debug.Console(0, Debug.ErrorLogLevel.Notice, "WebSocket UiClient Closing: {0} reason: {1}", e.Code, e.Reason);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnError(WebSocketSharp.ErrorEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnError(e);
|
||||||
|
|
||||||
|
Debug.Console(2, Debug.ErrorLogLevel.Notice, "WebSocket UiClient Error: {0} message: {1}", e.Exception, e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
|
|
||||||
namespace PepperDash.Core.PasswordManagement
|
namespace PepperDash.Core.PasswordManagement
|
||||||
{
|
{
|
||||||
|
|
@ -1,12 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using PepperDash.Core.JsonToSimpl;
|
|
||||||
using PepperDash.Core.JsonStandardObjects;
|
|
||||||
|
|
||||||
namespace PepperDash.Core.PasswordManagement
|
namespace PepperDash.Core.PasswordManagement
|
||||||
{
|
{
|
||||||
63
src/PepperDash.Core.4Series.csproj
Normal file
63
src/PepperDash.Core.4Series.csproj
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<RootNamespace>PepperDash.Core</RootNamespace>
|
||||||
|
<AssemblyName>PepperDashCore</AssemblyName>
|
||||||
|
<TargetFramework>net472</TargetFramework>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
<NeutralLanguage>en</NeutralLanguage>
|
||||||
|
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||||
|
<SignAssembly>False</SignAssembly>
|
||||||
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
|
<Title>PepperDash Core</Title>
|
||||||
|
<Company>PepperDash Technologies</Company>
|
||||||
|
<RepositoryType>git</RepositoryType>
|
||||||
|
<RepositoryUrl>https://github.com/PepperDash/PepperDashCore</RepositoryUrl>
|
||||||
|
<PackageTags>crestron;4series;</PackageTags>
|
||||||
|
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||||
|
<InformationalVersion>$(Version)</InformationalVersion>
|
||||||
|
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|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>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="lib\**" />
|
||||||
|
<Compile Remove="Properties\**" />
|
||||||
|
<EmbeddedResource Remove="lib\**" />
|
||||||
|
<EmbeddedResource Remove="Properties\**" />
|
||||||
|
<None Remove="lib\**" />
|
||||||
|
<None Remove="Properties\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<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" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="Comm\._GenericSshClient.cs" />
|
||||||
|
<Compile Remove="Comm\._GenericTcpIpClient.cs" />
|
||||||
|
<Compile Remove="Comm\DynamicTCPServer.cs" />
|
||||||
|
<Compile Remove="PasswordManagement\OLD-ARRAY-Config.cs" />
|
||||||
|
<Compile Remove="PasswordManagement\OLD-ARRAY-PasswordClient.cs" />
|
||||||
|
<Compile Remove="PasswordManagement\OLD-ARRAY-PasswordManager.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
356
src/Web/BouncyCertificate.cs
Normal file
356
src/Web/BouncyCertificate.cs
Normal file
|
|
@ -0,0 +1,356 @@
|
||||||
|
using Crestron.SimplSharp;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Org.BouncyCastle.Asn1;
|
||||||
|
using Org.BouncyCastle.Asn1.X509;
|
||||||
|
using Org.BouncyCastle.Crypto;
|
||||||
|
using Org.BouncyCastle.Crypto.Generators;
|
||||||
|
using Org.BouncyCastle.Crypto.Prng;
|
||||||
|
using Org.BouncyCastle.Pkcs;
|
||||||
|
using Org.BouncyCastle.Security;
|
||||||
|
using Org.BouncyCastle.Utilities;
|
||||||
|
using Org.BouncyCastle.X509;
|
||||||
|
using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2;
|
||||||
|
using X509KeyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags;
|
||||||
|
using X509ContentType = System.Security.Cryptography.X509Certificates.X509ContentType;
|
||||||
|
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
|
||||||
|
{
|
||||||
|
public string CertificatePassword { get; set; } = "password";
|
||||||
|
public X509Certificate2 LoadCertificate(string issuerFileName, string password)
|
||||||
|
{
|
||||||
|
// We need to pass 'Exportable', otherwise we can't get the private key.
|
||||||
|
var issuerCertificate = new X509Certificate2(issuerFileName, password, X509KeyStorageFlags.Exportable);
|
||||||
|
return issuerCertificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate2 IssueCertificate(string subjectName, X509Certificate2 issuerCertificate, string[] subjectAlternativeNames, KeyPurposeID[] usages)
|
||||||
|
{
|
||||||
|
// It's self-signed, so these are the same.
|
||||||
|
var issuerName = issuerCertificate.Subject;
|
||||||
|
|
||||||
|
var random = GetSecureRandom();
|
||||||
|
var subjectKeyPair = GenerateKeyPair(random, 2048);
|
||||||
|
|
||||||
|
var issuerKeyPair = DotNetUtilities.GetKeyPair(issuerCertificate.PrivateKey);
|
||||||
|
|
||||||
|
var serialNumber = GenerateSerialNumber(random);
|
||||||
|
var issuerSerialNumber = new BigInteger(issuerCertificate.GetSerialNumber());
|
||||||
|
|
||||||
|
const bool isCertificateAuthority = false;
|
||||||
|
var certificate = GenerateCertificate(random, subjectName, subjectKeyPair, serialNumber,
|
||||||
|
subjectAlternativeNames, issuerName, issuerKeyPair,
|
||||||
|
issuerSerialNumber, isCertificateAuthority,
|
||||||
|
usages);
|
||||||
|
return ConvertCertificate(certificate, subjectKeyPair, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate2 CreateCertificateAuthorityCertificate(string subjectName, string[] subjectAlternativeNames, KeyPurposeID[] usages)
|
||||||
|
{
|
||||||
|
// It's self-signed, so these are the same.
|
||||||
|
var issuerName = subjectName;
|
||||||
|
|
||||||
|
var random = GetSecureRandom();
|
||||||
|
var subjectKeyPair = GenerateKeyPair(random, 2048);
|
||||||
|
|
||||||
|
// It's self-signed, so these are the same.
|
||||||
|
var issuerKeyPair = subjectKeyPair;
|
||||||
|
|
||||||
|
var serialNumber = GenerateSerialNumber(random);
|
||||||
|
var issuerSerialNumber = serialNumber; // Self-signed, so it's the same serial number.
|
||||||
|
|
||||||
|
const bool isCertificateAuthority = true;
|
||||||
|
var certificate = GenerateCertificate(random, subjectName, subjectKeyPair, serialNumber,
|
||||||
|
subjectAlternativeNames, issuerName, issuerKeyPair,
|
||||||
|
issuerSerialNumber, isCertificateAuthority,
|
||||||
|
usages);
|
||||||
|
return ConvertCertificate(certificate, subjectKeyPair, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate2 CreateSelfSignedCertificate(string subjectName, string[] subjectAlternativeNames, KeyPurposeID[] usages)
|
||||||
|
{
|
||||||
|
// It's self-signed, so these are the same.
|
||||||
|
var issuerName = subjectName;
|
||||||
|
|
||||||
|
var random = GetSecureRandom();
|
||||||
|
var subjectKeyPair = GenerateKeyPair(random, 2048);
|
||||||
|
|
||||||
|
// It's self-signed, so these are the same.
|
||||||
|
var issuerKeyPair = subjectKeyPair;
|
||||||
|
|
||||||
|
var serialNumber = GenerateSerialNumber(random);
|
||||||
|
var issuerSerialNumber = serialNumber; // Self-signed, so it's the same serial number.
|
||||||
|
|
||||||
|
const bool isCertificateAuthority = false;
|
||||||
|
var certificate = GenerateCertificate(random, subjectName, subjectKeyPair, serialNumber,
|
||||||
|
subjectAlternativeNames, issuerName, issuerKeyPair,
|
||||||
|
issuerSerialNumber, isCertificateAuthority,
|
||||||
|
usages);
|
||||||
|
return ConvertCertificate(certificate, subjectKeyPair, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecureRandom GetSecureRandom()
|
||||||
|
{
|
||||||
|
// Since we're on Windows, we'll use the CryptoAPI one (on the assumption
|
||||||
|
// that it might have access to better sources of entropy than the built-in
|
||||||
|
// Bouncy Castle ones):
|
||||||
|
var randomGenerator = new CryptoApiRandomGenerator();
|
||||||
|
var random = new SecureRandom(randomGenerator);
|
||||||
|
return random;
|
||||||
|
}
|
||||||
|
|
||||||
|
private X509Certificate GenerateCertificate(SecureRandom random,
|
||||||
|
string subjectName,
|
||||||
|
AsymmetricCipherKeyPair subjectKeyPair,
|
||||||
|
BigInteger subjectSerialNumber,
|
||||||
|
string[] subjectAlternativeNames,
|
||||||
|
string issuerName,
|
||||||
|
AsymmetricCipherKeyPair issuerKeyPair,
|
||||||
|
BigInteger issuerSerialNumber,
|
||||||
|
bool isCertificateAuthority,
|
||||||
|
KeyPurposeID[] usages)
|
||||||
|
{
|
||||||
|
var certificateGenerator = new X509V3CertificateGenerator();
|
||||||
|
|
||||||
|
certificateGenerator.SetSerialNumber(subjectSerialNumber);
|
||||||
|
|
||||||
|
var issuerDN = new X509Name(issuerName);
|
||||||
|
certificateGenerator.SetIssuerDN(issuerDN);
|
||||||
|
|
||||||
|
// Note: The subject can be omitted if you specify a subject alternative name (SAN).
|
||||||
|
var subjectDN = new X509Name(subjectName);
|
||||||
|
certificateGenerator.SetSubjectDN(subjectDN);
|
||||||
|
|
||||||
|
// Our certificate needs valid from/to values.
|
||||||
|
var notBefore = DateTime.UtcNow.Date;
|
||||||
|
var notAfter = notBefore.AddYears(2);
|
||||||
|
|
||||||
|
certificateGenerator.SetNotBefore(notBefore);
|
||||||
|
certificateGenerator.SetNotAfter(notAfter);
|
||||||
|
|
||||||
|
// The subject's public key goes in the certificate.
|
||||||
|
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
|
||||||
|
|
||||||
|
AddAuthorityKeyIdentifier(certificateGenerator, issuerDN, issuerKeyPair, issuerSerialNumber);
|
||||||
|
AddSubjectKeyIdentifier(certificateGenerator, subjectKeyPair);
|
||||||
|
//AddBasicConstraints(certificateGenerator, isCertificateAuthority);
|
||||||
|
|
||||||
|
if (usages != null && usages.Any())
|
||||||
|
AddExtendedKeyUsage(certificateGenerator, usages);
|
||||||
|
|
||||||
|
if (subjectAlternativeNames != null && subjectAlternativeNames.Any())
|
||||||
|
AddSubjectAlternativeNames(certificateGenerator, subjectAlternativeNames);
|
||||||
|
|
||||||
|
// Set the signature algorithm. This is used to generate the thumbprint which is then signed
|
||||||
|
// with the issuer's private key. We'll use SHA-256, which is (currently) considered fairly strong.
|
||||||
|
const string signatureAlgorithm = "SHA256WithRSA";
|
||||||
|
|
||||||
|
// The certificate is signed with the issuer's private key.
|
||||||
|
ISignatureFactory signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, issuerKeyPair.Private, random);
|
||||||
|
var certificate = certificateGenerator.Generate(signatureFactory);
|
||||||
|
return certificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The certificate needs a serial number. This is used for revocation,
|
||||||
|
/// and usually should be an incrementing index (which makes it easier to revoke a range of certificates).
|
||||||
|
/// Since we don't have anywhere to store the incrementing index, we can just use a random number.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="random"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private BigInteger GenerateSerialNumber(SecureRandom random)
|
||||||
|
{
|
||||||
|
var serialNumber =
|
||||||
|
BigIntegers.CreateRandomInRange(
|
||||||
|
BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
|
||||||
|
return serialNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate a key pair.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="random">The random number generator.</param>
|
||||||
|
/// <param name="strength">The key length in bits. For RSA, 2048 bits should be considered the minimum acceptable these days.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private AsymmetricCipherKeyPair GenerateKeyPair(SecureRandom random, int strength)
|
||||||
|
{
|
||||||
|
var keyGenerationParameters = new KeyGenerationParameters(random, strength);
|
||||||
|
|
||||||
|
var keyPairGenerator = new RsaKeyPairGenerator();
|
||||||
|
keyPairGenerator.Init(keyGenerationParameters);
|
||||||
|
var subjectKeyPair = keyPairGenerator.GenerateKeyPair();
|
||||||
|
return subjectKeyPair;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add the Authority Key Identifier. According to http://www.alvestrand.no/objectid/2.5.29.35.html, this
|
||||||
|
/// identifies the public key to be used to verify the signature on this certificate.
|
||||||
|
/// In a certificate chain, this corresponds to the "Subject Key Identifier" on the *issuer* certificate.
|
||||||
|
/// The Bouncy Castle documentation, at http://www.bouncycastle.org/wiki/display/JA1/X.509+Public+Key+Certificate+and+Certification+Request+Generation,
|
||||||
|
/// shows how to create this from the issuing certificate. Since we're creating a self-signed certificate, we have to do this slightly differently.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="certificateGenerator"></param>
|
||||||
|
/// <param name="issuerDN"></param>
|
||||||
|
/// <param name="issuerKeyPair"></param>
|
||||||
|
/// <param name="issuerSerialNumber"></param>
|
||||||
|
private void AddAuthorityKeyIdentifier(X509V3CertificateGenerator certificateGenerator,
|
||||||
|
X509Name issuerDN,
|
||||||
|
AsymmetricCipherKeyPair issuerKeyPair,
|
||||||
|
BigInteger issuerSerialNumber)
|
||||||
|
{
|
||||||
|
var authorityKeyIdentifierExtension =
|
||||||
|
new AuthorityKeyIdentifier(
|
||||||
|
SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(issuerKeyPair.Public),
|
||||||
|
new GeneralNames(new GeneralName(issuerDN)),
|
||||||
|
issuerSerialNumber);
|
||||||
|
certificateGenerator.AddExtension(
|
||||||
|
X509Extensions.AuthorityKeyIdentifier.Id, false, authorityKeyIdentifierExtension);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add the "Subject Alternative Names" extension. Note that you have to repeat
|
||||||
|
/// the value from the "Subject Name" property.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="certificateGenerator"></param>
|
||||||
|
/// <param name="subjectAlternativeNames"></param>
|
||||||
|
private void AddSubjectAlternativeNames(X509V3CertificateGenerator certificateGenerator,
|
||||||
|
IEnumerable<string> subjectAlternativeNames)
|
||||||
|
{
|
||||||
|
var subjectAlternativeNamesExtension =
|
||||||
|
new DerSequence(
|
||||||
|
subjectAlternativeNames.Select(name => new GeneralName(GeneralName.DnsName, name))
|
||||||
|
.ToArray<Asn1Encodable>());
|
||||||
|
certificateGenerator.AddExtension(
|
||||||
|
X509Extensions.SubjectAlternativeName.Id, false, subjectAlternativeNamesExtension);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add the "Extended Key Usage" extension, specifying (for example) "server authentication".
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="certificateGenerator"></param>
|
||||||
|
/// <param name="usages"></param>
|
||||||
|
private void AddExtendedKeyUsage(X509V3CertificateGenerator certificateGenerator, KeyPurposeID[] usages)
|
||||||
|
{
|
||||||
|
certificateGenerator.AddExtension(
|
||||||
|
X509Extensions.ExtendedKeyUsage.Id, false, new ExtendedKeyUsage(usages));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add the "Basic Constraints" extension.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="certificateGenerator"></param>
|
||||||
|
/// <param name="isCertificateAuthority"></param>
|
||||||
|
private void AddBasicConstraints(X509V3CertificateGenerator certificateGenerator,
|
||||||
|
bool isCertificateAuthority)
|
||||||
|
{
|
||||||
|
certificateGenerator.AddExtension(
|
||||||
|
X509Extensions.BasicConstraints.Id, true, new BasicConstraints(isCertificateAuthority));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add the Subject Key Identifier.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="certificateGenerator"></param>
|
||||||
|
/// <param name="subjectKeyPair"></param>
|
||||||
|
private void AddSubjectKeyIdentifier(X509V3CertificateGenerator certificateGenerator,
|
||||||
|
AsymmetricCipherKeyPair subjectKeyPair)
|
||||||
|
{
|
||||||
|
var subjectKeyIdentifierExtension =
|
||||||
|
new SubjectKeyIdentifier(
|
||||||
|
SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectKeyPair.Public));
|
||||||
|
certificateGenerator.AddExtension(
|
||||||
|
X509Extensions.SubjectKeyIdentifier.Id, false, subjectKeyIdentifierExtension);
|
||||||
|
}
|
||||||
|
|
||||||
|
private X509Certificate2 ConvertCertificate(X509Certificate certificate,
|
||||||
|
AsymmetricCipherKeyPair subjectKeyPair,
|
||||||
|
SecureRandom random)
|
||||||
|
{
|
||||||
|
// Now to convert the Bouncy Castle certificate to a .NET certificate.
|
||||||
|
// See http://web.archive.org/web/20100504192226/http://www.fkollmann.de/v2/post/Creating-certificates-using-BouncyCastle.aspx
|
||||||
|
// ...but, basically, we create a PKCS12 store (a .PFX file) in memory, and add the public and private key to that.
|
||||||
|
var store = new Pkcs12StoreBuilder().Build();
|
||||||
|
|
||||||
|
// What Bouncy Castle calls "alias" is the same as what Windows terms the "friendly name".
|
||||||
|
string friendlyName = certificate.SubjectDN.ToString();
|
||||||
|
|
||||||
|
// Add the certificate.
|
||||||
|
var certificateEntry = new X509CertificateEntry(certificate);
|
||||||
|
store.SetCertificateEntry(friendlyName, certificateEntry);
|
||||||
|
|
||||||
|
// Add the private key.
|
||||||
|
store.SetKeyEntry(friendlyName, new AsymmetricKeyEntry(subjectKeyPair.Private), new[] { certificateEntry });
|
||||||
|
|
||||||
|
// Convert it to an X509Certificate2 object by saving/loading it from a MemoryStream.
|
||||||
|
// It needs a password. Since we'll remove this later, it doesn't particularly matter what we use.
|
||||||
|
|
||||||
|
var stream = new MemoryStream();
|
||||||
|
store.Save(stream, CertificatePassword.ToCharArray(), random);
|
||||||
|
|
||||||
|
var convertedCertificate =
|
||||||
|
new X509Certificate2(stream.ToArray(),
|
||||||
|
CertificatePassword,
|
||||||
|
X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
|
||||||
|
return convertedCertificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteCertificate(X509Certificate2 certificate, string outputDirectory, string certName)
|
||||||
|
{
|
||||||
|
// This password is the one attached to the PFX file. Use 'null' for no password.
|
||||||
|
// Create PFX (PKCS #12) with private key
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var pfx = certificate.Export(X509ContentType.Pfx, CertificatePassword);
|
||||||
|
File.WriteAllBytes(string.Format("{0}.pfx", Path.Combine(outputDirectory, certName)), pfx);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
CrestronConsole.PrintLine(string.Format("Failed to write x509 cert pfx\r\n{0}", ex.Message));
|
||||||
|
}
|
||||||
|
// Create Base 64 encoded CER (public key only)
|
||||||
|
using (var writer = new StreamWriter($"{Path.Combine(outputDirectory, certName)}.cer", false))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var contents = string.Format("-----BEGIN CERTIFICATE-----\r\n{0}\r\n-----END CERTIFICATE-----", Convert.ToBase64String(certificate.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks));
|
||||||
|
writer.Write(contents);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
CrestronConsole.PrintLine(string.Format("Failed to write x509 cert cer\r\n{0}", ex.Message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public bool AddCertToStore(X509Certificate2 cert, System.Security.Cryptography.X509Certificates.StoreName st, System.Security.Cryptography.X509Certificates.StoreLocation sl)
|
||||||
|
{
|
||||||
|
bool bRet = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var store = new System.Security.Cryptography.X509Certificates.X509Store(st, sl);
|
||||||
|
store.Open(System.Security.Cryptography.X509Certificates.OpenFlags.ReadWrite);
|
||||||
|
store.Add(cert);
|
||||||
|
|
||||||
|
store.Close();
|
||||||
|
bRet = true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
CrestronConsole.PrintLine(string.Format("AddCertToStore Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace));
|
||||||
|
}
|
||||||
|
|
||||||
|
return bRet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
163
src/Web/RequestHandlers/WebApiBaseRequestAsyncHandler.cs
Normal file
163
src/Web/RequestHandlers/WebApiBaseRequestAsyncHandler.cs
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
using Crestron.SimplSharp.WebScripting;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace PepperDash.Core.Web.RequestHandlers
|
||||||
|
{
|
||||||
|
public abstract class WebApiBaseRequestAsyncHandler:IHttpCwsHandler
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, Func<HttpCwsContext, Task>> _handlers;
|
||||||
|
protected readonly bool EnableCors;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
protected WebApiBaseRequestAsyncHandler(bool enableCors)
|
||||||
|
{
|
||||||
|
EnableCors = enableCors;
|
||||||
|
|
||||||
|
_handlers = new Dictionary<string, Func<HttpCwsContext, Task>>
|
||||||
|
{
|
||||||
|
{"CONNECT", HandleConnect},
|
||||||
|
{"DELETE", HandleDelete},
|
||||||
|
{"GET", HandleGet},
|
||||||
|
{"HEAD", HandleHead},
|
||||||
|
{"OPTIONS", HandleOptions},
|
||||||
|
{"PATCH", HandlePatch},
|
||||||
|
{"POST", HandlePost},
|
||||||
|
{"PUT", HandlePut},
|
||||||
|
{"TRACE", HandleTrace}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
protected WebApiBaseRequestAsyncHandler()
|
||||||
|
: this(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles CONNECT method requests
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
protected virtual async Task HandleConnect(HttpCwsContext context)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 501;
|
||||||
|
context.Response.StatusDescription = "Not Implemented";
|
||||||
|
context.Response.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles DELETE method requests
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
protected virtual async Task HandleDelete(HttpCwsContext context)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 501;
|
||||||
|
context.Response.StatusDescription = "Not Implemented";
|
||||||
|
context.Response.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles GET method requests
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
protected virtual async Task HandleGet(HttpCwsContext context)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 501;
|
||||||
|
context.Response.StatusDescription = "Not Implemented";
|
||||||
|
context.Response.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles HEAD method requests
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
protected virtual async Task HandleHead(HttpCwsContext context)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 501;
|
||||||
|
context.Response.StatusDescription = "Not Implemented";
|
||||||
|
context.Response.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles OPTIONS method requests
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
protected virtual async Task HandleOptions(HttpCwsContext context)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 501;
|
||||||
|
context.Response.StatusDescription = "Not Implemented";
|
||||||
|
context.Response.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles PATCH method requests
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
protected virtual async Task HandlePatch(HttpCwsContext context)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 501;
|
||||||
|
context.Response.StatusDescription = "Not Implemented";
|
||||||
|
context.Response.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles POST method requests
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
protected virtual async Task HandlePost(HttpCwsContext context)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 501;
|
||||||
|
context.Response.StatusDescription = "Not Implemented";
|
||||||
|
context.Response.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles PUT method requests
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
protected virtual async Task HandlePut(HttpCwsContext context)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 501;
|
||||||
|
context.Response.StatusDescription = "Not Implemented";
|
||||||
|
context.Response.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles TRACE method requests
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
protected virtual async Task HandleTrace(HttpCwsContext context)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 501;
|
||||||
|
context.Response.StatusDescription = "Not Implemented";
|
||||||
|
context.Response.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Process request
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public void ProcessRequest(HttpCwsContext context)
|
||||||
|
{
|
||||||
|
if (!_handlers.TryGetValue(context.Request.HttpMethod, out Func<HttpCwsContext, Task> handler))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EnableCors)
|
||||||
|
{
|
||||||
|
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
|
||||||
|
context.Response.Headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
|
||||||
|
}
|
||||||
|
|
||||||
|
var handlerTask = handler(context);
|
||||||
|
|
||||||
|
handlerTask.GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
|
|
||||||
namespace PepperDash.Core.WebApi.Presets
|
namespace PepperDash.Core.WebApi.Presets
|
||||||
{
|
{
|
||||||
|
|
@ -1,14 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp; // For Basic SIMPL# Classes
|
using Crestron.SimplSharp; // For Basic SIMPL# Classes
|
||||||
using Crestron.SimplSharp.CrestronIO;
|
using Crestron.SimplSharp.CrestronIO;
|
||||||
using Crestron.SimplSharp.Net;
|
|
||||||
using Crestron.SimplSharp.Net.Http;
|
using Crestron.SimplSharp.Net.Http;
|
||||||
using Crestron.SimplSharp.Net.Https;
|
using Crestron.SimplSharp.Net.Https;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
using PepperDash.Core;
|
|
||||||
using PepperDash.Core.JsonToSimpl;
|
using PepperDash.Core.JsonToSimpl;
|
||||||
|
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue