mirror of
https://github.com/PepperDash/PepperDashCore.git
synced 2026-07-02 10:38:14 +00:00
Merge pull request #183 from PepperDash/release-2
Step 1 to releasing PD Core v2
This commit is contained in:
commit
0454410857
106 changed files with 14031 additions and 12933 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
|
||||
410
.gitignore
vendored
410
.gitignore
vendored
|
|
@ -1,25 +1,399 @@
|
|||
#ignore thumbnails created by windows
|
||||
Thumbs.db
|
||||
#Ignore files build by Visual Studio
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.aps
|
||||
*.pch
|
||||
*.vspscc
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# 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
|
||||
*_p.c
|
||||
*.ncb
|
||||
*.suo
|
||||
*.bak
|
||||
*.cache
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.log
|
||||
[Bb]in/
|
||||
[Dd]ebug*/
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
obj/
|
||||
[Rr]elease*/
|
||||
*.tlb
|
||||
*.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*/
|
||||
SIMPLSharpLogs/
|
||||
*.projectinfo
|
||||
*.[Rr]e[Ss]harper
|
||||
*.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
|
||||
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
|
||||
|
|
|
|||
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.
|
|
@ -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,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";
|
||||
}
|
||||
BIN
output/PepperDashCore.2.0.0-local.clz
Normal file
BIN
output/PepperDashCore.2.0.0-local.clz
Normal file
Binary file not shown.
|
|
@ -1,9 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
|
|
@ -15,38 +13,44 @@ namespace PepperDash.Core
|
|||
/// <summary>
|
||||
/// The method of control
|
||||
/// </summary>
|
||||
[JsonProperty("method")]
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public eControlMethod Method { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key of the device that contains the control port
|
||||
/// </summary>
|
||||
[JsonProperty("controlPortDevKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string ControlPortDevKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of the control port on the device specified by ControlPortDevKey
|
||||
/// </summary>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] // In case "null" is present in config on this value
|
||||
public uint ControlPortNumber { get; set; }
|
||||
[JsonProperty("controlPortNumber", NullValueHandling = NullValueHandling.Ignore)] // In case "null" is present in config on this value
|
||||
public uint? ControlPortNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the control port on the device specified by ControlPortDevKey
|
||||
/// </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; }
|
||||
|
||||
/// <summary>
|
||||
/// Properties for ethernet based communications
|
||||
/// </summary>
|
||||
[JsonProperty("tcpSshProperties", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public TcpSshPropertiesConfig TcpSshProperties { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The filename and path for the IR file
|
||||
/// </summary>
|
||||
[JsonProperty("irFile", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string IrFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The IpId of a Crestron device
|
||||
/// </summary>
|
||||
[JsonProperty("ipId", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string IpId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -58,21 +62,25 @@ namespace PepperDash.Core
|
|||
/// <summary>
|
||||
/// Char indicating end of line
|
||||
/// </summary>
|
||||
[JsonProperty("endOfLineChar", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public char EndOfLineChar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defaults to Environment.NewLine;
|
||||
/// </summary>
|
||||
[JsonProperty("endOfLineString", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string EndOfLineString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates
|
||||
/// </summary>
|
||||
[JsonProperty("deviceReadyResponsePattern", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string DeviceReadyResponsePattern { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used when communcating to programs running in VC-4
|
||||
/// </summary>
|
||||
[JsonProperty("roomId", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string RoomId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -80,7 +88,6 @@ namespace PepperDash.Core
|
|||
/// </summary>
|
||||
public ControlPropertiesConfig()
|
||||
{
|
||||
EndOfLineString = CrestronEnvironment.NewLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,8 +15,6 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
|
|
@ -1,16 +1,18 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using Crestron.SimplSharp.Ssh;
|
||||
using Crestron.SimplSharp.Ssh.Common;
|
||||
using Org.BouncyCastle.Utilities;
|
||||
using PepperDash.Core.Logging;
|
||||
using Renci.SshNet;
|
||||
using Renci.SshNet.Common;
|
||||
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
|
||||
{
|
||||
private const string SPlusKey = "Uninitialized SshClient";
|
||||
|
|
@ -133,7 +135,8 @@ namespace PepperDash.Core
|
|||
CTimer ReconnectTimer;
|
||||
|
||||
//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;
|
||||
|
||||
|
|
@ -158,7 +161,7 @@ namespace PepperDash.Core
|
|||
{
|
||||
Connect();
|
||||
}
|
||||
}, Timeout.Infinite);
|
||||
}, System.Threading.Timeout.Infinite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -176,7 +179,7 @@ namespace PepperDash.Core
|
|||
{
|
||||
Connect();
|
||||
}
|
||||
}, Timeout.Infinite);
|
||||
}, System.Threading.Timeout.Infinite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -196,7 +199,7 @@ namespace PepperDash.Core
|
|||
{
|
||||
if (Client != null)
|
||||
{
|
||||
Debug.Console(1, this, "Program stopping. Closing connection");
|
||||
this.LogDebug("Program stopping. Closing connection");
|
||||
Disconnect();
|
||||
}
|
||||
}
|
||||
|
|
@ -211,7 +214,7 @@ namespace PepperDash.Core
|
|||
if (string.IsNullOrEmpty(Hostname) || Port < 1 || Port > 65535
|
||||
|| 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;
|
||||
}
|
||||
|
||||
|
|
@ -219,14 +222,14 @@ namespace PepperDash.Core
|
|||
|
||||
try
|
||||
{
|
||||
connectLock.Enter();
|
||||
connectLock.Wait();
|
||||
if (IsConnected)
|
||||
{
|
||||
Debug.Console(1, this, "Connection already connected. Exiting Connect()");
|
||||
this.LogDebug("Connection already connected. Exiting Connect");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Console(1, this, "Attempting connect");
|
||||
this.LogDebug("Attempting connect");
|
||||
|
||||
// Cancel reconnect if running.
|
||||
if (ReconnectTimer != null)
|
||||
|
|
@ -237,7 +240,7 @@ namespace PepperDash.Core
|
|||
// Cleanup the old client if it already exists
|
||||
if (Client != null)
|
||||
{
|
||||
Debug.Console(1, this, "Cleaning up disconnected client");
|
||||
this.LogDebug("Cleaning up disconnected client");
|
||||
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
|
||||
}
|
||||
|
||||
|
|
@ -246,7 +249,7 @@ namespace PepperDash.Core
|
|||
kauth.AuthenticationPrompt += new EventHandler<AuthenticationPromptEventArgs>(kauth_AuthenticationPrompt);
|
||||
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);
|
||||
Client = new SshClient(connectionInfo);
|
||||
Client.ErrorOccurred += Client_ErrorOccurred;
|
||||
|
|
@ -263,7 +266,7 @@ namespace PepperDash.Core
|
|||
string str = TheStream.Read();
|
||||
}
|
||||
TheStream.DataReceived += Stream_DataReceived;
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Connected");
|
||||
this.LogInformation("Connected");
|
||||
ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED;
|
||||
DisconnectLogged = false;
|
||||
}
|
||||
|
|
@ -273,35 +276,52 @@ namespace PepperDash.Core
|
|||
var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
|
||||
|
||||
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);
|
||||
}
|
||||
else
|
||||
Debug.Console(1, this, errorLogLevel, "Error on connect:\r({0})", ie.Message);
|
||||
this.LogException(ie, "Error on connect");
|
||||
|
||||
DisconnectLogged = true;
|
||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||
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);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
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;
|
||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -309,7 +329,7 @@ namespace PepperDash.Core
|
|||
}
|
||||
finally
|
||||
{
|
||||
connectLock.Leave();
|
||||
connectLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -345,43 +365,15 @@ namespace PepperDash.Core
|
|||
Client.Dispose();
|
||||
Client = null;
|
||||
ClientStatus = status;
|
||||
Debug.Console(1, this, "Disconnected");
|
||||
this.LogDebug("Disconnected");
|
||||
}
|
||||
}
|
||||
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>
|
||||
/// Kills the stream
|
||||
/// </summary>
|
||||
|
|
@ -395,12 +387,12 @@ namespace PepperDash.Core
|
|||
TheStream.Close();
|
||||
TheStream.Dispose();
|
||||
TheStream = null;
|
||||
Debug.Console(1, this, "Disconnected stream");
|
||||
this.LogDebug("Disconnected stream");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception in Kill Stream:{0}", ex);
|
||||
this.LogException(ex, "Exception in Kill Stream:{0}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -417,7 +409,7 @@ namespace PepperDash.Core
|
|||
/// <summary>
|
||||
/// Handler for data receive on ShellStream. Passes data across to queue for line parsing.
|
||||
/// </summary>
|
||||
void Stream_DataReceived(object sender, Crestron.SimplSharp.Ssh.Common.ShellDataEventArgs e)
|
||||
void Stream_DataReceived(object sender, ShellDataEventArgs e)
|
||||
{
|
||||
if (((ShellStream)sender).Length <= 0L)
|
||||
{
|
||||
|
|
@ -432,7 +424,7 @@ namespace PepperDash.Core
|
|||
var bytes = Encoding.UTF8.GetBytes(response);
|
||||
if (StreamDebugging.RxStreamDebuggingIsEnabled)
|
||||
{
|
||||
Debug.Console(0, this, "Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
|
||||
this.LogInformation("Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
|
||||
}
|
||||
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
|
||||
}
|
||||
|
|
@ -441,7 +433,7 @@ namespace PepperDash.Core
|
|||
if (textHandler != null)
|
||||
{
|
||||
if (StreamDebugging.RxStreamDebuggingIsEnabled)
|
||||
Debug.Console(0, this, "Received: '{0}'", ComTextHelper.GetDebugText(response));
|
||||
this.LogInformation("Received: '{0}'", ComTextHelper.GetDebugText(response));
|
||||
|
||||
textHandler(this, new GenericCommMethodReceiveTextArgs(response));
|
||||
}
|
||||
|
|
@ -453,27 +445,26 @@ namespace PepperDash.Core
|
|||
/// Error event handler for client events - disconnect, etc. Will forward those events via ConnectionChange
|
||||
/// event
|
||||
/// </summary>
|
||||
void Client_ErrorOccurred(object sender, Crestron.SimplSharp.Ssh.Common.ExceptionEventArgs e)
|
||||
void Client_ErrorOccurred(object sender, ExceptionEventArgs e)
|
||||
{
|
||||
CrestronInvoke.BeginInvoke(o =>
|
||||
{
|
||||
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
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Unhandled SSH client error: {0}", e.Exception);
|
||||
|
||||
this.LogException(e.Exception, "Unhandled SSH client error");
|
||||
try
|
||||
{
|
||||
connectLock.Enter();
|
||||
connectLock.Wait();
|
||||
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_REMOTELY);
|
||||
}
|
||||
finally
|
||||
{
|
||||
connectLock.Leave();
|
||||
connectLock.Release();
|
||||
}
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
|
@ -501,9 +492,8 @@ namespace PepperDash.Core
|
|||
if (Client != null && TheStream != null && IsConnected)
|
||||
{
|
||||
if (StreamDebugging.TxStreamDebuggingIsEnabled)
|
||||
Debug.Console(0,
|
||||
this,
|
||||
"Sending {0} characters of text: '{1}'",
|
||||
this.LogInformation(
|
||||
"Sending {length} characters of text: '{text}'",
|
||||
text.Length,
|
||||
ComTextHelper.GetDebugText(text));
|
||||
|
||||
|
|
@ -512,23 +502,19 @@ namespace PepperDash.Core
|
|||
}
|
||||
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)
|
||||
{
|
||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception: {0}", ex.Message);
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Stack Trace: {0}", ex.StackTrace);
|
||||
this.LogException(ex, "ObjectDisposedException sending {message}", text);
|
||||
|
||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||
ReconnectTimer.Reset();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception: {0}", ex.Message);
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Stack Trace: {0}", ex.StackTrace);
|
||||
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Stream write failed");
|
||||
this.LogException(ex, "Exception sending text: {message}", text);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -538,47 +524,43 @@ namespace PepperDash.Core
|
|||
/// <param name="bytes"></param>
|
||||
public void SendBytes(byte[] bytes)
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Client != null && TheStream != null && IsConnected)
|
||||
{
|
||||
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.Flush();
|
||||
}
|
||||
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)
|
||||
{
|
||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception: {0}", ex.Message);
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Stack Trace: {0}", ex.StackTrace);
|
||||
this.LogException(ex, "ObjectDisposedException sending {message}", ComTextHelper.GetEscapedText(bytes));
|
||||
|
||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||
ReconnectTimer.Reset();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception: {0}", ex.Message);
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Stack Trace: {0}", ex.StackTrace);
|
||||
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Stream write failed");
|
||||
}
|
||||
this.LogException(ex, "Exception sending {message}", ComTextHelper.GetEscapedText(bytes));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
//*****************************************************************************************************
|
||||
//*****************************************************************************************************
|
||||
/// <summary>
|
||||
/// Fired when connection changes
|
||||
/// </summary>
|
||||
public class SshConnectionChangeEventArgs : EventArgs
|
||||
//*****************************************************************************************************
|
||||
//*****************************************************************************************************
|
||||
/// <summary>
|
||||
/// Fired when connection changes
|
||||
/// </summary>
|
||||
public class SshConnectionChangeEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Connection State
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
|
@ -15,8 +15,6 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
|
|
@ -1,17 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
|
||||
|
||||
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
|
|
@ -5,8 +5,6 @@ using System.Text;
|
|||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using System.Text.RegularExpressions;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
|
|
@ -1,13 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core;
|
||||
|
||||
namespace PepperDash.Core.Config
|
||||
|
|
@ -83,7 +79,7 @@ namespace PepperDash.Core.Config
|
|||
merged.Add("info", template["info"]);
|
||||
|
||||
merged.Add("devices", MergeArraysOnTopLevelProperty(template["devices"] as JArray,
|
||||
system["devices"] as JArray, "uid", "devices"));
|
||||
system["devices"] as JArray, "key", "devices"));
|
||||
|
||||
if (system["rooms"] == null)
|
||||
merged.Add("rooms", template["rooms"]);
|
||||
|
|
@ -121,7 +117,7 @@ namespace PepperDash.Core.Config
|
|||
else
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Serilog;
|
||||
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
|
|
@ -15,16 +16,17 @@ namespace PepperDash.Core
|
|||
/// Unique Key
|
||||
/// </summary>
|
||||
string Key { get; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public interface IKeyName : IKeyed
|
||||
{
|
||||
{
|
||||
/// <summary>
|
||||
/// Isn't it obvious :)
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Core
|
||||
{
|
||||
|
|
@ -10,6 +10,7 @@ namespace PepperDash.Core
|
|||
/// </summary>
|
||||
public class Device : IKeyName
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Unique Key
|
||||
/// </summary>
|
||||
|
|
@ -49,9 +50,8 @@ namespace PepperDash.Core
|
|||
public Device(string 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 = "";
|
||||
|
||||
}
|
||||
|
||||
/// <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 System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
using Crestron.SimplSharp;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PepperDash.Core
|
||||
|
|
@ -1,10 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core.JsonToSimpl;
|
||||
|
||||
namespace PepperDash.Core.JsonStandardObjects
|
||||
|
|
@ -1,9 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace PepperDash.Core.JsonToSimpl
|
||||
|
|
@ -1,9 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace PepperDash.Core.JsonToSimpl
|
||||
|
|
@ -1,12 +1,10 @@
|
|||
using System;
|
||||
//using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace PepperDash.Core.JsonToSimpl
|
||||
|
|
@ -196,7 +194,7 @@ namespace PepperDash.Core.JsonToSimpl
|
|||
/// Sets the debug level
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
public void setDebugLevel(int level)
|
||||
public void setDebugLevel(uint 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
|
||||
{
|
||||
|
|
@ -1,8 +1,5 @@
|
|||
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;
|
||||
|
|
@ -162,7 +159,7 @@ namespace PepperDash.Core.JsonToSimpl
|
|||
/// <returns></returns>
|
||||
public static JObject ParseObject(string json)
|
||||
{
|
||||
using (var reader = new JsonTextReader(new Crestron.SimplSharp.CrestronIO.StringReader(json)))
|
||||
using (var reader = new JsonTextReader(new StringReader(json)))
|
||||
{
|
||||
var startDepth = reader.Depth;
|
||||
var obj = JObject.Load(reader);
|
||||
|
|
@ -179,7 +176,7 @@ namespace PepperDash.Core.JsonToSimpl
|
|||
/// <returns></returns>
|
||||
public static JArray ParseArray(string json)
|
||||
{
|
||||
using (var reader = new JsonTextReader(new Crestron.SimplSharp.CrestronIO.StringReader(json)))
|
||||
using (var reader = new JsonTextReader(new StringReader(json)))
|
||||
{
|
||||
var startDepth = reader.Depth;
|
||||
var obj = JArray.Load(reader);
|
||||
|
|
@ -1,13 +1,9 @@
|
|||
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;
|
||||
|
||||
using PepperDash.Core.Config;
|
||||
|
||||
namespace PepperDash.Core.JsonToSimpl
|
||||
|
|
@ -132,7 +128,7 @@ namespace PepperDash.Core.JsonToSimpl
|
|||
///
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
public void setDebugLevel(int level)
|
||||
public void setDebugLevel(uint 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 System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
using Crestron.SimplSharp.CrestronLogger;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronDataStore;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Crestron.SimplSharp.CrestronLogger;
|
||||
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
|
||||
{
|
||||
|
|
@ -16,6 +23,43 @@ namespace PepperDash.Core
|
|||
/// </summary>
|
||||
public static class Debug
|
||||
{
|
||||
private static readonly string LevelStoreKey = "ConsoleDebugLevel";
|
||||
private static readonly string WebSocketLevelStoreKey = "WebsocketDebugLevel";
|
||||
private static readonly string ErrorLogLevelStoreKey = "ErrorLogDebugLevel";
|
||||
private static readonly string FileLevelStoreKey = "FileDebugLevel";
|
||||
|
||||
private static readonly 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>
|
||||
/// 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.
|
||||
|
|
@ -41,12 +85,14 @@ namespace PepperDash.Core
|
|||
/// <summary>
|
||||
/// When this is true, the configuration file will NOT be loaded until triggered by either a console command or a signal
|
||||
/// </summary>
|
||||
public static bool DoNotLoadOnNextBoot { get; private set; }
|
||||
public static bool DoNotLoadConfigOnNextBoot { get; private set; }
|
||||
|
||||
private static DebugContextCollection _contexts;
|
||||
|
||||
private const int SaveTimeoutMs = 30000;
|
||||
|
||||
public static bool IsRunningOnAppliance = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance;
|
||||
|
||||
/// <summary>
|
||||
/// Version for the currently loaded PepperDashCore dll
|
||||
/// </summary>
|
||||
|
|
@ -64,29 +110,93 @@ namespace PepperDash.Core
|
|||
|
||||
private static readonly Dictionary<string, object> IncludedExcludedKeys;
|
||||
|
||||
private static readonly LoggerConfiguration _defaultLoggerConfiguration;
|
||||
|
||||
private static LoggerConfiguration _loggerConfiguration;
|
||||
|
||||
public static LoggerConfiguration LoggerConfiguration => _loggerConfiguration;
|
||||
|
||||
static Debug()
|
||||
{
|
||||
CrestronDataStoreStatic.InitCrestronDataStore();
|
||||
|
||||
var defaultConsoleLevel = GetStoredLogEventLevel(LevelStoreKey);
|
||||
|
||||
var defaultWebsocketLevel = GetStoredLogEventLevel(WebSocketLevelStoreKey);
|
||||
|
||||
var defaultErrorLogLevel = GetStoredLogEventLevel(ErrorLogLevelStoreKey);
|
||||
|
||||
var defaultFileLogLevel = GetStoredLogEventLevel(FileLevelStoreKey);
|
||||
|
||||
_consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel);
|
||||
|
||||
_websocketLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel);
|
||||
|
||||
_errorLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultErrorLogLevel);
|
||||
|
||||
_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
|
||||
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);
|
||||
}
|
||||
else if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server)
|
||||
{
|
||||
msg = string.Format("[Room {0}] Using PepperDash_Core v{1}", InitialParametersClass.RoomId, PepperDashCoreVersion);
|
||||
msg = $"[Room {InitialParametersClass.RoomId}] Using PepperDash_Core v{PepperDashCoreVersion}";
|
||||
}
|
||||
|
||||
CrestronConsole.PrintLine(msg);
|
||||
|
||||
LogError(ErrorLogLevel.Notice, msg);
|
||||
LogMessage(LogEventLevel.Information,msg);
|
||||
|
||||
IncludedExcludedKeys = new Dictionary<string, object>();
|
||||
|
||||
//CrestronDataStoreStatic.InitCrestronDataStore();
|
||||
if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro)
|
||||
{
|
||||
// Add command to console
|
||||
|
|
@ -94,7 +204,7 @@ namespace PepperDash.Core
|
|||
"donotloadonnextboot:P [true/false]: Should the application load on next boot", ConsoleAccessLevelEnum.AccessOperator);
|
||||
|
||||
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);
|
||||
CrestronConsole.AddNewConsoleCommand(ShowDebugLog, "appdebuglog",
|
||||
"appdebuglog:P [all] Use \"all\" for full log.",
|
||||
|
|
@ -112,25 +222,54 @@ namespace PepperDash.Core
|
|||
|
||||
var context = _contexts.GetOrCreateItem("DEFAULT");
|
||||
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));
|
||||
|
||||
_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
|
||||
{
|
||||
if (InitialParametersClass.NumberOfRemovableDrives > 0)
|
||||
{
|
||||
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)
|
||||
{
|
||||
var result = CrestronDataStoreStatic.GetLocalIntValue(levelStoreKey, out int logLevel);
|
||||
|
||||
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)
|
||||
{
|
||||
var verAttribute = ver[0] as AssemblyInformationalVersionAttribute;
|
||||
|
||||
if (verAttribute != null)
|
||||
if (ver[0] is AssemblyInformationalVersionAttribute verAttribute)
|
||||
{
|
||||
PepperDashCoreVersion = verAttribute.InformationalVersion;
|
||||
}
|
||||
|
|
@ -153,8 +290,7 @@ namespace PepperDash.Core
|
|||
else
|
||||
{
|
||||
var version = assembly.GetName().Version;
|
||||
PepperDashCoreVersion = string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build,
|
||||
version.Revision);
|
||||
PepperDashCoreVersion = version.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -164,8 +300,11 @@ namespace PepperDash.Core
|
|||
/// <param name="programEventType"></param>
|
||||
static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
|
||||
{
|
||||
|
||||
if (programEventType == eProgramStatusEventType.Stopping)
|
||||
{
|
||||
Log.CloseAndFlush();
|
||||
|
||||
if (_saveTimer != null)
|
||||
{
|
||||
_saveTimer.Stop();
|
||||
|
|
@ -184,20 +323,121 @@ namespace PepperDash.Core
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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>
|
||||
/// Callback for console command
|
||||
/// </summary>
|
||||
|
|
@ -208,15 +448,15 @@ namespace PepperDash.Core
|
|||
{
|
||||
if (string.IsNullOrEmpty(stateString.Trim()))
|
||||
{
|
||||
CrestronConsole.PrintLine("DoNotLoadOnNextBoot = {0}", DoNotLoadOnNextBoot);
|
||||
CrestronConsole.ConsoleCommandResponse("DoNotLoadOnNextBoot = {0}", DoNotLoadConfigOnNextBoot);
|
||||
return;
|
||||
}
|
||||
|
||||
SetDoNotLoadOnNextBoot(Boolean.Parse(stateString));
|
||||
SetDoNotLoadConfigOnNextBoot(bool.Parse(stateString));
|
||||
}
|
||||
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 " +
|
||||
"+all: at beginning puts filter into 'default include' mode\r" +
|
||||
" All keys that follow will be excluded from output.\r" +
|
||||
"-all: at beginning puts filter into 'default 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" +
|
||||
"+nokey: Enables messages with no key (default)\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>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <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;
|
||||
SaveMemoryOnTimeout();
|
||||
|
||||
CrestronConsole.PrintLine("[Application {0}], Do Not Start on Next Boot set to {1}",
|
||||
InitialParametersClass.ApplicationNumber, DoNotLoadOnNextBoot);
|
||||
CrestronConsole.ConsoleCommandResponse("[Application {0}], Do Not Load Config on Next Boot set to {1}",
|
||||
InitialParametersClass.ApplicationNumber, DoNotLoadConfigOnNextBoot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -367,6 +588,66 @@ namespace PepperDash.Core
|
|||
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>
|
||||
/// Prints message to console if current debug level is equal to or higher than the level of this message.
|
||||
/// Uses CrestronConsole.PrintLine.
|
||||
|
|
@ -374,67 +655,52 @@ namespace PepperDash.Core
|
|||
/// <param name="level"></param>
|
||||
/// <param name="format">Console format string</param>
|
||||
/// <param name="items">Object parameters</param>
|
||||
[Obsolete("Use LogMessage methods")]
|
||||
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);
|
||||
return;
|
||||
}
|
||||
LogMessage(level, format, items);
|
||||
|
||||
if(Level < level)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CrestronConsole.PrintLine("[{0}]App {1}:{2}", DateTime.Now.ToString("HH:mm:ss.fff"), InitialParametersClass.ApplicationNumber,
|
||||
string.Format(format, items));
|
||||
//if (IsRunningOnAppliance)
|
||||
//{
|
||||
// CrestronConsole.PrintLine("[{0}]App {1} Lvl {2}:{3}", DateTime.Now.ToString("HH:mm:ss.fff"),
|
||||
// InitialParametersClass.ApplicationNumber,
|
||||
// level,
|
||||
// string.Format(format, items));
|
||||
//}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs to Console when at-level, and all messages to error log, including device key
|
||||
/// </summary>
|
||||
[Obsolete("Use LogMessage methods")]
|
||||
public static void Console(uint level, IKeyed dev, string format, params object[] items)
|
||||
{
|
||||
if (Level >= level)
|
||||
Console(level, "[{0}] {1}", dev.Key, string.Format(format, items));
|
||||
LogMessage(level, dev, format, items);
|
||||
|
||||
//if (Level >= level)
|
||||
// Console(level, "[{0}] {1}", dev.Key, message);
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// Uses CrestronConsole.PrintLine.
|
||||
/// </summary>
|
||||
[Obsolete("Use LogMessage methods")]
|
||||
public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel,
|
||||
string format, params object[] items)
|
||||
{
|
||||
var str = string.Format("[{0}] {1}", dev.Key, string.Format(format, items));
|
||||
if (errorLogLevel != ErrorLogLevel.None)
|
||||
{
|
||||
LogError(errorLogLevel, str);
|
||||
}
|
||||
if (Level >= level)
|
||||
{
|
||||
Console(level, str);
|
||||
}
|
||||
LogMessage(level, dev, format, items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs to Console when at-level, and all messages to error log
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// Logs to Console when at-level, and all messages to error log
|
||||
/// </summary>
|
||||
[Obsolete("Use LogMessage methods")]
|
||||
public static void Console(uint level, ErrorLogLevel errorLogLevel,
|
||||
string format, params object[] items)
|
||||
{
|
||||
var str = string.Format(format, items);
|
||||
if (errorLogLevel != ErrorLogLevel.None)
|
||||
{
|
||||
LogError(errorLogLevel, str);
|
||||
}
|
||||
if (Level >= level)
|
||||
{
|
||||
Console(level, str);
|
||||
}
|
||||
LogMessage(level, format, items);
|
||||
}
|
||||
|
||||
/// <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
|
||||
/// it will only be written to the log.
|
||||
/// </summary>
|
||||
[Obsolete("Use LogMessage methods")]
|
||||
public static void ConsoleWithLog(uint level, string format, params object[] items)
|
||||
{
|
||||
var str = string.Format(format, items);
|
||||
if (Level >= level)
|
||||
CrestronConsole.PrintLine("App {0}:{1}", InitialParametersClass.ApplicationNumber, str);
|
||||
CrestronLogger.WriteToLog(str, level);
|
||||
LogMessage(level, format, items);
|
||||
|
||||
// var str = string.Format(format, items);
|
||||
//if (Level >= level)
|
||||
// CrestronConsole.PrintLine("App {0}:{1}", InitialParametersClass.ApplicationNumber, str);
|
||||
// CrestronLogger.WriteToLog(str, level);
|
||||
}
|
||||
|
||||
/// <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
|
||||
/// it will only be written to the log.
|
||||
/// </summary>
|
||||
[Obsolete("Use LogMessage methods")]
|
||||
public static void ConsoleWithLog(uint level, IKeyed dev, string format, params object[] items)
|
||||
{
|
||||
var str = string.Format(format, items);
|
||||
if (Level >= level)
|
||||
ConsoleWithLog(level, "[{0}] {1}", dev.Key, str);
|
||||
LogMessage(level, dev, format, items);
|
||||
|
||||
// var str = string.Format(format, items);
|
||||
// CrestronLogger.WriteToLog(string.Format("[{0}] {1}", dev.Key, str), level);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -467,20 +738,19 @@ namespace PepperDash.Core
|
|||
/// </summary>
|
||||
/// <param name="errorLogLevel"></param>
|
||||
/// <param name="str"></param>
|
||||
[Obsolete("Use LogMessage methods")]
|
||||
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)
|
||||
{
|
||||
case ErrorLogLevel.Error:
|
||||
ErrorLog.Error(msg);
|
||||
LogMessage(LogEventLevel.Error, str);
|
||||
break;
|
||||
case ErrorLogLevel.Warning:
|
||||
ErrorLog.Warn(msg);
|
||||
LogMessage(LogEventLevel.Warning, str);
|
||||
break;
|
||||
case ErrorLogLevel.Notice:
|
||||
ErrorLog.Notice(msg);
|
||||
LogMessage(LogEventLevel.Information, str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -553,7 +823,7 @@ namespace PepperDash.Core
|
|||
{
|
||||
if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)
|
||||
{
|
||||
CheckForMigration();
|
||||
// CheckForMigration();
|
||||
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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronDataStore;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core.DebugThings;
|
||||
|
||||
|
||||
namespace PepperDash.Core
|
||||
|
|
@ -101,7 +98,7 @@ namespace PepperDash.Core
|
|||
{
|
||||
if (string.IsNullOrEmpty(levelString.Trim()))
|
||||
{
|
||||
CrestronConsole.PrintLine("AppDebug level = {0}", SaveData.Level);
|
||||
CrestronConsole.ConsoleCommandResponse("AppDebug level = {0}", SaveData.Level);
|
||||
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 Crestron.SimplSharp;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PepperDash.Core.DebugThings
|
||||
namespace PepperDash.Core.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// 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,12 +1,6 @@
|
|||
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
|
||||
{
|
||||
51
src/PepperDash.Core.4Series.csproj
Normal file
51
src/PepperDash.Core.4Series.csproj
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<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>
|
||||
<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>
|
||||
<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.20.66" />
|
||||
<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>
|
||||
<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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
||||
namespace PepperDash.Core.WebApi.Presets
|
||||
{
|
||||
|
|
@ -1,14 +1,10 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp; // For Basic SIMPL# Classes
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Crestron.SimplSharp.Net;
|
||||
using Crestron.SimplSharp.Net.Http;
|
||||
using Crestron.SimplSharp.Net.Https;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using PepperDash.Core;
|
||||
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