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(); } } }