mirror of
https://github.com/PepperDash/PepperDashCore.git
synced 2026-01-11 19:44:44 +00:00
add XSigUtility from Troy Garner
https://github.com/bitm0de/XSigUtilityLibrary
This commit is contained in:
@@ -122,6 +122,16 @@
|
|||||||
<Compile Include="WebApi\Presets\Preset.cs" />
|
<Compile Include="WebApi\Presets\Preset.cs" />
|
||||||
<Compile Include="WebApi\Presets\User.cs" />
|
<Compile Include="WebApi\Presets\User.cs" />
|
||||||
<Compile Include="WebApi\Presets\WebApiPasscodeClient.cs" />
|
<Compile Include="WebApi\Presets\WebApiPasscodeClient.cs" />
|
||||||
|
<Compile Include="XSigUtility\Serialization\IXSigSerialization.cs" />
|
||||||
|
<Compile Include="XSigUtility\Serialization\XSigSerializationException.cs" />
|
||||||
|
<Compile Include="XSigUtility\Tokens\XSigAnalogToken.cs" />
|
||||||
|
<Compile Include="XSigUtility\Tokens\XSigDigitalToken.cs" />
|
||||||
|
<Compile Include="XSigUtility\Tokens\XSigSerialToken.cs" />
|
||||||
|
<Compile Include="XSigUtility\Tokens\XSigToken.cs" />
|
||||||
|
<Compile Include="XSigUtility\Tokens\XSigTokenType.cs" />
|
||||||
|
<Compile Include="XSigUtility\XSigHelpers.cs" />
|
||||||
|
<Compile Include="XSigUtility\XSigTokenStreamReader.cs" />
|
||||||
|
<Compile Include="XSigUtility\XSigTokenStreamWriter.cs" />
|
||||||
<None Include="Properties\ControlSystem.cfg" />
|
<None Include="Properties\ControlSystem.cfg" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildBinPath)\Microsoft.CompactFramework.CSharp.targets" />
|
<Import Project="$(MSBuildBinPath)\Microsoft.CompactFramework.CSharp.targets" />
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using PepperDash.Core.Intersystem.Tokens;
|
||||||
|
|
||||||
|
namespace PepperDash.Core.Intersystem.Serialization
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interface to determine XSig serialization for an object.
|
||||||
|
/// </summary>
|
||||||
|
public interface IXSigSerialization
|
||||||
|
{
|
||||||
|
IEnumerable<XSigToken> Serialize();
|
||||||
|
T Deserialize<T>(IEnumerable<XSigToken> tokens) where T : class, IXSigSerialization;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
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 = 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 + "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
namespace PepperDash.Core.Intersystem.Tokens
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the base class for all XSig datatypes.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class XSigToken
|
||||||
|
{
|
||||||
|
private readonly int _index;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs an XSigToken with the specified index.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">Index for the data.</param>
|
||||||
|
protected XSigToken(int index)
|
||||||
|
{
|
||||||
|
_index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// XSig 1-based index.
|
||||||
|
/// </summary>
|
||||||
|
public int Index
|
||||||
|
{
|
||||||
|
get { return _index; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// XSigToken type.
|
||||||
|
/// </summary>
|
||||||
|
public abstract XSigTokenType TokenType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates the XSig bytes for the corresponding token.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>XSig byte array.</returns>
|
||||||
|
public abstract byte[] GetBytes();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a new token if necessary with an updated index based on the specified offset.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">Offset to adjust the index with.</param>
|
||||||
|
/// <returns>XSigToken</returns>
|
||||||
|
public abstract XSigToken GetTokenWithOffset(int offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
namespace PepperDash.Core.Intersystem.Tokens
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// XSig token types.
|
||||||
|
/// </summary>
|
||||||
|
public enum XSigTokenType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Digital signal datatype.
|
||||||
|
/// </summary>
|
||||||
|
Digital,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Analog signal datatype.
|
||||||
|
/// </summary>
|
||||||
|
Analog,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Serial signal datatype.
|
||||||
|
/// </summary>
|
||||||
|
Serial
|
||||||
|
}
|
||||||
|
}
|
||||||
239
Pepperdash Core/Pepperdash Core/XSigUtility/XSigHelpers.cs
Normal file
239
Pepperdash Core/Pepperdash Core/XSigUtility/XSigHelpers.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Helper methods for creating XSig byte sequences compatible with the Intersystem Communications (ISC) symbol.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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.
|
||||||
|
/// </remarks>
|
||||||
|
public static class XSigHelpers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Forces all outputs to 0.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Bytes in XSig format for clear outputs trigger.</returns>
|
||||||
|
public static byte[] ClearOutputs()
|
||||||
|
{
|
||||||
|
return new byte[] { 0xFC };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Evaluate all inputs and re-transmit any digital, analog, and permanent serail signals not set to 0.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Bytes in XSig format for send status trigger.</returns>
|
||||||
|
public static byte[] SendStatus()
|
||||||
|
{
|
||||||
|
return new byte[] { 0xFD };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get bytes for an IXSigStateResolver object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xSigSerialization">XSig state resolver.</param>
|
||||||
|
/// <returns>Bytes in XSig format for each token within the state representation.</returns>
|
||||||
|
public static byte[] GetBytes(IXSigSerialization xSigSerialization)
|
||||||
|
{
|
||||||
|
return GetBytes(xSigSerialization, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get bytes for an IXSigStateResolver object, with a specified offset.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xSigSerialization">XSig state resolver.</param>
|
||||||
|
/// <param name="offset">Offset to which the data will be aligned.</param>
|
||||||
|
/// <returns>Bytes in XSig format for each token within the state representation.</returns>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get bytes for a single digital signal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">1-based digital index</param>
|
||||||
|
/// <param name="value">Digital data to be encoded</param>
|
||||||
|
/// <returns>Bytes in XSig format for digtial information.</returns>
|
||||||
|
public static byte[] GetBytes(int index, bool value)
|
||||||
|
{
|
||||||
|
return GetBytes(index, 0, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get bytes for a single digital signal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">1-based digital index</param>
|
||||||
|
/// <param name="offset">Index offset.</param>
|
||||||
|
/// <param name="value">Digital data to be encoded</param>
|
||||||
|
/// <returns>Bytes in XSig format for digtial information.</returns>
|
||||||
|
public static byte[] GetBytes(int index, int offset, bool value)
|
||||||
|
{
|
||||||
|
return new XSigDigitalToken(index + offset, value).GetBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get byte sequence for multiple digital signals.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="startIndex">Starting index of the sequence.</param>
|
||||||
|
/// <param name="values">Digital signal value array.</param>
|
||||||
|
/// <returns>Byte sequence in XSig format for digital signal information.</returns>
|
||||||
|
public static byte[] GetBytes(int startIndex, bool[] values)
|
||||||
|
{
|
||||||
|
return GetBytes(startIndex, 0, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get byte sequence for multiple digital signals.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="startIndex">Starting index of the sequence.</param>
|
||||||
|
/// <param name="offset">Index offset.</param>
|
||||||
|
/// <param name="values">Digital signal value array.</param>
|
||||||
|
/// <returns>Byte sequence in XSig format for digital signal information.</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get bytes for a single analog signal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">1-based analog index</param>
|
||||||
|
/// <param name="value">Analog data to be encoded</param>
|
||||||
|
/// <returns>Bytes in XSig format for analog signal information.</returns>
|
||||||
|
public static byte[] GetBytes(int index, ushort value)
|
||||||
|
{
|
||||||
|
return GetBytes(index, 0, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get bytes for a single analog signal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">1-based analog index</param>
|
||||||
|
/// <param name="offset">Index offset.</param>
|
||||||
|
/// <param name="value">Analog data to be encoded</param>
|
||||||
|
/// <returns>Bytes in XSig format for analog signal information.</returns>
|
||||||
|
public static byte[] GetBytes(int index, int offset, ushort value)
|
||||||
|
{
|
||||||
|
return new XSigAnalogToken(index + offset, value).GetBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get byte sequence for multiple analog signals.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="startIndex">Starting index of the sequence.</param>
|
||||||
|
/// <param name="values">Analog signal value array.</param>
|
||||||
|
/// <returns>Byte sequence in XSig format for analog signal information.</returns>
|
||||||
|
public static byte[] GetBytes(int startIndex, ushort[] values)
|
||||||
|
{
|
||||||
|
return GetBytes(startIndex, 0, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get byte sequence for multiple analog signals.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="startIndex">Starting index of the sequence.</param>
|
||||||
|
/// <param name="offset">Index offset.</param>
|
||||||
|
/// <param name="values">Analog signal value array.</param>
|
||||||
|
/// <returns>Byte sequence in XSig format for analog signal information.</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get bytes for a single serial signal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">1-based serial index</param>
|
||||||
|
/// <param name="value">Serial data to be encoded</param>
|
||||||
|
/// <returns>Bytes in XSig format for serial signal information.</returns>
|
||||||
|
public static byte[] GetBytes(int index, string value)
|
||||||
|
{
|
||||||
|
return GetBytes(index, 0, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get bytes for a single serial signal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">1-based serial index</param>
|
||||||
|
/// <param name="offset">Index offset.</param>
|
||||||
|
/// <param name="value">Serial data to be encoded</param>
|
||||||
|
/// <returns>Bytes in XSig format for serial signal information.</returns>
|
||||||
|
public static byte[] GetBytes(int index, int offset, string value)
|
||||||
|
{
|
||||||
|
return new XSigSerialToken(index + offset, value).GetBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get byte sequence for multiple serial signals.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="startIndex">Starting index of the sequence.</param>
|
||||||
|
/// <param name="values">Serial signal value array.</param>
|
||||||
|
/// <returns>Byte sequence in XSig format for serial signal information.</returns>
|
||||||
|
public static byte[] GetBytes(int startIndex, string[] values)
|
||||||
|
{
|
||||||
|
return GetBytes(startIndex, 0, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get byte sequence for multiple serial signals.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="startIndex">Starting index of the sequence.</param>
|
||||||
|
/// <param name="offset">Index offset.</param>
|
||||||
|
/// <param name="values">Serial signal value array.</param>
|
||||||
|
/// <returns>Byte sequence in XSig format for serial signal information.</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// XSigToken stream reader.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class XSigTokenStreamReader : IDisposable
|
||||||
|
{
|
||||||
|
private readonly Stream _stream;
|
||||||
|
private readonly bool _leaveOpen;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <summary>
|
||||||
|
/// XSigToken stream reader constructor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">Input stream to read from.</param>
|
||||||
|
/// <exception cref="T:System.ArgumentNullException">Stream is null.</exception>
|
||||||
|
/// <exception cref="T:System.ArgumentException">Stream cannot be read from.</exception>
|
||||||
|
public XSigTokenStreamReader(Stream stream)
|
||||||
|
: this(stream, false) { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// XSigToken stream reader constructor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">Input stream to read from.</param>
|
||||||
|
/// <param name="leaveOpen">Determines whether to leave the stream open or not.</param>
|
||||||
|
/// <exception cref="ArgumentNullException">Stream is null.</exception>
|
||||||
|
/// <exception cref="ArgumentException">Stream cannot be read from.</exception>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a 16-bit unsigned integer from the specified stream using Big Endian byte order.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">Input stream</param>
|
||||||
|
/// <param name="value">Result</param>
|
||||||
|
/// <returns>True if successful, otherwise false.</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read XSig token from the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>XSigToken</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Offset is less than 0.</exception>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads all available XSig tokens from the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>XSigToken collection.</returns>
|
||||||
|
public IEnumerable<XSigToken> ReadAllXSigTokens()
|
||||||
|
{
|
||||||
|
var tokens = new List<XSigToken>();
|
||||||
|
XSigToken token;
|
||||||
|
while ((token = ReadXSigToken()) != null)
|
||||||
|
tokens.Add(token);
|
||||||
|
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to deserialize all XSig data within the stream from the current position.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type to deserialize the information to.</typeparam>
|
||||||
|
/// <returns>Deserialized object.</returns>
|
||||||
|
public T DeserializeStream<T>()
|
||||||
|
where T : class, IXSigSerialization, new()
|
||||||
|
{
|
||||||
|
return new T().Deserialize<T>(ReadAllXSigTokens());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes of the internal stream if specified to not leave open.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!_leaveOpen)
|
||||||
|
_stream.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// XSigToken stream writer.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class XSigTokenStreamWriter : IDisposable
|
||||||
|
{
|
||||||
|
private readonly Stream _stream;
|
||||||
|
private readonly bool _leaveOpen;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <summary>
|
||||||
|
/// XSigToken stream writer constructor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">Input stream to write to.</param>
|
||||||
|
/// <exception cref="T:System.ArgumentNullException">Stream is null.</exception>
|
||||||
|
/// <exception cref="T:System.ArgumentException">Stream cannot be written to.</exception>
|
||||||
|
public XSigTokenStreamWriter(Stream stream)
|
||||||
|
: this(stream, false) { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// XSigToken stream writer constructor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">Input stream to write to.</param>
|
||||||
|
/// <param name="leaveOpen">Determines whether to leave the stream open or not.</param>
|
||||||
|
/// <exception cref="ArgumentNullException">Stream is null.</exception>
|
||||||
|
/// <exception cref="ArgumentException">Stream cannot be written to.</exception>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write XSig data gathered from an IXSigStateResolver to the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xSigSerialization">IXSigStateResolver object.</param>
|
||||||
|
public void WriteXSigData(IXSigSerialization xSigSerialization)
|
||||||
|
{
|
||||||
|
WriteXSigData(xSigSerialization, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write XSig data gathered from an IXSigStateResolver to the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xSigSerialization">IXSigStateResolver object.</param>
|
||||||
|
/// <param name="offset">Index offset for each XSigToken.</param>
|
||||||
|
public void WriteXSigData(IXSigSerialization xSigSerialization, int offset)
|
||||||
|
{
|
||||||
|
if (xSigSerialization == null)
|
||||||
|
throw new ArgumentNullException("xSigSerialization");
|
||||||
|
|
||||||
|
var tokens = xSigSerialization.Serialize();
|
||||||
|
WriteXSigData(tokens, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write XSigToken to the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">XSigToken object.</param>
|
||||||
|
public void WriteXSigData(XSigToken token)
|
||||||
|
{
|
||||||
|
WriteXSigData(token, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write XSigToken to the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">XSigToken object.</param>
|
||||||
|
/// <param name="offset">Index offset for each XSigToken.</param>
|
||||||
|
public void WriteXSigData(XSigToken token, int offset)
|
||||||
|
{
|
||||||
|
WriteXSigData(new[] { token }, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes an array of XSigTokens to the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tokens">XSigToken objects.</param>
|
||||||
|
public void WriteXSigData(XSigToken[] tokens)
|
||||||
|
{
|
||||||
|
WriteXSigData(tokens.AsEnumerable());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write an enumerable collection of XSigTokens to the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tokens">XSigToken objects.</param>
|
||||||
|
public void WriteXSigData(IEnumerable<XSigToken> tokens)
|
||||||
|
{
|
||||||
|
WriteXSigData(tokens, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write an enumerable collection of XSigTokens to the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tokens">XSigToken objects.</param>
|
||||||
|
/// <param name="offset">Index offset for each XSigToken.</param>
|
||||||
|
public void WriteXSigData(IEnumerable<XSigToken> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes of the internal stream if specified to not leave open.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!_leaveOpen)
|
||||||
|
_stream.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user