diff --git a/._Readme.md b/._Readme.md deleted file mode 100644 index 4a7c548..0000000 Binary files a/._Readme.md and /dev/null differ diff --git a/.github/workflows/EssentialsPlugins-builds-4-series-caller.yml b/.github/workflows/EssentialsPlugins-builds-4-series-caller.yml new file mode 100644 index 0000000..36588bd --- /dev/null +++ b/.github/workflows/EssentialsPlugins-builds-4-series-caller.yml @@ -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 }} \ No newline at end of file diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index 9f10395..0000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: Branch Build Using Docker - -on: - push: - branches: - - feature-2/* - - hotfix-2/* - - release-2/* - - development-2 - -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: . - 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_4-Series: - runs-on: windows-latest - steps: - - uses: actions/checkout@v3 - # Fetch all tags - - name: Fetch tags - run: git fetch --tags - # Generate the appropriate version number - - name: Set Version Number - id: setVersion - shell: powershell - run: | - $latestVersion = [version]"2.0.0" - - $newVersion = [version]$latestVersion - $phase = "" - $newVersionString = "" - - switch -regex ($Env:GITHUB_REF) { - '^refs\/pull\/*.' { - $phase = 'beta'; - $newVersionString = "{0}-{1}-{2}" -f $newVersion, $phase, $Env:GITHUB_RUN_NUMBER - } - '^refs\/heads\/feature-2\/*.' { - $phase = 'alpha' - $newVersionString = "{0}-{1}-{2}" -f $newVersion, $phase, $Env:GITHUB_RUN_NUMBER - } - 'development-2' { - $phase = 'beta' - $newVersionString = "{0}-{1}-{2}" -f $newVersion, $phase, $Env:GITHUB_RUN_NUMBER - } - } - echo "version=$newVersionString" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append - - name: Setup MS Build - uses: microsoft/setup-msbuild@v1.1 - - name: restore Nuget Packages - run: nuget restore .\$($Env:SOLUTION_FILE).sln - # Build the solutions in the docker image - - name: Build Solution - run: msbuild .\$($Env:SOLUTION_FILE).sln /p:Platform="Any CPU" /p:Configuration="Debug" /p:Version="${{ steps.setVersion.outputs.version }}" - - name: Create tag for non-rc builds - if: contains(steps.setVersion.outputs.version, 'alpha') - run: | - git tag ${{ steps.setVersion.outputs.version }} - git push --tags origin - # Create the release on the source repo - - name: Create Release - id: create_release - if: contains(steps.setVersion.outputs.version,'-rc-') || - contains(steps.setVersion.outputs.version,'-hotfix-') || - contains(steps.setVersion.outputs.version, 'beta') - uses: ncipollo/release-action@v1 - with: - artifacts: '**\*.clz' - generateReleaseNotes: true - prerelease: ${{contains('debug', env.BUILD_TYPE)}} - tag: ${{ steps.setVersion.outputs.version }} - - name: Publish to Nuget - run: | - nuget sources add -name github -source https://nuget.pkg.github.com/pepperdash/index.json -username Pepperdash -password ${{ secrets.GITHUB_TOKEN }} - nuget setApiKey ${{ secrets.NUGET_API_KEY }} - nuget push ".\package\PepperDashCore.${{ steps.setVersion.outputs.version }}.nupkg" -Source github - nuget push ".\package\PepperDashCore.${{ steps.setVersion.outputs.version }}.nupkg" -Source https://api.nuget.org/v3/index.json \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 25d211b..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Main Build using Docker - -on: - release: - types: - - created - branches: - - main-2 -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: . - 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_4-Series: - runs-on: windows-latest - steps: - # First we checkout the source repo - - name: Checkout repo - uses: actions/checkout@v3 - # 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_OUTPUT -Encoding utf8 -Append - - name: Setup MS-Build - uses: microsoft/setup-msbuild@v1 - - name: restore Nuget Packages - run: nuget restore .\$($Env:SOLUTION_FILE).sln - - name: Build Solution - run: msbuild .\$($Env:SOLUTION_FILE).sln /p:Platform="Any CPU" /p:Configuration="Release" /p:Version="${{ steps.setVersion.outputs.version }}" - - name: Update Existing Release - id: create_release - uses: ncipollo/release-action@v1 - with: - artifacts: '**\*.clz' - generateReleaseNotes: false - prerelease: false - tag: ${{ github.event.release.tag_name }} - - name: Add nuget.exe - uses: nuget/setup-nuget@v1 - - name: Publish to Nuget - run: | - nuget sources add -name github -source https://nuget.pkg.github.com/pepperdash/index.json -username Pepperdash -password ${{ secrets.GITHUB_TOKEN }} - nuget setApiKey ${{ secrets.NUGET_API_KEY }} - nuget push ".\package\PepperDashCore.${{ github.event.release.tag_name }}.nupkg" -Source github - nuget push ".\package\PepperDashCore.${{ github.event.release.tag_name }}.nupkg" -Source https://api.nuget.org/v3/index.json diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 0000000..416f7dc --- /dev/null +++ b/.releaserc.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/PepperDash Core.sln b/PepperDash Core.sln deleted file mode 100644 index 9ce1b5d..0000000 --- a/PepperDash Core.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.1.32228.430 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PepperDash_Core", "src\Pepperdash Core\PepperDash_Core.csproj", "{85BC97D4-5564-4268-8F7B-1532E2DE0456}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {85BC97D4-5564-4268-8F7B-1532E2DE0456}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {85BC97D4-5564-4268-8F7B-1532E2DE0456}.Debug|Any CPU.Build.0 = Debug|Any CPU - {85BC97D4-5564-4268-8F7B-1532E2DE0456}.Release|Any CPU.ActiveCfg = Release|Any CPU - {85BC97D4-5564-4268-8F7B-1532E2DE0456}.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 diff --git a/src/PepperDash Core.sln b/PepperDash.Core.4Series.sln similarity index 63% rename from src/PepperDash Core.sln rename to PepperDash.Core.4Series.sln index f38b242..2c47107 100644 --- a/src/PepperDash Core.sln +++ b/PepperDash.Core.4Series.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32228.430 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PepperDash_Core", "Pepperdash Core\PepperDash_Core.csproj", "{66EB9188-E7AC-410D-AD59-931131DA7C2E}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Core.4Series", "src\PepperDash.Core.4Series.csproj", "{100ABA44-9471-4B18-8092-4D94D7D82223}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,10 +11,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {66EB9188-E7AC-410D-AD59-931131DA7C2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66EB9188-E7AC-410D-AD59-931131DA7C2E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66EB9188-E7AC-410D-AD59-931131DA7C2E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66EB9188-E7AC-410D-AD59-931131DA7C2E}.Release|Any CPU.Build.0 = Release|Any CPU + {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 diff --git a/output/PepperDashCore.2.0.0-local.clz b/output/PepperDashCore.2.0.0-local.clz new file mode 100644 index 0000000..c4f7a62 Binary files /dev/null and b/output/PepperDashCore.2.0.0-local.clz differ diff --git a/src/Pepperdash Core/Comm/._GenericSshClient.cs b/src/Comm/._GenericSshClient.cs similarity index 100% rename from src/Pepperdash Core/Comm/._GenericSshClient.cs rename to src/Comm/._GenericSshClient.cs diff --git a/src/Pepperdash Core/Comm/._GenericTcpIpClient.cs b/src/Comm/._GenericTcpIpClient.cs similarity index 100% rename from src/Pepperdash Core/Comm/._GenericTcpIpClient.cs rename to src/Comm/._GenericTcpIpClient.cs diff --git a/src/Pepperdash Core/Comm/CommunicationGather.cs b/src/Comm/CommunicationGather.cs similarity index 100% rename from src/Pepperdash Core/Comm/CommunicationGather.cs rename to src/Comm/CommunicationGather.cs diff --git a/src/Pepperdash Core/Comm/CommunicationStreamDebugging.cs b/src/Comm/CommunicationStreamDebugging.cs similarity index 100% rename from src/Pepperdash Core/Comm/CommunicationStreamDebugging.cs rename to src/Comm/CommunicationStreamDebugging.cs diff --git a/src/Pepperdash Core/Comm/ControlPropertiesConfig.cs b/src/Comm/ControlPropertiesConfig.cs similarity index 65% rename from src/Pepperdash Core/Comm/ControlPropertiesConfig.cs rename to src/Comm/ControlPropertiesConfig.cs index f342f25..ff869f7 100644 --- a/src/Pepperdash Core/Comm/ControlPropertiesConfig.cs +++ b/src/Comm/ControlPropertiesConfig.cs @@ -1,6 +1,7 @@ using System; using Crestron.SimplSharp; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace PepperDash.Core { @@ -12,38 +13,44 @@ namespace PepperDash.Core /// /// The method of control /// + [JsonProperty("method")] + [JsonConverter(typeof(StringEnumConverter))] public eControlMethod Method { get; set; } /// /// The key of the device that contains the control port /// + [JsonProperty("controlPortDevKey", NullValueHandling = NullValueHandling.Ignore)] public string ControlPortDevKey { get; set; } /// /// The number of the control port on the device specified by ControlPortDevKey /// - [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; } /// /// The name of the control port on the device specified by ControlPortDevKey /// - [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; } /// /// Properties for ethernet based communications /// + [JsonProperty("tcpSshProperties", NullValueHandling = NullValueHandling.Ignore)] public TcpSshPropertiesConfig TcpSshProperties { get; set; } /// /// The filename and path for the IR file /// + [JsonProperty("irFile", NullValueHandling = NullValueHandling.Ignore)] public string IrFile { get; set; } /// /// The IpId of a Crestron device /// + [JsonProperty("ipId", NullValueHandling = NullValueHandling.Ignore)] public string IpId { get; set; } /// @@ -55,29 +62,32 @@ namespace PepperDash.Core /// /// Char indicating end of line /// + [JsonProperty("endOfLineChar", NullValueHandling = NullValueHandling.Ignore)] public char EndOfLineChar { get; set; } /// /// Defaults to Environment.NewLine; /// + [JsonProperty("endOfLineString", NullValueHandling = NullValueHandling.Ignore)] public string EndOfLineString { get; set; } /// /// Indicates /// + [JsonProperty("deviceReadyResponsePattern", NullValueHandling = NullValueHandling.Ignore)] public string DeviceReadyResponsePattern { get; set; } /// /// Used when communcating to programs running in VC-4 /// + [JsonProperty("roomId", NullValueHandling = NullValueHandling.Ignore)] public string RoomId { get; set; } /// /// Constructor /// public ControlPropertiesConfig() - { - EndOfLineString = CrestronEnvironment.NewLine; + { } } } \ No newline at end of file diff --git a/src/Pepperdash Core/Comm/DynamicTCPServer.cs b/src/Comm/DynamicTCPServer.cs similarity index 100% rename from src/Pepperdash Core/Comm/DynamicTCPServer.cs rename to src/Comm/DynamicTCPServer.cs diff --git a/src/Pepperdash Core/Comm/EventArgs.cs b/src/Comm/EventArgs.cs similarity index 100% rename from src/Pepperdash Core/Comm/EventArgs.cs rename to src/Comm/EventArgs.cs diff --git a/src/Pepperdash Core/Comm/FINISH CommStatic.cs b/src/Comm/FINISH CommStatic.cs similarity index 100% rename from src/Pepperdash Core/Comm/FINISH CommStatic.cs rename to src/Comm/FINISH CommStatic.cs diff --git a/src/Pepperdash Core/Comm/GenericHttpSseClient.cs b/src/Comm/GenericHttpSseClient.cs similarity index 100% rename from src/Pepperdash Core/Comm/GenericHttpSseClient.cs rename to src/Comm/GenericHttpSseClient.cs diff --git a/src/Pepperdash Core/Comm/GenericSecureTcpIpClient.cs b/src/Comm/GenericSecureTcpIpClient.cs similarity index 100% rename from src/Pepperdash Core/Comm/GenericSecureTcpIpClient.cs rename to src/Comm/GenericSecureTcpIpClient.cs diff --git a/src/Pepperdash Core/Comm/GenericSecureTcpIpClient_ForServer.cs b/src/Comm/GenericSecureTcpIpClient_ForServer.cs similarity index 100% rename from src/Pepperdash Core/Comm/GenericSecureTcpIpClient_ForServer.cs rename to src/Comm/GenericSecureTcpIpClient_ForServer.cs diff --git a/src/Pepperdash Core/Comm/GenericSecureTcpIpServer.cs b/src/Comm/GenericSecureTcpIpServer.cs similarity index 100% rename from src/Pepperdash Core/Comm/GenericSecureTcpIpServer.cs rename to src/Comm/GenericSecureTcpIpServer.cs diff --git a/src/Pepperdash Core/Comm/GenericSshClient.cs b/src/Comm/GenericSshClient.cs similarity index 63% rename from src/Pepperdash Core/Comm/GenericSshClient.cs rename to src/Comm/GenericSshClient.cs index 51ba8f4..ea24876 100644 --- a/src/Pepperdash Core/Comm/GenericSshClient.cs +++ b/src/Comm/GenericSshClient.cs @@ -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 { - /// - /// - /// + /// + /// + /// 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); } /// @@ -176,7 +179,7 @@ namespace PepperDash.Core { Connect(); } - }, Timeout.Infinite); + }, System.Threading.Timeout.Infinite); } /// @@ -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,22 +222,25 @@ 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. - ReconnectTimer.Stop(); + if (ReconnectTimer != null) + { + ReconnectTimer.Stop(); + } // 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); } @@ -243,11 +249,9 @@ namespace PepperDash.Core kauth.AuthenticationPrompt += new EventHandler(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; Client.ErrorOccurred += Client_ErrorOccurred; //Attempt to connect @@ -256,8 +260,13 @@ namespace PepperDash.Core { Client.Connect(); TheStream = Client.CreateShellStream("PDTShell", 100, 80, 100, 200, 65534); + if (TheStream.DataAvailable) + { + // empty the buffer if there is data + string str = TheStream.Read(); + } TheStream.DataReceived += Stream_DataReceived; - Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Connected"); + this.LogInformation("Connected"); ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED; DisconnectLogged = false; } @@ -267,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); } } @@ -303,7 +329,7 @@ namespace PepperDash.Core } finally { - connectLock.Leave(); + connectLock.Release(); } } @@ -317,7 +343,7 @@ namespace PepperDash.Core if (ReconnectTimer != null) { ReconnectTimer.Stop(); - ReconnectTimer = null; + // ReconnectTimer = null; } KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY); @@ -330,56 +356,44 @@ namespace PepperDash.Core { KillStream(); - if (Client != null) - { - Client.Disconnect(); - Client = null; - ClientStatus = status; - Debug.Console(1, this, "Disconnected"); + try + { + if (Client != null) + { + Client.ErrorOccurred -= Client_ErrorOccurred; + Client.Disconnect(); + Client.Dispose(); + Client = null; + ClientStatus = status; + this.LogDebug("Disconnected"); + } + } + catch (Exception ex) + { + this.LogException(ex,"Exception in Kill Client"); } } - /// - /// Anything to do with reestablishing connection on failures - /// - 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)); - } - } - } - /// /// Kills the stream /// void KillStream() { - if (TheStream != null) - { - TheStream.DataReceived -= Stream_DataReceived; - TheStream.Close(); - TheStream.Dispose(); - TheStream = null; - Debug.Console(1, this, "Disconnected stream"); - } + try + { + if (TheStream != null) + { + TheStream.DataReceived -= Stream_DataReceived; + TheStream.Close(); + TheStream.Dispose(); + TheStream = null; + this.LogDebug("Disconnected stream"); + } + } + catch (Exception ex) + { + this.LogException(ex, "Exception in Kill Stream:{0}"); + } } /// @@ -395,31 +409,35 @@ namespace PepperDash.Core /// /// Handler for data receive on ShellStream. Passes data across to queue for line parsing. /// - void Stream_DataReceived(object sender, Crestron.SimplSharp.Ssh.Common.ShellDataEventArgs e) + void Stream_DataReceived(object sender, ShellDataEventArgs e) { - var bytes = e.Data; - if (bytes.Length > 0) + if (((ShellStream)sender).Length <= 0L) + { + return; + } + var response = ((ShellStream)sender).Read(); + + var bytesHandler = BytesReceived; + + if (bytesHandler != null) + { + var bytes = Encoding.UTF8.GetBytes(response); + if (StreamDebugging.RxStreamDebuggingIsEnabled) + { + this.LogInformation("Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length); + } + bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes)); + } + + var textHandler = TextReceived; + if (textHandler != null) { - var bytesHandler = BytesReceived; - if (bytesHandler != null) - { - if (StreamDebugging.RxStreamDebuggingIsEnabled) - { - Debug.Console(0, this, "Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length); - } - bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes)); - } - - var textHandler = TextReceived; - if (textHandler != null) - { - var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); - if (StreamDebugging.RxStreamDebuggingIsEnabled) - Debug.Console(0, this, "Received: '{0}'", ComTextHelper.GetDebugText(str)); + if (StreamDebugging.RxStreamDebuggingIsEnabled) + this.LogInformation("Received: '{0}'", ComTextHelper.GetDebugText(response)); - textHandler(this, new GenericCommMethodReceiveTextArgs(str)); - } - } + textHandler(this, new GenericCommMethodReceiveTextArgs(response)); + } + } @@ -427,27 +445,26 @@ namespace PepperDash.Core /// Error event handler for client events - disconnect, etc. Will forward those events via ConnectionChange /// event /// - 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); } }); @@ -470,28 +487,34 @@ namespace PepperDash.Core /// public void SendText(string text) { - try - { - if (Client != null && TheStream != null && IsConnected) - { - if (StreamDebugging.TxStreamDebuggingIsEnabled) - Debug.Console(0, this, "Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text)); + try + { + if (Client != null && TheStream != null && IsConnected) + { + if (StreamDebugging.TxStreamDebuggingIsEnabled) + this.LogInformation( + "Sending {length} characters of text: '{text}'", + text.Length, + ComTextHelper.GetDebugText(text)); - TheStream.Write(text); - TheStream.Flush(); + TheStream.Write(text); + TheStream.Flush(); + } + else + { + this.LogDebug("Client is null or disconnected. Cannot Send Text"); + } + } + catch (ObjectDisposedException ex) + { + this.LogException(ex, "ObjectDisposedException sending {message}", text); - } - else - { - Debug.Console(1, this, "Client is null or disconnected. Cannot Send Text"); - } - } + KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); + ReconnectTimer.Reset(); + } catch (Exception ex) { - Debug.Console(0, "Exception: {0}", ex.Message); - Debug.Console(0, "Stack Trace: {0}", ex.StackTrace); - - Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Stream write failed. Disconnected, closing"); + this.LogException(ex, "Exception sending text: {message}", text); } } @@ -501,36 +524,43 @@ namespace PepperDash.Core /// 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 - { - Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Stream write failed. Disconnected, closing"); - } + } + catch (ObjectDisposedException ex) + { + this.LogException(ex, "ObjectDisposedException sending {message}", ComTextHelper.GetEscapedText(bytes)); + + KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); + ReconnectTimer.Reset(); + } + catch (Exception ex) + { + this.LogException(ex, "Exception sending {message}", ComTextHelper.GetEscapedText(bytes)); + } } + #endregion - #endregion - } +} - //***************************************************************************************************** - //***************************************************************************************************** - /// - /// Fired when connection changes - /// - public class SshConnectionChangeEventArgs : EventArgs +//***************************************************************************************************** +//***************************************************************************************************** +/// +/// Fired when connection changes +/// +public class SshConnectionChangeEventArgs : EventArgs { /// /// Connection State diff --git a/src/Pepperdash Core/Comm/GenericTcpIpClient.cs b/src/Comm/GenericTcpIpClient.cs similarity index 99% rename from src/Pepperdash Core/Comm/GenericTcpIpClient.cs rename to src/Comm/GenericTcpIpClient.cs index e5f53b9..9529aa2 100644 --- a/src/Pepperdash Core/Comm/GenericTcpIpClient.cs +++ b/src/Comm/GenericTcpIpClient.cs @@ -219,7 +219,8 @@ namespace PepperDash.Core /// public GenericTcpIpClient() : base(SplusKey) - { + { + StreamDebugging = new CommunicationStreamDebugging(SplusKey); CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); AutoReconnectIntervalMs = 5000; BufferSize = 2000; diff --git a/src/Pepperdash Core/Comm/GenericTcpIpClient_ForServer.cs b/src/Comm/GenericTcpIpClient_ForServer.cs similarity index 100% rename from src/Pepperdash Core/Comm/GenericTcpIpClient_ForServer.cs rename to src/Comm/GenericTcpIpClient_ForServer.cs diff --git a/src/Pepperdash Core/Comm/GenericTcpIpServer.cs b/src/Comm/GenericTcpIpServer.cs similarity index 100% rename from src/Pepperdash Core/Comm/GenericTcpIpServer.cs rename to src/Comm/GenericTcpIpServer.cs diff --git a/src/Pepperdash Core/Comm/GenericUdpServer.cs b/src/Comm/GenericUdpServer.cs similarity index 100% rename from src/Pepperdash Core/Comm/GenericUdpServer.cs rename to src/Comm/GenericUdpServer.cs diff --git a/src/Pepperdash Core/Comm/QscCoreDoubleTcpIpClient.cs b/src/Comm/QscCoreDoubleTcpIpClient.cs similarity index 100% rename from src/Pepperdash Core/Comm/QscCoreDoubleTcpIpClient.cs rename to src/Comm/QscCoreDoubleTcpIpClient.cs diff --git a/src/Pepperdash Core/Comm/TcpClientConfigObject.cs b/src/Comm/TcpClientConfigObject.cs similarity index 100% rename from src/Pepperdash Core/Comm/TcpClientConfigObject.cs rename to src/Comm/TcpClientConfigObject.cs diff --git a/src/Pepperdash Core/Comm/TcpServerConfigObject.cs b/src/Comm/TcpServerConfigObject.cs similarity index 100% rename from src/Pepperdash Core/Comm/TcpServerConfigObject.cs rename to src/Comm/TcpServerConfigObject.cs diff --git a/src/Pepperdash Core/Comm/eControlMethods.cs b/src/Comm/eControlMethods.cs similarity index 100% rename from src/Pepperdash Core/Comm/eControlMethods.cs rename to src/Comm/eControlMethods.cs diff --git a/src/Pepperdash Core/CommunicationExtras.cs b/src/CommunicationExtras.cs similarity index 100% rename from src/Pepperdash Core/CommunicationExtras.cs rename to src/CommunicationExtras.cs diff --git a/src/Pepperdash Core/Config/PortalConfigReader.cs b/src/Config/PortalConfigReader.cs similarity index 100% rename from src/Pepperdash Core/Config/PortalConfigReader.cs rename to src/Config/PortalConfigReader.cs diff --git a/src/Pepperdash Core/Conversion/Convert.cs b/src/Conversion/Convert.cs similarity index 100% rename from src/Pepperdash Core/Conversion/Convert.cs rename to src/Conversion/Convert.cs diff --git a/src/Pepperdash Core/CoreInterfaces.cs b/src/CoreInterfaces.cs similarity index 100% rename from src/Pepperdash Core/CoreInterfaces.cs rename to src/CoreInterfaces.cs diff --git a/src/Pepperdash Core/Device.cs b/src/Device.cs similarity index 100% rename from src/Pepperdash Core/Device.cs rename to src/Device.cs diff --git a/src/Directory.build.props b/src/Directory.build.props new file mode 100644 index 0000000..196b6a0 --- /dev/null +++ b/src/Directory.build.props @@ -0,0 +1,19 @@ + + + 2.0.0-local + PepperDash Technologies + PepperDash Technologies + PepperDash Essentials + Copyright © 2025 + git + Crestron; 4series + ../output + True + LICENSE.md + README.md + + + + + + \ No newline at end of file diff --git a/src/Directory.build.targets b/src/Directory.build.targets new file mode 100644 index 0000000..0ef185c --- /dev/null +++ b/src/Directory.build.targets @@ -0,0 +1,43 @@ + + + + true + content; + + + true + content; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + doNotUse + + + + \ No newline at end of file diff --git a/src/EssentialsPlugins-builds-4-series-caller.yml b/src/EssentialsPlugins-builds-4-series-caller.yml new file mode 100644 index 0000000..c4b4d2c --- /dev/null +++ b/src/EssentialsPlugins-builds-4-series-caller.yml @@ -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 }} \ No newline at end of file diff --git a/src/Pepperdash Core/EthernetHelper.cs b/src/EthernetHelper.cs similarity index 100% rename from src/Pepperdash Core/EthernetHelper.cs rename to src/EthernetHelper.cs diff --git a/src/Pepperdash Core/EventArgs.cs b/src/EventArgs.cs similarity index 100% rename from src/Pepperdash Core/EventArgs.cs rename to src/EventArgs.cs diff --git a/src/Pepperdash Core/GenericRESTfulCommunications/Constants.cs b/src/GenericRESTfulCommunications/Constants.cs similarity index 100% rename from src/Pepperdash Core/GenericRESTfulCommunications/Constants.cs rename to src/GenericRESTfulCommunications/Constants.cs diff --git a/src/Pepperdash Core/GenericRESTfulCommunications/GenericRESTfulClient.cs b/src/GenericRESTfulCommunications/GenericRESTfulClient.cs similarity index 100% rename from src/Pepperdash Core/GenericRESTfulCommunications/GenericRESTfulClient.cs rename to src/GenericRESTfulCommunications/GenericRESTfulClient.cs diff --git a/src/Pepperdash Core/JsonStandardObjects/EventArgs and Constants.cs b/src/JsonStandardObjects/EventArgs and Constants.cs similarity index 100% rename from src/Pepperdash Core/JsonStandardObjects/EventArgs and Constants.cs rename to src/JsonStandardObjects/EventArgs and Constants.cs diff --git a/src/Pepperdash Core/JsonStandardObjects/JsonToSimplDevice.cs b/src/JsonStandardObjects/JsonToSimplDevice.cs similarity index 100% rename from src/Pepperdash Core/JsonStandardObjects/JsonToSimplDevice.cs rename to src/JsonStandardObjects/JsonToSimplDevice.cs diff --git a/src/Pepperdash Core/JsonStandardObjects/JsonToSimplDeviceConfig.cs b/src/JsonStandardObjects/JsonToSimplDeviceConfig.cs similarity index 100% rename from src/Pepperdash Core/JsonStandardObjects/JsonToSimplDeviceConfig.cs rename to src/JsonStandardObjects/JsonToSimplDeviceConfig.cs diff --git a/src/Pepperdash Core/JsonToSimpl/Constants.cs b/src/JsonToSimpl/Constants.cs similarity index 100% rename from src/Pepperdash Core/JsonToSimpl/Constants.cs rename to src/JsonToSimpl/Constants.cs diff --git a/src/Pepperdash Core/JsonToSimpl/Global.cs b/src/JsonToSimpl/Global.cs similarity index 100% rename from src/Pepperdash Core/JsonToSimpl/Global.cs rename to src/JsonToSimpl/Global.cs diff --git a/src/Pepperdash Core/JsonToSimpl/JsonToSimplArrayLookupChild.cs b/src/JsonToSimpl/JsonToSimplArrayLookupChild.cs similarity index 100% rename from src/Pepperdash Core/JsonToSimpl/JsonToSimplArrayLookupChild.cs rename to src/JsonToSimpl/JsonToSimplArrayLookupChild.cs diff --git a/src/Pepperdash Core/JsonToSimpl/JsonToSimplChildObjectBase.cs b/src/JsonToSimpl/JsonToSimplChildObjectBase.cs similarity index 100% rename from src/Pepperdash Core/JsonToSimpl/JsonToSimplChildObjectBase.cs rename to src/JsonToSimpl/JsonToSimplChildObjectBase.cs diff --git a/src/Pepperdash Core/JsonToSimpl/JsonToSimplFileMaster.cs b/src/JsonToSimpl/JsonToSimplFileMaster.cs similarity index 100% rename from src/Pepperdash Core/JsonToSimpl/JsonToSimplFileMaster.cs rename to src/JsonToSimpl/JsonToSimplFileMaster.cs diff --git a/src/Pepperdash Core/JsonToSimpl/JsonToSimplFixedPathObject.cs b/src/JsonToSimpl/JsonToSimplFixedPathObject.cs similarity index 100% rename from src/Pepperdash Core/JsonToSimpl/JsonToSimplFixedPathObject.cs rename to src/JsonToSimpl/JsonToSimplFixedPathObject.cs diff --git a/src/Pepperdash Core/JsonToSimpl/JsonToSimplGenericMaster.cs b/src/JsonToSimpl/JsonToSimplGenericMaster.cs similarity index 100% rename from src/Pepperdash Core/JsonToSimpl/JsonToSimplGenericMaster.cs rename to src/JsonToSimpl/JsonToSimplGenericMaster.cs diff --git a/src/Pepperdash Core/JsonToSimpl/JsonToSimplMaster.cs b/src/JsonToSimpl/JsonToSimplMaster.cs similarity index 100% rename from src/Pepperdash Core/JsonToSimpl/JsonToSimplMaster.cs rename to src/JsonToSimpl/JsonToSimplMaster.cs diff --git a/src/Pepperdash Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs b/src/JsonToSimpl/JsonToSimplPortalFileMaster.cs similarity index 100% rename from src/Pepperdash Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs rename to src/JsonToSimpl/JsonToSimplPortalFileMaster.cs diff --git a/src/Logging/CrestronEnricher.cs b/src/Logging/CrestronEnricher.cs new file mode 100644 index 0000000..902ce8d --- /dev/null +++ b/src/Logging/CrestronEnricher.cs @@ -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); + } + } +} diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Logging/Debug.cs similarity index 94% rename from src/Pepperdash Core/Logging/Debug.cs rename to src/Logging/Debug.cs index 1cf916a..4e79a60 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Logging/Debug.cs @@ -10,6 +10,7 @@ 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; @@ -143,12 +144,17 @@ namespace PepperDash.Core 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() - .WriteTo.Sink(new DebugConsoleSink(new JsonFormatter(renderMessage: true)), levelSwitch: _consoleLoggingLevelSwitch) + .Enrich.With(new CrestronEnricher()) + .WriteTo.Sink(new DebugConsoleSink(new ExpressionTemplate("[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}")), levelSwitch: _consoleLoggingLevelSwitch) .WriteTo.Sink(_websocketSink, levelSwitch: _websocketLoggingLevelSwitch) - .WriteTo.Sink(new DebugErrorLogSink(), levelSwitch: _errorLogLevelSwitch) + .WriteTo.Sink(new DebugErrorLogSink(new ExpressionTemplate(errorLogTemplate)), levelSwitch: _errorLogLevelSwitch) .WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath, rollingInterval: RollingInterval.Day, restrictedToMinimumLevel: LogEventLevel.Debug, @@ -187,7 +193,7 @@ namespace PepperDash.Core CrestronConsole.PrintLine(msg); - LogError(ErrorLogLevel.Notice, msg); + LogMessage(LogEventLevel.Information,msg); IncludedExcludedKeys = new Dictionary(); @@ -591,7 +597,7 @@ namespace PepperDash.Core /// Args to put into message template public static void LogMessage(Exception ex, string message, IKeyed device = null, params object[] args) { - using (LogContext.PushProperty("Key", device?.Key ?? string.Empty)) + using (LogContext.PushProperty("Key", device?.Key)) { _logger.Error(ex, message, args); } @@ -606,7 +612,7 @@ namespace PepperDash.Core /// Args to put into message template public static void LogMessage(LogEventLevel level, string message, IKeyed device=null, params object[] args) { - using (LogContext.PushProperty("Key", device?.Key ?? string.Empty)) + using (LogContext.PushProperty("Key", device?.Key)) { _logger.Write(level, message, args); } @@ -655,14 +661,6 @@ namespace PepperDash.Core LogMessage(level, format, items); - if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server) - { - var logString = string.Format("[level {0}] {1}", level, string.Format(format, items)); - - LogError(ErrorLogLevel.Notice, logString); - return; - } - //if (IsRunningOnAppliance) //{ // CrestronConsole.PrintLine("[{0}]App {1} Lvl {2}:{3}", DateTime.Now.ToString("HH:mm:ss.fff"), @@ -691,25 +689,8 @@ namespace PepperDash.Core [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); - } - + { LogMessage(level, dev, format, items); - - //var log = _logger.ForContext("Key", dev.Key); - //var message = string.Format(format, items); - - //log.Write((LogEventLevel)level, message); - - //if (Level >= level) - //{ - // Console(level, str); - //} } /// @@ -719,17 +700,7 @@ namespace PepperDash.Core 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); - } - LogMessage(level, format, items); - //if (Level >= level) - //{ - // Console(level, str); - //} } /// @@ -770,18 +741,16 @@ namespace PepperDash.Core [Obsolete("Use LogMessage methods")] public static void LogError(ErrorLogLevel errorLogLevel, string str) { - - var msg = IsRunningOnAppliance ? 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; } } diff --git a/src/Pepperdash Core/Logging/DebugConsoleSink.cs b/src/Logging/DebugConsoleSink.cs similarity index 78% rename from src/Pepperdash Core/Logging/DebugConsoleSink.cs rename to src/Logging/DebugConsoleSink.cs index 990d47b..a6c7f89 100644 --- a/src/Pepperdash Core/Logging/DebugConsoleSink.cs +++ b/src/Logging/DebugConsoleSink.cs @@ -5,6 +5,8 @@ using Serilog.Core; using Serilog.Events; using Serilog.Formatting; using Serilog.Formatting.Json; +using System.IO; +using System.Text; namespace PepperDash.Core @@ -17,12 +19,18 @@ namespace PepperDash.Core { if (!Debug.IsRunningOnAppliance) return; - string message = $"[{logEvent.Timestamp}][{logEvent.Level}][App {InitialParametersClass.ApplicationNumber}]{logEvent.RenderMessage()}"; + /*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); } diff --git a/src/Pepperdash Core/Logging/DebugContext.cs b/src/Logging/DebugContext.cs similarity index 100% rename from src/Pepperdash Core/Logging/DebugContext.cs rename to src/Logging/DebugContext.cs diff --git a/src/Pepperdash Core/Logging/DebugCrestronLoggerSink.cs b/src/Logging/DebugCrestronLoggerSink.cs similarity index 100% rename from src/Pepperdash Core/Logging/DebugCrestronLoggerSink.cs rename to src/Logging/DebugCrestronLoggerSink.cs diff --git a/src/Pepperdash Core/Logging/DebugErrorLogSink.cs b/src/Logging/DebugErrorLogSink.cs similarity index 52% rename from src/Pepperdash Core/Logging/DebugErrorLogSink.cs rename to src/Logging/DebugErrorLogSink.cs index b5bbb09..3885982 100644 --- a/src/Pepperdash Core/Logging/DebugErrorLogSink.cs +++ b/src/Logging/DebugErrorLogSink.cs @@ -1,8 +1,10 @@ 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; @@ -11,6 +13,8 @@ namespace PepperDash.Core.Logging { public class DebugErrorLogSink : ILogEventSink { + private ITextFormatter _formatter; + private Dictionary> _errorLogMap = new Dictionary> { { LogEventLevel.Verbose, (msg) => ErrorLog.Notice(msg) }, @@ -22,15 +26,27 @@ namespace PepperDash.Core.Logging }; public void Emit(LogEvent logEvent) { - var programId = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance - ? $"App {InitialParametersClass.ApplicationNumber}" - : $"Room {InitialParametersClass.RoomId}"; + string message; - string message = $"[{logEvent.Timestamp}][{logEvent.Level}][{programId}]{logEvent.RenderMessage()}"; - - if (logEvent.Properties.TryGetValue("Key", out var value) && value is ScalarValue sv && sv.Value is string rawValue) + if (_formatter == null) { - message = $"[{logEvent.Timestamp}][{logEvent.Level}][{programId}][{rawValue}]: {logEvent.RenderMessage()}"; + var programId = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance + ? $"App {InitialParametersClass.ApplicationNumber}" + : $"Room {InitialParametersClass.RoomId}"; + + message = $"[{logEvent.Timestamp}][{logEvent.Level}][{programId}]{logEvent.RenderMessage()}"; + + if (logEvent.Properties.TryGetValue("Key", out var value) && value is ScalarValue sv && sv.Value is string rawValue) + { + message = $"[{logEvent.Timestamp}][{logEvent.Level}][{programId}][{rawValue}]: {logEvent.RenderMessage()}"; + } + } else + { + var buffer = new StringWriter(new StringBuilder(256)); + + _formatter.Format(logEvent, buffer); + + message = buffer.ToString(); } if(!_errorLogMap.TryGetValue(logEvent.Level, out var handler)) @@ -40,5 +56,10 @@ namespace PepperDash.Core.Logging handler(message); } + + public DebugErrorLogSink(ITextFormatter formatter = null) + { + _formatter = formatter; + } } } diff --git a/src/Logging/DebugExtensions.cs b/src/Logging/DebugExtensions.cs new file mode 100644 index 0000000..68e9868 --- /dev/null +++ b/src/Logging/DebugExtensions.cs @@ -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); + } + } +} diff --git a/src/Pepperdash Core/Logging/DebugMemory.cs b/src/Logging/DebugMemory.cs similarity index 100% rename from src/Pepperdash Core/Logging/DebugMemory.cs rename to src/Logging/DebugMemory.cs diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Logging/DebugWebsocketSink.cs similarity index 100% rename from src/Pepperdash Core/Logging/DebugWebsocketSink.cs rename to src/Logging/DebugWebsocketSink.cs diff --git a/src/Pepperdash Core/Network/DiscoveryThings.cs b/src/Network/DiscoveryThings.cs similarity index 100% rename from src/Pepperdash Core/Network/DiscoveryThings.cs rename to src/Network/DiscoveryThings.cs diff --git a/src/Pepperdash Core/PasswordManagement/Config.cs b/src/PasswordManagement/Config.cs similarity index 100% rename from src/Pepperdash Core/PasswordManagement/Config.cs rename to src/PasswordManagement/Config.cs diff --git a/src/Pepperdash Core/PasswordManagement/Constants.cs b/src/PasswordManagement/Constants.cs similarity index 100% rename from src/Pepperdash Core/PasswordManagement/Constants.cs rename to src/PasswordManagement/Constants.cs diff --git a/src/Pepperdash Core/PasswordManagement/OLD-ARRAY-Config.cs b/src/PasswordManagement/OLD-ARRAY-Config.cs similarity index 100% rename from src/Pepperdash Core/PasswordManagement/OLD-ARRAY-Config.cs rename to src/PasswordManagement/OLD-ARRAY-Config.cs diff --git a/src/Pepperdash Core/PasswordManagement/OLD-ARRAY-PasswordClient.cs b/src/PasswordManagement/OLD-ARRAY-PasswordClient.cs similarity index 100% rename from src/Pepperdash Core/PasswordManagement/OLD-ARRAY-PasswordClient.cs rename to src/PasswordManagement/OLD-ARRAY-PasswordClient.cs diff --git a/src/Pepperdash Core/PasswordManagement/OLD-ARRAY-PasswordManager.cs b/src/PasswordManagement/OLD-ARRAY-PasswordManager.cs similarity index 100% rename from src/Pepperdash Core/PasswordManagement/OLD-ARRAY-PasswordManager.cs rename to src/PasswordManagement/OLD-ARRAY-PasswordManager.cs diff --git a/src/Pepperdash Core/PasswordManagement/PasswordClient.cs b/src/PasswordManagement/PasswordClient.cs similarity index 100% rename from src/Pepperdash Core/PasswordManagement/PasswordClient.cs rename to src/PasswordManagement/PasswordClient.cs diff --git a/src/Pepperdash Core/PasswordManagement/PasswordManager.cs b/src/PasswordManagement/PasswordManager.cs similarity index 100% rename from src/Pepperdash Core/PasswordManagement/PasswordManager.cs rename to src/PasswordManagement/PasswordManager.cs diff --git a/src/Pepperdash Core/PepperDash_Core.csproj b/src/PepperDash.Core.4Series.csproj similarity index 85% rename from src/Pepperdash Core/PepperDash_Core.csproj rename to src/PepperDash.Core.4Series.csproj index 1d756f9..1433862 100644 --- a/src/Pepperdash Core/PepperDash_Core.csproj +++ b/src/PepperDash.Core.4Series.csproj @@ -2,7 +2,7 @@ PepperDash.Core PepperDashCore - net472;net6 + net472 true en bin\$(Configuration)\ @@ -12,10 +12,8 @@ PepperDash Technologies git https://github.com/PepperDash/PepperDashCore - crestron;4series; - 2.0.0-local - $(Version) - ../../package + crestron;4series; + $(Version) true @@ -40,12 +38,14 @@ - - + + + - + + diff --git a/src/Pepperdash Core/PepperDash_Core.projectinfo b/src/Pepperdash Core/PepperDash_Core.projectinfo deleted file mode 100644 index ded18d3..0000000 Binary files a/src/Pepperdash Core/PepperDash_Core.projectinfo and /dev/null differ diff --git a/src/Pepperdash Core/Web/RequestHandlers/DefaultRequestHandler.cs b/src/Pepperdash Core/Web/RequestHandlers/DefaultRequestHandler.cs deleted file mode 100644 index 57fa1de..0000000 --- a/src/Pepperdash Core/Web/RequestHandlers/DefaultRequestHandler.cs +++ /dev/null @@ -1,16 +0,0 @@ - -namespace PepperDash.Core.Web.RequestHandlers -{ - /// - /// Web API default request handler - /// - public class DefaultRequestHandler : WebApiBaseRequestHandler - { - /// - /// Constructor - /// - public DefaultRequestHandler() - : base(true) - { } - } -} diff --git a/src/Pepperdash Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs b/src/Pepperdash Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs deleted file mode 100644 index caa6a7c..0000000 --- a/src/Pepperdash Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs +++ /dev/null @@ -1,165 +0,0 @@ -using System; -using System.Collections.Generic; -using Crestron.SimplSharp.WebScripting; - -namespace PepperDash.Core.Web.RequestHandlers -{ - /// - /// CWS Base Handler, implements IHttpCwsHandler - /// - public abstract class WebApiBaseRequestHandler : IHttpCwsHandler - { - private readonly Dictionary> _handlers; - protected readonly bool EnableCors; - - /// - /// Constructor - /// - protected WebApiBaseRequestHandler(bool enableCors) - { - EnableCors = enableCors; - - _handlers = new Dictionary> - { - {"CONNECT", HandleConnect}, - {"DELETE", HandleDelete}, - {"GET", HandleGet}, - {"HEAD", HandleHead}, - {"OPTIONS", HandleOptions}, - {"PATCH", HandlePatch}, - {"POST", HandlePost}, - {"PUT", HandlePut}, - {"TRACE", HandleTrace} - }; - } - - /// - /// Constructor - /// - protected WebApiBaseRequestHandler() - : this(false) - { - } - - /// - /// Handles CONNECT method requests - /// - /// - protected virtual void HandleConnect(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); - } - - /// - /// Handles DELETE method requests - /// - /// - protected virtual void HandleDelete(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); - } - - /// - /// Handles GET method requests - /// - /// - protected virtual void HandleGet(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); - } - - /// - /// Handles HEAD method requests - /// - /// - protected virtual void HandleHead(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); - } - - /// - /// Handles OPTIONS method requests - /// - /// - protected virtual void HandleOptions(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); - } - - /// - /// Handles PATCH method requests - /// - /// - protected virtual void HandlePatch(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); - } - - /// - /// Handles POST method requests - /// - /// - protected virtual void HandlePost(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); - } - - /// - /// Handles PUT method requests - /// - /// - protected virtual void HandlePut(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); - } - - /// - /// Handles TRACE method requests - /// - /// - protected virtual void HandleTrace(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); - } - - /// - /// Process request - /// - /// - public void ProcessRequest(HttpCwsContext context) - { - Action handler; - - if (!_handlers.TryGetValue(context.Request.HttpMethod, out handler)) - { - return; - } - - if (EnableCors) - { - context.Response.Headers.Add("Access-Control-Allow-Origin", "*"); - context.Response.Headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); - } - - handler(context); - } - } -} \ No newline at end of file diff --git a/src/Pepperdash Core/Web/WebApiServer.cs b/src/Pepperdash Core/Web/WebApiServer.cs deleted file mode 100644 index 17c737a..0000000 --- a/src/Pepperdash Core/Web/WebApiServer.cs +++ /dev/null @@ -1,284 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Crestron.SimplSharp; -using Crestron.SimplSharp.WebScripting; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using PepperDash.Core.Web.RequestHandlers; - -namespace PepperDash.Core.Web -{ - /// - /// Web API server - /// - public class WebApiServer : IKeyName - { - private const string SplusKey = "Uninitialized Web API Server"; - private const string DefaultName = "Web API Server"; - private const string DefaultBasePath = "/api"; - - private const uint DebugTrace = 0; - private const uint DebugInfo = 1; - private const uint DebugVerbose = 2; - - private readonly CCriticalSection _serverLock = new CCriticalSection(); - private HttpCwsServer _server; - - /// - /// Web API server key - /// - public string Key { get; private set; } - - /// - /// Web API server name - /// - public string Name { get; private set; } - - /// - /// CWS base path, will default to "/api" if not set via initialize method - /// - public string BasePath { get; private set; } - - /// - /// Indicates CWS is registered with base path - /// - public bool IsRegistered { get; private set; } - - /// - /// Http request handler - /// - //public IHttpCwsHandler HttpRequestHandler - //{ - // get { return _server.HttpRequestHandler; } - // set - // { - // if (_server == null) return; - // _server.HttpRequestHandler = value; - // } - //} - - /// - /// Received request event handler - /// - //public event EventHandler ReceivedRequestEvent - //{ - // add { _server.ReceivedRequestEvent += new HttpCwsRequestEventHandler(value); } - // remove { _server.ReceivedRequestEvent -= new HttpCwsRequestEventHandler(value); } - //} - - /// - /// Constructor for S+. Make sure to set necessary properties using init method - /// - public WebApiServer() - : this(SplusKey, DefaultName, null) - { - } - - /// - /// Constructor - /// - /// - /// - public WebApiServer(string key, string basePath) - : this(key, DefaultName, basePath) - { - } - - /// - /// Constructor - /// - /// - /// - /// - public WebApiServer(string key, string name, string basePath) - { - Key = key; - Name = string.IsNullOrEmpty(name) ? DefaultName : name; - BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath; - - if (_server == null) _server = new HttpCwsServer(BasePath); - - _server.setProcessName(Key); - _server.HttpRequestHandler = new DefaultRequestHandler(); - - CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler; - CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; - } - - /// - /// Program status event handler - /// - /// - void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) - { - if (programEventType != eProgramStatusEventType.Stopping) return; - - Debug.Console(DebugInfo, this, "Program stopping. stopping server"); - - Stop(); - } - - /// - /// Ethernet event handler - /// - /// - void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs) - { - // Re-enable the server if the link comes back up and the status should be connected - if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp && IsRegistered) - { - Debug.Console(DebugInfo, this, "Ethernet link up. Server is alreedy registered."); - return; - } - - Debug.Console(DebugInfo, this, "Ethernet link up. Starting server"); - - Start(); - } - - /// - /// Initializes CWS class - /// - public void Initialize(string key, string basePath) - { - Key = key; - BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath; - } - - /// - /// Adds a route to CWS - /// - public void AddRoute(HttpCwsRoute route) - { - if (route == null) - { - Debug.Console(DebugInfo, this, "Failed to add route, route parameter is null"); - return; - } - - _server.Routes.Add(route); - - } - - /// - /// Removes a route from CWS - /// - /// - public void RemoveRoute(HttpCwsRoute route) - { - if (route == null) - { - Debug.Console(DebugInfo, this, "Failed to remote route, orute parameter is null"); - return; - } - - _server.Routes.Remove(route); - } - - /// - /// Returns a list of the current routes - /// - public HttpCwsRouteCollection GetRouteCollection() - { - return _server.Routes; - } - - /// - /// Starts CWS instance - /// - public void Start() - { - try - { - _serverLock.Enter(); - - if (_server == null) - { - Debug.Console(DebugInfo, this, "Server is null, unable to start"); - return; - } - - if (IsRegistered) - { - Debug.Console(DebugInfo, this, "Server has already been started"); - return; - } - - IsRegistered = _server.Register(); - - Debug.Console(DebugInfo, this, "Starting server, registration {0}", IsRegistered ? "was successful" : "failed"); - } - catch (Exception ex) - { - Debug.Console(DebugInfo, this, "Start Exception Message: {0}", ex.Message); - Debug.Console(DebugVerbose, this, "Start Exception StackTrace: {0}", ex.StackTrace); - if (ex.InnerException != null) - Debug.Console(DebugVerbose, this, "Start Exception InnerException: {0}", ex.InnerException); - } - finally - { - _serverLock.Leave(); - } - } - - /// - /// Stop CWS instance - /// - public void Stop() - { - try - { - _serverLock.Enter(); - - if (_server == null) - { - Debug.Console(DebugInfo, this, "Server is null or has already been stopped"); - return; - } - - IsRegistered = _server.Unregister() == false; - - Debug.Console(DebugInfo, this, "Stopping server, unregistration {0}", IsRegistered ? "failed" : "was successful"); - - _server.Dispose(); - _server = null; - } - catch (Exception ex) - { - Debug.Console(DebugInfo, this, "Server Stop Exception Message: {0}", ex.Message); - Debug.Console(DebugVerbose, this, "Server Stop Exception StackTrace: {0}", ex.StackTrace); - if (ex.InnerException != null) - Debug.Console(DebugVerbose, this, "Server Stop Exception InnerException: {0}", ex.InnerException); - } - finally - { - _serverLock.Leave(); - } - } - - /// - /// Received request handler - /// - /// - /// This is here for development and testing - /// - /// - /// - public void ReceivedRequestEventHandler(object sender, HttpCwsRequestEventArgs args) - { - try - { - var j = JsonConvert.SerializeObject(args.Context, Formatting.Indented); - Debug.Console(DebugVerbose, this, "RecieveRequestEventHandler Context:\x0d\x0a{0}", j); - } - catch (Exception ex) - { - Debug.Console(DebugInfo, this, "ReceivedRequestEventHandler Exception Message: {0}", ex.Message); - Debug.Console(DebugVerbose, this, "ReceivedRequestEventHandler Exception StackTrace: {0}", ex.StackTrace); - if (ex.InnerException != null) - Debug.Console(DebugVerbose, this, "ReceivedRequestEventHandler Exception InnerException: {0}", ex.InnerException); - } - } - } -} \ No newline at end of file diff --git a/src/Pepperdash Core/lib/net35/PepperDash_Core.clz b/src/Pepperdash Core/lib/net35/PepperDash_Core.clz deleted file mode 100644 index 93e2b02..0000000 Binary files a/src/Pepperdash Core/lib/net35/PepperDash_Core.clz and /dev/null differ diff --git a/src/Pepperdash Core/lib/net35/PepperDash_Core.dll b/src/Pepperdash Core/lib/net35/PepperDash_Core.dll deleted file mode 100644 index 1b3d097..0000000 Binary files a/src/Pepperdash Core/lib/net35/PepperDash_Core.dll and /dev/null differ diff --git a/src/Pepperdash Core/Properties/ControlSystem.cfg b/src/Properties/ControlSystem.cfg similarity index 100% rename from src/Pepperdash Core/Properties/ControlSystem.cfg rename to src/Properties/ControlSystem.cfg diff --git a/src/Pepperdash Core/SystemInfo/EventArgs and Constants.cs b/src/SystemInfo/EventArgs and Constants.cs similarity index 100% rename from src/Pepperdash Core/SystemInfo/EventArgs and Constants.cs rename to src/SystemInfo/EventArgs and Constants.cs diff --git a/src/Pepperdash Core/SystemInfo/SystemInfoConfig.cs b/src/SystemInfo/SystemInfoConfig.cs similarity index 100% rename from src/Pepperdash Core/SystemInfo/SystemInfoConfig.cs rename to src/SystemInfo/SystemInfoConfig.cs diff --git a/src/Pepperdash Core/SystemInfo/SystemInfoToSimpl.cs b/src/SystemInfo/SystemInfoToSimpl.cs similarity index 100% rename from src/Pepperdash Core/SystemInfo/SystemInfoToSimpl.cs rename to src/SystemInfo/SystemInfoToSimpl.cs diff --git a/src/Pepperdash Core/Web/BouncyCertificate.cs b/src/Web/BouncyCertificate.cs similarity index 99% rename from src/Pepperdash Core/Web/BouncyCertificate.cs rename to src/Web/BouncyCertificate.cs index 67c129f..bf8b0f4 100644 --- a/src/Pepperdash Core/Web/BouncyCertificate.cs +++ b/src/Web/BouncyCertificate.cs @@ -9,7 +9,6 @@ using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Prng; -using Org.BouncyCastle.Math; using Org.BouncyCastle.Pkcs; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; @@ -17,10 +16,7 @@ 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 System.Text; using Org.BouncyCastle.Crypto.Operators; -using System.Numerics; -using System.Security.Cryptography.X509Certificates; using BigInteger = Org.BouncyCastle.Math.BigInteger; using X509Certificate = Org.BouncyCastle.X509.X509Certificate; @@ -284,7 +280,7 @@ namespace PepperDash.Core // 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 Pkcs12Store(); + 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(); diff --git a/src/Web/RequestHandlers/DefaultRequestHandler.cs b/src/Web/RequestHandlers/DefaultRequestHandler.cs new file mode 100644 index 0000000..ca19cf2 --- /dev/null +++ b/src/Web/RequestHandlers/DefaultRequestHandler.cs @@ -0,0 +1,17 @@ +using Crestron.SimplSharp.WebScripting; + +namespace PepperDash.Core.Web.RequestHandlers +{ + /// + /// Web API default request handler + /// + public class DefaultRequestHandler : WebApiBaseRequestHandler + { + /// + /// Constructor + /// + public DefaultRequestHandler() + : base(true) + { } + } +} \ No newline at end of file diff --git a/src/Pepperdash Core/Web/RequestHandlers/WebApiBaseRequestAsyncHandler.cs b/src/Web/RequestHandlers/WebApiBaseRequestAsyncHandler.cs similarity index 100% rename from src/Pepperdash Core/Web/RequestHandlers/WebApiBaseRequestAsyncHandler.cs rename to src/Web/RequestHandlers/WebApiBaseRequestAsyncHandler.cs diff --git a/src/Web/RequestHandlers/WebApiBaseRequestHandler.cs b/src/Web/RequestHandlers/WebApiBaseRequestHandler.cs new file mode 100644 index 0000000..99e4aa9 --- /dev/null +++ b/src/Web/RequestHandlers/WebApiBaseRequestHandler.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using Crestron.SimplSharp.WebScripting; + +namespace PepperDash.Core.Web.RequestHandlers +{ + /// + /// CWS Base Handler, implements IHttpCwsHandler + /// + public abstract class WebApiBaseRequestHandler : IHttpCwsHandler + { + private readonly Dictionary> _handlers; + protected readonly bool EnableCors; + + /// + /// Constructor + /// + protected WebApiBaseRequestHandler(bool enableCors) + { + EnableCors = enableCors; + + _handlers = new Dictionary> + { + {"CONNECT", HandleConnect}, + {"DELETE", HandleDelete}, + {"GET", HandleGet}, + {"HEAD", HandleHead}, + {"OPTIONS", HandleOptions}, + {"PATCH", HandlePatch}, + {"POST", HandlePost}, + {"PUT", HandlePut}, + {"TRACE", HandleTrace} + }; + } + + /// + /// Constructor + /// + protected WebApiBaseRequestHandler() + : this(false) + { + } + + /// + /// Handles CONNECT method requests + /// + /// + protected virtual void HandleConnect(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles DELETE method requests + /// + /// + protected virtual void HandleDelete(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles GET method requests + /// + /// + protected virtual void HandleGet(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles HEAD method requests + /// + /// + protected virtual void HandleHead(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles OPTIONS method requests + /// + /// + protected virtual void HandleOptions(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles PATCH method requests + /// + /// + protected virtual void HandlePatch(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles POST method requests + /// + /// + protected virtual void HandlePost(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles PUT method requests + /// + /// + protected virtual void HandlePut(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles TRACE method requests + /// + /// + protected virtual void HandleTrace(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Process request + /// + /// + public void ProcessRequest(HttpCwsContext context) + { + Action handler; + + if (!_handlers.TryGetValue(context.Request.HttpMethod, out handler)) + { + return; + } + + if (EnableCors) + { + context.Response.Headers.Add("Access-Control-Allow-Origin", "*"); + context.Response.Headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); + } + + handler(context); + } + } +} \ No newline at end of file diff --git a/src/Web/WebApiServer.cs b/src/Web/WebApiServer.cs new file mode 100644 index 0000000..cf45b36 --- /dev/null +++ b/src/Web/WebApiServer.cs @@ -0,0 +1,284 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharp; +using Crestron.SimplSharp.WebScripting; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core.Web.RequestHandlers; + +namespace PepperDash.Core.Web +{ + /// + /// Web API server + /// + public class WebApiServer : IKeyName + { + private const string SplusKey = "Uninitialized Web API Server"; + private const string DefaultName = "Web API Server"; + private const string DefaultBasePath = "/api"; + + private const uint DebugTrace = 0; + private const uint DebugInfo = 1; + private const uint DebugVerbose = 2; + + private readonly CCriticalSection _serverLock = new CCriticalSection(); + private HttpCwsServer _server; + + /// + /// Web API server key + /// + public string Key { get; private set; } + + /// + /// Web API server name + /// + public string Name { get; private set; } + + /// + /// CWS base path, will default to "/api" if not set via initialize method + /// + public string BasePath { get; private set; } + + /// + /// Indicates CWS is registered with base path + /// + public bool IsRegistered { get; private set; } + + /// + /// Http request handler + /// + //public IHttpCwsHandler HttpRequestHandler + //{ + // get { return _server.HttpRequestHandler; } + // set + // { + // if (_server == null) return; + // _server.HttpRequestHandler = value; + // } + //} + + /// + /// Received request event handler + /// + //public event EventHandler ReceivedRequestEvent + //{ + // add { _server.ReceivedRequestEvent += new HttpCwsRequestEventHandler(value); } + // remove { _server.ReceivedRequestEvent -= new HttpCwsRequestEventHandler(value); } + //} + + /// + /// Constructor for S+. Make sure to set necessary properties using init method + /// + public WebApiServer() + : this(SplusKey, DefaultName, null) + { + } + + /// + /// Constructor + /// + /// + /// + public WebApiServer(string key, string basePath) + : this(key, DefaultName, basePath) + { + } + + /// + /// Constructor + /// + /// + /// + /// + public WebApiServer(string key, string name, string basePath) + { + Key = key; + Name = string.IsNullOrEmpty(name) ? DefaultName : name; + BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath; + + if (_server == null) _server = new HttpCwsServer(BasePath); + + _server.setProcessName(Key); + _server.HttpRequestHandler = new DefaultRequestHandler(); + + CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler; + CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; + } + + /// + /// Program status event handler + /// + /// + void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) + { + if (programEventType != eProgramStatusEventType.Stopping) return; + + Debug.Console(DebugInfo, this, "Program stopping. stopping server"); + + Stop(); + } + + /// + /// Ethernet event handler + /// + /// + void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs) + { + // Re-enable the server if the link comes back up and the status should be connected + if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp && IsRegistered) + { + Debug.Console(DebugInfo, this, "Ethernet link up. Server is alreedy registered."); + return; + } + + Debug.Console(DebugInfo, this, "Ethernet link up. Starting server"); + + Start(); + } + + /// + /// Initializes CWS class + /// + public void Initialize(string key, string basePath) + { + Key = key; + BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath; + } + + /// + /// Adds a route to CWS + /// + public void AddRoute(HttpCwsRoute route) + { + if (route == null) + { + Debug.Console(DebugInfo, this, "Failed to add route, route parameter is null"); + return; + } + + _server.Routes.Add(route); + + } + + /// + /// Removes a route from CWS + /// + /// + public void RemoveRoute(HttpCwsRoute route) + { + if (route == null) + { + Debug.Console(DebugInfo, this, "Failed to remote route, orute parameter is null"); + return; + } + + _server.Routes.Remove(route); + } + + /// + /// Returns a list of the current routes + /// + public HttpCwsRouteCollection GetRouteCollection() + { + return _server.Routes; + } + + /// + /// Starts CWS instance + /// + public void Start() + { + try + { + _serverLock.Enter(); + + if (_server == null) + { + Debug.Console(DebugInfo, this, "Server is null, unable to start"); + return; + } + + if (IsRegistered) + { + Debug.Console(DebugInfo, this, "Server has already been started"); + return; + } + + IsRegistered = _server.Register(); + + Debug.Console(DebugInfo, this, "Starting server, registration {0}", IsRegistered ? "was successful" : "failed"); + } + catch (Exception ex) + { + Debug.Console(DebugInfo, this, "Start Exception Message: {0}", ex.Message); + Debug.Console(DebugVerbose, this, "Start Exception StackTrace: {0}", ex.StackTrace); + if (ex.InnerException != null) + Debug.Console(DebugVerbose, this, "Start Exception InnerException: {0}", ex.InnerException); + } + finally + { + _serverLock.Leave(); + } + } + + /// + /// Stop CWS instance + /// + public void Stop() + { + try + { + _serverLock.Enter(); + + if (_server == null) + { + Debug.Console(DebugInfo, this, "Server is null or has already been stopped"); + return; + } + + IsRegistered = _server.Unregister() == false; + + Debug.Console(DebugInfo, this, "Stopping server, unregistration {0}", IsRegistered ? "failed" : "was successful"); + + _server.Dispose(); + _server = null; + } + catch (Exception ex) + { + Debug.Console(DebugInfo, this, "Server Stop Exception Message: {0}", ex.Message); + Debug.Console(DebugVerbose, this, "Server Stop Exception StackTrace: {0}", ex.StackTrace); + if (ex.InnerException != null) + Debug.Console(DebugVerbose, this, "Server Stop Exception InnerException: {0}", ex.InnerException); + } + finally + { + _serverLock.Leave(); + } + } + + /// + /// Received request handler + /// + /// + /// This is here for development and testing + /// + /// + /// + public void ReceivedRequestEventHandler(object sender, HttpCwsRequestEventArgs args) + { + try + { + var j = JsonConvert.SerializeObject(args.Context, Formatting.Indented); + Debug.Console(DebugVerbose, this, "RecieveRequestEventHandler Context:\x0d\x0a{0}", j); + } + catch (Exception ex) + { + Debug.Console(DebugInfo, this, "ReceivedRequestEventHandler Exception Message: {0}", ex.Message); + Debug.Console(DebugVerbose, this, "ReceivedRequestEventHandler Exception StackTrace: {0}", ex.StackTrace); + if (ex.InnerException != null) + Debug.Console(DebugVerbose, this, "ReceivedRequestEventHandler Exception InnerException: {0}", ex.InnerException); + } + } + } +} \ No newline at end of file diff --git a/src/Pepperdash Core/WebApi/Presets/Preset.cs b/src/WebApi/Presets/Preset.cs similarity index 100% rename from src/Pepperdash Core/WebApi/Presets/Preset.cs rename to src/WebApi/Presets/Preset.cs diff --git a/src/Pepperdash Core/WebApi/Presets/User.cs b/src/WebApi/Presets/User.cs similarity index 100% rename from src/Pepperdash Core/WebApi/Presets/User.cs rename to src/WebApi/Presets/User.cs diff --git a/src/Pepperdash Core/WebApi/Presets/WebApiPasscodeClient.cs b/src/WebApi/Presets/WebApiPasscodeClient.cs similarity index 100% rename from src/Pepperdash Core/WebApi/Presets/WebApiPasscodeClient.cs rename to src/WebApi/Presets/WebApiPasscodeClient.cs diff --git a/src/Pepperdash Core/XSigUtility/Serialization/IXSigSerialization.cs b/src/XSigUtility/Serialization/IXSigSerialization.cs similarity index 100% rename from src/Pepperdash Core/XSigUtility/Serialization/IXSigSerialization.cs rename to src/XSigUtility/Serialization/IXSigSerialization.cs diff --git a/src/Pepperdash Core/XSigUtility/Serialization/XSigSerializationException.cs b/src/XSigUtility/Serialization/XSigSerializationException.cs similarity index 100% rename from src/Pepperdash Core/XSigUtility/Serialization/XSigSerializationException.cs rename to src/XSigUtility/Serialization/XSigSerializationException.cs diff --git a/src/Pepperdash Core/XSigUtility/Tokens/XSigAnalogToken.cs b/src/XSigUtility/Tokens/XSigAnalogToken.cs similarity index 100% rename from src/Pepperdash Core/XSigUtility/Tokens/XSigAnalogToken.cs rename to src/XSigUtility/Tokens/XSigAnalogToken.cs diff --git a/src/Pepperdash Core/XSigUtility/Tokens/XSigDigitalToken.cs b/src/XSigUtility/Tokens/XSigDigitalToken.cs similarity index 100% rename from src/Pepperdash Core/XSigUtility/Tokens/XSigDigitalToken.cs rename to src/XSigUtility/Tokens/XSigDigitalToken.cs diff --git a/src/Pepperdash Core/XSigUtility/Tokens/XSigSerialToken.cs b/src/XSigUtility/Tokens/XSigSerialToken.cs similarity index 100% rename from src/Pepperdash Core/XSigUtility/Tokens/XSigSerialToken.cs rename to src/XSigUtility/Tokens/XSigSerialToken.cs diff --git a/src/Pepperdash Core/XSigUtility/Tokens/XSigToken.cs b/src/XSigUtility/Tokens/XSigToken.cs similarity index 100% rename from src/Pepperdash Core/XSigUtility/Tokens/XSigToken.cs rename to src/XSigUtility/Tokens/XSigToken.cs diff --git a/src/Pepperdash Core/XSigUtility/Tokens/XSigTokenType.cs b/src/XSigUtility/Tokens/XSigTokenType.cs similarity index 100% rename from src/Pepperdash Core/XSigUtility/Tokens/XSigTokenType.cs rename to src/XSigUtility/Tokens/XSigTokenType.cs diff --git a/src/Pepperdash Core/XSigUtility/XSigHelpers.cs b/src/XSigUtility/XSigHelpers.cs similarity index 100% rename from src/Pepperdash Core/XSigUtility/XSigHelpers.cs rename to src/XSigUtility/XSigHelpers.cs diff --git a/src/Pepperdash Core/XSigUtility/XSigTokenStreamReader.cs b/src/XSigUtility/XSigTokenStreamReader.cs similarity index 100% rename from src/Pepperdash Core/XSigUtility/XSigTokenStreamReader.cs rename to src/XSigUtility/XSigTokenStreamReader.cs diff --git a/src/Pepperdash Core/XSigUtility/XSigTokenStreamWriter.cs b/src/XSigUtility/XSigTokenStreamWriter.cs similarity index 100% rename from src/Pepperdash Core/XSigUtility/XSigTokenStreamWriter.cs rename to src/XSigUtility/XSigTokenStreamWriter.cs