diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 17fcebf..9e09430 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -44,7 +44,7 @@ jobs: shell: powershell run: | $version = ./.github/scripts/GenerateVersionNumber.ps1 - Write-Output "::set-env name=VERSION::$version" + 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 @@ -123,7 +123,7 @@ jobs: Get-ChildItem "./Version" $version = Get-Content -Path ./Version/version.txt Write-Host "Version: $version" - Write-Output "::set-env name=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 @@ -178,7 +178,7 @@ jobs: Get-ChildItem "./Version" $version = Get-Content -Path ./Version/version.txt Write-Host "Version: $version" - Write-Output "::set-env name=VERSION::$version" + echo "VERSION=$version" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append Remove-Item -Path ./Version/version.txt Remove-Item -Path ./Version # Checkout/Create the branch @@ -255,7 +255,7 @@ jobs: Get-ChildItem "./Version" $version = Get-Content -Path ./Version/version.txt Write-Host "Version: $version" - Write-Output "::set-env name=VERSION::$version" + echo "VERSION=$version" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append Remove-Item -Path ./Version/version.txt Remove-Item -Path ./Version # Checkout/Create the branch diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b41e08c..db83b18 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,7 +37,7 @@ jobs: shell: powershell env: TAG_NAME: ${{ github.event.release.tag_name }} - run: Write-Output "::set-env name=VERSION::$($Env:TAG_NAME)" + run: 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 @@ -98,7 +98,7 @@ jobs: Get-ChildItem "./Version" $version = Get-Content -Path ./Version/version.txt Write-Host "Version: $version" - Write-Output "::set-env name=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 @@ -152,7 +152,7 @@ jobs: Get-ChildItem "./Version" $version = Get-Content -Path ./Version/version.txt Write-Host "Version: $version" - Write-Output "::set-env name=VERSION::$version" + echo "VERSION=$version" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append Remove-Item -Path ./Version/version.txt Remove-Item -Path ./Version # Checkout/Create the branch @@ -225,7 +225,7 @@ jobs: Get-ChildItem "./Version" $version = Get-Content -Path ./Version/version.txt Write-Host "Version: $version" - Write-Output "::set-env name=VERSION::$version" + echo "VERSION=$version" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append Remove-Item -Path ./Version/version.txt Remove-Item -Path ./Version # Checkout/Create the branch diff --git a/Pepperdash Core/Pepperdash Core/Comm/CommunicationStreamDebugging.cs b/Pepperdash Core/Pepperdash Core/Comm/CommunicationStreamDebugging.cs index f3b5613..b5b1c15 100644 --- a/Pepperdash Core/Pepperdash Core/Comm/CommunicationStreamDebugging.cs +++ b/Pepperdash Core/Pepperdash Core/Comm/CommunicationStreamDebugging.cs @@ -79,10 +79,7 @@ namespace PepperDash.Core _DebugTimeoutInMs = minutes * 60000; - if (DebugExpiryPeriod != null) - { - DisableDebugging(); - } + StopDebugTimer(); DebugExpiryPeriod = new CTimer((o) => DisableDebugging(), _DebugTimeoutInMs); @@ -101,14 +98,24 @@ namespace PepperDash.Core /// private void DisableDebugging() { - DebugExpiryPeriod.Stop(); - DebugExpiryPeriod.Dispose(); - DebugExpiryPeriod = null; + StopDebugTimer(); + Debug.SetDeviceDebugSettings(ParentDeviceKey, eStreamDebuggingSetting.Off); + } + + private void StopDebugTimer() + { RxStreamDebuggingIsEnabled = false; TxStreamDebuggingIsEnabled = false; - Debug.SetDeviceDebugSettings(ParentDeviceKey, eStreamDebuggingSetting.Off); + if (DebugExpiryPeriod == null) + { + return; + } + + DebugExpiryPeriod.Stop(); + DebugExpiryPeriod.Dispose(); + DebugExpiryPeriod = null; } } @@ -123,4 +130,4 @@ namespace PepperDash.Core Tx = 2, Both = Rx | Tx } -} \ No newline at end of file +} diff --git a/Pepperdash Core/Pepperdash Core/Comm/eControlMethods.cs b/Pepperdash Core/Pepperdash Core/Comm/eControlMethods.cs index 5a7a6ea..4e5d2eb 100644 --- a/Pepperdash Core/Pepperdash Core/Comm/eControlMethods.cs +++ b/Pepperdash Core/Pepperdash Core/Comm/eControlMethods.cs @@ -11,6 +11,6 @@ namespace PepperDash.Core /// public enum eControlMethod { - None = 0, Com, IpId, IpidTcp, IR, Ssh, Tcpip, Telnet, Cresnet, Cec, Udp + None = 0, Com, IpId, IpidTcp, IR, Ssh, Tcpip, Telnet, Cresnet, Cec, Udp, Http, Https, Ws, Wss } } \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj b/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj index d4fbf18..f746202 100644 --- a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj +++ b/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj @@ -122,6 +122,16 @@ + + + + + + + + + + diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/Serialization/IXSigSerialization.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/Serialization/IXSigSerialization.cs new file mode 100644 index 0000000..074d8a5 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/Serialization/IXSigSerialization.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using PepperDash.Core.Intersystem.Tokens; + +namespace PepperDash.Core.Intersystem.Serialization +{ + /// + /// Interface to determine XSig serialization for an object. + /// + public interface IXSigSerialization + { + IEnumerable Serialize(); + T Deserialize(IEnumerable tokens) where T : class, IXSigSerialization; + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/Serialization/XSigSerializationException.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/Serialization/XSigSerializationException.cs new file mode 100644 index 0000000..f1c50f5 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/Serialization/XSigSerializationException.cs @@ -0,0 +1,11 @@ +using System; + +namespace PepperDash.Core.Intersystem.Serialization +{ + public class XSigSerializationException : Exception + { + public XSigSerializationException() { } + public XSigSerializationException(string message) : base(message) { } + public XSigSerializationException(string message, Exception inner) : base(message, inner) { } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigAnalogToken.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigAnalogToken.cs new file mode 100644 index 0000000..41427fe --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigAnalogToken.cs @@ -0,0 +1,55 @@ +using System; + +namespace PepperDash.Core.Intersystem.Tokens +{ + public sealed class XSigAnalogToken : XSigToken, IFormattable + { + private readonly ushort _value; + + public XSigAnalogToken(int index, ushort value) + : base(index) + { + // 10-bits available for analog encoded data + if (index >= 1024 || index < 0) + throw new ArgumentOutOfRangeException("index"); + + _value = value; + } + + public ushort Value + { + get { return _value; } + } + + public override XSigTokenType TokenType + { + get { return XSigTokenType.Analog; } + } + + public override byte[] GetBytes() + { + return new[] { + (byte)(0xC0 | ((Value & 0xC000) >> 10) | (Index >> 7)), + (byte)((Index - 1) & 0x7F), + (byte)((Value & 0x3F80) >> 7), + (byte)(Value & 0x7F) + }; + } + + public override XSigToken GetTokenWithOffset(int offset) + { + if (offset == 0) return this; + return new XSigAnalogToken(Index + offset, Value); + } + + public override string ToString() + { + return Index + " = 0x" + Value.ToString("X4"); + } + + public string ToString(string format, IFormatProvider formatProvider) + { + return Value.ToString(format, formatProvider); + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigDigitalToken.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigDigitalToken.cs new file mode 100644 index 0000000..f721387 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigDigitalToken.cs @@ -0,0 +1,53 @@ +using System; + +namespace PepperDash.Core.Intersystem.Tokens +{ + public sealed class XSigDigitalToken : XSigToken + { + private readonly bool _value; + + public XSigDigitalToken(int index, bool value) + : base(index) + { + // 12-bits available for digital encoded data + if (index >= 4096 || index < 0) + throw new ArgumentOutOfRangeException("index"); + + _value = value; + } + + public bool Value + { + get { return _value; } + } + + public override XSigTokenType TokenType + { + get { return XSigTokenType.Digital; } + } + + public override byte[] GetBytes() + { + return new[] { + (byte)(0x80 | (Value ? 0 : 0x20) | (Index >> 7)), + (byte)((Index - 1) & 0x7F) + }; + } + + public override XSigToken GetTokenWithOffset(int offset) + { + if (offset == 0) return this; + return new XSigDigitalToken(Index + offset, Value); + } + + public override string ToString() + { + return Index + " = " + (Value ? "High" : "Low"); + } + + public string ToString(IFormatProvider formatProvider) + { + return Value.ToString(formatProvider); + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigSerialToken.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigSerialToken.cs new file mode 100644 index 0000000..cc61b1f --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigSerialToken.cs @@ -0,0 +1,54 @@ +using System; +using System.Text; + +namespace PepperDash.Core.Intersystem.Tokens +{ + public sealed class XSigSerialToken : XSigToken + { + private readonly string _value; + + public XSigSerialToken(int index, string value) + : base(index) + { + // 10-bits available for serial encoded data + if (index >= 1024 || index < 0) + throw new ArgumentOutOfRangeException("index"); + + _value = value; + } + + public string Value + { + get { return _value; } + } + + public override XSigTokenType TokenType + { + get { return XSigTokenType.Serial; } + } + + public override byte[] GetBytes() + { + var serialBytes = String.IsNullOrEmpty(Value) ? new byte[0] : Encoding.GetEncoding(28591).GetBytes(Value); + + var xsig = new byte[serialBytes.Length + 3]; + xsig[0] = (byte)(0xC8 | (Index >> 7)); + xsig[1] = (byte)((Index - 1) & 0x7F); + xsig[xsig.Length - 1] = 0xFF; + + Buffer.BlockCopy(serialBytes, 0, xsig, 2, serialBytes.Length); + return xsig; + } + + public override XSigToken GetTokenWithOffset(int offset) + { + if (offset == 0) return this; + return new XSigSerialToken(Index + offset, Value); + } + + public override string ToString() + { + return Index + " = \"" + Value + "\""; + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigToken.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigToken.cs new file mode 100644 index 0000000..4c00a2e --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigToken.cs @@ -0,0 +1,45 @@ +namespace PepperDash.Core.Intersystem.Tokens +{ + /// + /// Represents the base class for all XSig datatypes. + /// + public abstract class XSigToken + { + private readonly int _index; + + /// + /// Constructs an XSigToken with the specified index. + /// + /// Index for the data. + protected XSigToken(int index) + { + _index = index; + } + + /// + /// XSig 1-based index. + /// + public int Index + { + get { return _index; } + } + + /// + /// XSigToken type. + /// + public abstract XSigTokenType TokenType { get; } + + /// + /// Generates the XSig bytes for the corresponding token. + /// + /// XSig byte array. + public abstract byte[] GetBytes(); + + /// + /// Returns a new token if necessary with an updated index based on the specified offset. + /// + /// Offset to adjust the index with. + /// XSigToken + public abstract XSigToken GetTokenWithOffset(int offset); + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigTokenType.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigTokenType.cs new file mode 100644 index 0000000..26d6c12 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/Tokens/XSigTokenType.cs @@ -0,0 +1,23 @@ +namespace PepperDash.Core.Intersystem.Tokens +{ + /// + /// XSig token types. + /// + public enum XSigTokenType + { + /// + /// Digital signal datatype. + /// + Digital, + + /// + /// Analog signal datatype. + /// + Analog, + + /// + /// Serial signal datatype. + /// + Serial + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/XSigHelpers.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/XSigHelpers.cs new file mode 100644 index 0000000..4ea6f63 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/XSigHelpers.cs @@ -0,0 +1,239 @@ +using System; +using System.Linq; +using Crestron.SimplSharp.CrestronIO; +using PepperDash.Core.Intersystem.Serialization; +using PepperDash.Core.Intersystem.Tokens; + +/* + Digital (2 bytes) + 10C##### 0####### (mask = 11000000_10000000b -> 0xC080) + + Analog (4 bytes) + 11aa0### 0####### (mask = 11001000_10000000b -> 0xC880) + 0aaaaaaa 0aaaaaaa + + Serial (Variable length) + 11001### 0####### (mask = 11111000_10000000b -> 0xF880) + dddddddd ........ <- up to 252 bytes of serial data (255 - 3) + 11111111 <- denotes end of data +*/ + +namespace PepperDash.Core.Intersystem +{ + /// + /// Helper methods for creating XSig byte sequences compatible with the Intersystem Communications (ISC) symbol. + /// + /// + /// Indexing is not from the start of each signal type but rather from the beginning of the first defined signal + /// the Intersystem Communications (ISC) symbol. + /// + public static class XSigHelpers + { + /// + /// Forces all outputs to 0. + /// + /// Bytes in XSig format for clear outputs trigger. + public static byte[] ClearOutputs() + { + return new byte[] { 0xFC }; + } + + /// + /// Evaluate all inputs and re-transmit any digital, analog, and permanent serail signals not set to 0. + /// + /// Bytes in XSig format for send status trigger. + public static byte[] SendStatus() + { + return new byte[] { 0xFD }; + } + + /// + /// Get bytes for an IXSigStateResolver object. + /// + /// XSig state resolver. + /// Bytes in XSig format for each token within the state representation. + public static byte[] GetBytes(IXSigSerialization xSigSerialization) + { + return GetBytes(xSigSerialization, 0); + } + + /// + /// Get bytes for an IXSigStateResolver object, with a specified offset. + /// + /// XSig state resolver. + /// Offset to which the data will be aligned. + /// Bytes in XSig format for each token within the state representation. + public static byte[] GetBytes(IXSigSerialization xSigSerialization, int offset) + { + var tokens = xSigSerialization.Serialize(); + if (tokens == null) return new byte[0]; + using (var memoryStream = new MemoryStream()) + { + using (var tokenWriter = new XSigTokenStreamWriter(memoryStream)) + tokenWriter.WriteXSigData(xSigSerialization, offset); + + return memoryStream.ToArray(); + } + } + + /// + /// Get bytes for a single digital signal. + /// + /// 1-based digital index + /// Digital data to be encoded + /// Bytes in XSig format for digtial information. + public static byte[] GetBytes(int index, bool value) + { + return GetBytes(index, 0, value); + } + + /// + /// Get bytes for a single digital signal. + /// + /// 1-based digital index + /// Index offset. + /// Digital data to be encoded + /// Bytes in XSig format for digtial information. + public static byte[] GetBytes(int index, int offset, bool value) + { + return new XSigDigitalToken(index + offset, value).GetBytes(); + } + + /// + /// Get byte sequence for multiple digital signals. + /// + /// Starting index of the sequence. + /// Digital signal value array. + /// Byte sequence in XSig format for digital signal information. + public static byte[] GetBytes(int startIndex, bool[] values) + { + return GetBytes(startIndex, 0, values); + } + + /// + /// Get byte sequence for multiple digital signals. + /// + /// Starting index of the sequence. + /// Index offset. + /// Digital signal value array. + /// Byte sequence in XSig format for digital signal information. + public static byte[] GetBytes(int startIndex, int offset, bool[] values) + { + // Digital XSig data is 2 bytes per value + const int fixedLength = 2; + var bytes = new byte[values.Length * fixedLength]; + for (var i = 0; i < values.Length; i++) + Buffer.BlockCopy(GetBytes(startIndex++, offset, values[i]), 0, bytes, i * fixedLength, fixedLength); + + return bytes; + } + + /// + /// Get bytes for a single analog signal. + /// + /// 1-based analog index + /// Analog data to be encoded + /// Bytes in XSig format for analog signal information. + public static byte[] GetBytes(int index, ushort value) + { + return GetBytes(index, 0, value); + } + + /// + /// Get bytes for a single analog signal. + /// + /// 1-based analog index + /// Index offset. + /// Analog data to be encoded + /// Bytes in XSig format for analog signal information. + public static byte[] GetBytes(int index, int offset, ushort value) + { + return new XSigAnalogToken(index + offset, value).GetBytes(); + } + + /// + /// Get byte sequence for multiple analog signals. + /// + /// Starting index of the sequence. + /// Analog signal value array. + /// Byte sequence in XSig format for analog signal information. + public static byte[] GetBytes(int startIndex, ushort[] values) + { + return GetBytes(startIndex, 0, values); + } + + /// + /// Get byte sequence for multiple analog signals. + /// + /// Starting index of the sequence. + /// Index offset. + /// Analog signal value array. + /// Byte sequence in XSig format for analog signal information. + public static byte[] GetBytes(int startIndex, int offset, ushort[] values) + { + // Analog XSig data is 4 bytes per value + const int fixedLength = 4; + var bytes = new byte[values.Length * fixedLength]; + for (var i = 0; i < values.Length; i++) + Buffer.BlockCopy(GetBytes(startIndex++, offset, values[i]), 0, bytes, i * fixedLength, fixedLength); + + return bytes; + } + + /// + /// Get bytes for a single serial signal. + /// + /// 1-based serial index + /// Serial data to be encoded + /// Bytes in XSig format for serial signal information. + public static byte[] GetBytes(int index, string value) + { + return GetBytes(index, 0, value); + } + + /// + /// Get bytes for a single serial signal. + /// + /// 1-based serial index + /// Index offset. + /// Serial data to be encoded + /// Bytes in XSig format for serial signal information. + public static byte[] GetBytes(int index, int offset, string value) + { + return new XSigSerialToken(index + offset, value).GetBytes(); + } + + /// + /// Get byte sequence for multiple serial signals. + /// + /// Starting index of the sequence. + /// Serial signal value array. + /// Byte sequence in XSig format for serial signal information. + public static byte[] GetBytes(int startIndex, string[] values) + { + return GetBytes(startIndex, 0, values); + } + + /// + /// Get byte sequence for multiple serial signals. + /// + /// Starting index of the sequence. + /// Index offset. + /// Serial signal value array. + /// Byte sequence in XSig format for serial signal information. + public static byte[] GetBytes(int startIndex, int offset, string[] values) + { + // Serial XSig data is not fixed-length like the other formats + var dstOffset = 0; + var bytes = new byte[values.Sum(v => v.Length + 3)]; + for (var i = 0; i < values.Length; i++) + { + var data = GetBytes(startIndex++, offset, values[i]); + Buffer.BlockCopy(data, 0, bytes, dstOffset, data.Length); + dstOffset += data.Length; + } + + return bytes; + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/XSigTokenStreamReader.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/XSigTokenStreamReader.cs new file mode 100644 index 0000000..9d70d02 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/XSigTokenStreamReader.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using Crestron.SimplSharp.CrestronIO; +using PepperDash.Core.Intersystem.Serialization; +using PepperDash.Core.Intersystem.Tokens; + +namespace PepperDash.Core.Intersystem +{ + /// + /// XSigToken stream reader. + /// + public sealed class XSigTokenStreamReader : IDisposable + { + private readonly Stream _stream; + private readonly bool _leaveOpen; + + /// + /// + /// XSigToken stream reader constructor. + /// + /// Input stream to read from. + /// Stream is null. + /// Stream cannot be read from. + public XSigTokenStreamReader(Stream stream) + : this(stream, false) { } + + /// + /// XSigToken stream reader constructor. + /// + /// Input stream to read from. + /// Determines whether to leave the stream open or not. + /// Stream is null. + /// Stream cannot be read from. + public XSigTokenStreamReader(Stream stream, bool leaveOpen) + { + if (stream == null) + throw new ArgumentNullException("stream"); + if (!stream.CanRead) + throw new ArgumentException("The specified stream cannot be read from."); + + _stream = stream; + _leaveOpen = leaveOpen; + } + + /// + /// Reads a 16-bit unsigned integer from the specified stream using Big Endian byte order. + /// + /// Input stream + /// Result + /// True if successful, otherwise false. + public static bool TryReadUInt16BE(Stream stream, out ushort value) + { + value = 0; + if (stream.Length < 2) + return false; + + var buffer = new byte[2]; + stream.Read(buffer, 0, 2); + value = (ushort)((buffer[0] << 8) | buffer[1]); + return true; + } + + /// + /// Read XSig token from the stream. + /// + /// XSigToken + /// Offset is less than 0. + public XSigToken ReadXSigToken() + { + ushort prefix; + if (!TryReadUInt16BE(_stream, out prefix)) + return null; + + if ((prefix & 0xF880) == 0xC800) // Serial data + { + var index = ((prefix & 0x0700) >> 1) | (prefix & 0x7F); + var n = 0; + const int maxSerialDataLength = 252; + var chars = new char[maxSerialDataLength]; + int ch; + while ((ch = _stream.ReadByte()) != 0xFF) + { + if (ch == -1) // Reached end of stream without end of data marker + return null; + + chars[n++] = (char)ch; + } + + return new XSigSerialToken((ushort)(index + 1), new string(chars, 0, n)); + } + + if ((prefix & 0xC880) == 0xC000) // Analog data + { + ushort data; + if (!TryReadUInt16BE(_stream, out data)) + return null; + + var index = ((prefix & 0x0700) >> 1) | (prefix & 0x7F); + var value = ((prefix & 0x3000) << 2) | ((data & 0x7F00) >> 1) | (data & 0x7F); + return new XSigAnalogToken((ushort)(index + 1), (ushort)value); + } + + if ((prefix & 0xC080) == 0x8000) // Digital data + { + var index = ((prefix & 0x1F00) >> 1) | (prefix & 0x7F); + var value = (prefix & 0x2000) == 0; + return new XSigDigitalToken((ushort)(index + 1), value); + } + + return null; + } + + /// + /// Reads all available XSig tokens from the stream. + /// + /// XSigToken collection. + public IEnumerable ReadAllXSigTokens() + { + var tokens = new List(); + XSigToken token; + while ((token = ReadXSigToken()) != null) + tokens.Add(token); + + return tokens; + } + + /// + /// Attempts to deserialize all XSig data within the stream from the current position. + /// + /// Type to deserialize the information to. + /// Deserialized object. + public T DeserializeStream() + where T : class, IXSigSerialization, new() + { + return new T().Deserialize(ReadAllXSigTokens()); + } + + /// + /// Disposes of the internal stream if specified to not leave open. + /// + public void Dispose() + { + if (!_leaveOpen) + _stream.Dispose(); + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/XSigUtility/XSigTokenStreamWriter.cs b/Pepperdash Core/Pepperdash Core/XSigUtility/XSigTokenStreamWriter.cs new file mode 100644 index 0000000..934f2c2 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/XSigUtility/XSigTokenStreamWriter.cs @@ -0,0 +1,136 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using Crestron.SimplSharp.CrestronIO; +using PepperDash.Core.Intersystem.Serialization; +using PepperDash.Core.Intersystem.Tokens; + +namespace PepperDash.Core.Intersystem +{ + /// + /// XSigToken stream writer. + /// + public sealed class XSigTokenStreamWriter : IDisposable + { + private readonly Stream _stream; + private readonly bool _leaveOpen; + + /// + /// + /// XSigToken stream writer constructor. + /// + /// Input stream to write to. + /// Stream is null. + /// Stream cannot be written to. + public XSigTokenStreamWriter(Stream stream) + : this(stream, false) { } + + /// + /// XSigToken stream writer constructor. + /// + /// Input stream to write to. + /// Determines whether to leave the stream open or not. + /// Stream is null. + /// Stream cannot be written to. + public XSigTokenStreamWriter(Stream stream, bool leaveOpen) + { + if (stream == null) + throw new ArgumentNullException("stream"); + if (!stream.CanWrite) + throw new ArgumentException("The specified stream cannot be written to."); + + _stream = stream; + _leaveOpen = leaveOpen; + } + + /// + /// Write XSig data gathered from an IXSigStateResolver to the stream. + /// + /// IXSigStateResolver object. + public void WriteXSigData(IXSigSerialization xSigSerialization) + { + WriteXSigData(xSigSerialization, 0); + } + + /// + /// Write XSig data gathered from an IXSigStateResolver to the stream. + /// + /// IXSigStateResolver object. + /// Index offset for each XSigToken. + public void WriteXSigData(IXSigSerialization xSigSerialization, int offset) + { + if (xSigSerialization == null) + throw new ArgumentNullException("xSigSerialization"); + + var tokens = xSigSerialization.Serialize(); + WriteXSigData(tokens, offset); + } + + /// + /// Write XSigToken to the stream. + /// + /// XSigToken object. + public void WriteXSigData(XSigToken token) + { + WriteXSigData(token, 0); + } + + /// + /// Write XSigToken to the stream. + /// + /// XSigToken object. + /// Index offset for each XSigToken. + public void WriteXSigData(XSigToken token, int offset) + { + WriteXSigData(new[] { token }, offset); + } + + /// + /// Writes an array of XSigTokens to the stream. + /// + /// XSigToken objects. + public void WriteXSigData(XSigToken[] tokens) + { + WriteXSigData(tokens.AsEnumerable()); + } + + /// + /// Write an enumerable collection of XSigTokens to the stream. + /// + /// XSigToken objects. + public void WriteXSigData(IEnumerable tokens) + { + WriteXSigData(tokens, 0); + } + + /// + /// Write an enumerable collection of XSigTokens to the stream. + /// + /// XSigToken objects. + /// Index offset for each XSigToken. + public void WriteXSigData(IEnumerable tokens, int offset) + { + if (offset < 0) + throw new ArgumentOutOfRangeException("offset", "Offset must be greater than or equal to 0."); + + if (tokens != null) + { + foreach (var token in tokens) + { + if (token == null) continue; + var bytes = token.GetTokenWithOffset(offset).GetBytes(); + _stream.Write(bytes, 0, bytes.Length); + } + } + } + + /// + /// Disposes of the internal stream if specified to not leave open. + /// + public void Dispose() + { + if (!_leaveOpen) + _stream.Dispose(); + } + } +} \ No newline at end of file diff --git a/Readme.md b/Readme.md index ab3ecf9..36f157c 100644 --- a/Readme.md +++ b/Readme.md @@ -3,9 +3,11 @@ ## [Latest Release](https://github.com/PepperDash/PepperDashCore/releases/latest) ## License + Provided under MIT license ## Overview + 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. ## Constituent Elements @@ -13,23 +15,24 @@ PepperDash Core is an open source Crestron SIMPL# library that can be used in SI - JSON Configuration File reading/writing - PortalConfigReader - Generic config classes -- Communications - - TCP/IP client and server - - Secure TCP/IP client and server - - UDP server - - SSH client - - HTTP SSE client - - HTTP (RESTful client) +- Communications + - TCP/IP client and server + - Secure TCP/IP client and server + - UDP server + - SSH client + - HTTP SSE client + - HTTP (RESTful client) - Debugging - - Console debugging - - Logging both to Crestron error log as well as a custom log file + - Console debugging + - Logging both to Crestron error log as well as a custom log file - System Info - Reports system and Ethernet information to SIMPL via SIMPL+ - Device Class, IKeyed and IKeyName Interfaces - - Base level device class that most classes derive from + - Base level device class that most classes derive from - Password Manager ## Minimum Requirements + - PepperDash Core runs on any Crestron 3-series processor or Crestron's VC-4 platform. - To edit and compile the source, Microsoft Visual Studio 2008 Professional with SP1 is required. - Crestron's Simpl# Plugin is also required (must be obtained from Crestron). @@ -39,12 +42,16 @@ PepperDash Core is an open source Crestron SIMPL# library that can be used in SI None ## Utilization + PepperDash Core has two main applications: - 1. As a utility library for SIMPL# Pro applications like [Essentials]([Essentials](https://github.com/PepperDash/Essentials)) - 2. As a library referenced by SIMPL+ wrapper modules in a SIMPL Windows application +1. As a utility library for SIMPL# Pro applications like [Essentials](<[Essentials](https://github.com/PepperDash/Essentials)>) +2. As a library referenced by SIMPL+ wrapper modules in a SIMPL Windows application - ## Documentation - For detailed documentation, follow this [LINK](https://github.com/PepperDash/PepperDashCore/wiki) to the Wiki. +## Documentation +For detailed documentation, follow this [LINK](https://github.com/PepperDash/PepperDashCore/wiki) to the Wiki. +## XSigUtility + + `XSigUtility` classes included with permission of Troy Garner. See https://github.com/bitm0de/XSigUtilityLibrary for more information