Represents a message excluding payload.

See the "Ember+ Specification"Ember+ Specification, chapter "Message Framing".

Non-Ember messages are not currently supported.

        private async Task <NonSeekableStream> WriteMessageCoreAsync(
            S101Message message, CancellationToken cancellationToken)
        {
            this.AssertNotDisposed();

            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            if ((this.stream != null) && this.stream.CanWrite)
            {
                throw new InvalidOperationException(
                          "DisposeAsync() has not been called on the payload stream of the previous message.");
            }

            this.stream = await MessageEncodingStream.CreateAsync(this.writeBuffer, message, cancellationToken);

            if (!message.CanHavePayload)
            {
                await this.stream.DisposeAsync(cancellationToken);

                this.stream = null;
            }

            return(this.stream);
        }
        private void ValidateMessage(S101Message newMessage)
        {
            if (newMessage == null)
            {
                throw new S101Exception("Unexpected end of stream.");
            }

            if (this.message.Slot != newMessage.Slot)
            {
                throw new S101Exception("Inconsistent Slot in multi-packet message.");
            }

            if (!this.message.Command.Equals(newMessage.Command))
            {
                throw new S101Exception("Inconsistent Command in multi-packet message.");
            }

            if ((newMessage.PacketFlags & PacketFlags.FirstPacket) > 0)
            {
                throw new S101Exception(string.Format(
                                            CultureInfo.InvariantCulture, "{0} flag in subsequent packet.", PacketFlags.FirstPacket));
            }

            this.message = newMessage;
        }
Exemple #3
0
 private MessageEncodingStream(S101Message message, WriteBuffer rawBuffer, FramingStream framingStream)
 {
     this.unframedBuffer = new WriteBuffer(this.WriteUnframedAsync, Constants.MessageHeaderMaxLength);
     this.message        = message;
     this.rawBuffer      = rawBuffer;
     this.framingStream  = framingStream;
 }
Exemple #4
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        internal EventInfo LogMessage(DateTime timeUtc, string direction, S101Message message, byte[] payload)
        {
            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            if (direction == null)
            {
                throw new ArgumentNullException(nameof(direction));
            }

            this.WriteStartEvent(LogNames.Message, timeUtc);
            int number;

            this.messageCounts.TryGetValue(direction, out number);
            this.messageCounts[direction] = ++number;

            this.xmlLogWriter.WriteAttributeString(LogNames.Direction, direction);
            this.xmlLogWriter.WriteAttributeString(LogNames.Number, number.ToString(CultureInfo.InvariantCulture));
            this.xmlLogWriter.WriteElementString(
                LogNames.Slot, message.Slot.ToString("X2", CultureInfo.InvariantCulture));
            this.xmlLogWriter.WriteElementString(LogNames.Command, message.Command.ToString());
            this.xmlLogWriter.WriteStartElement(LogNames.Payload);

            if (payload != null)
            {
                this.converter.ToXml(payload, this.xmlLogWriter);
            }

            this.xmlLogWriter.WriteEndElement();
            this.WriteEndEvent();
            return(new EventInfo(timeUtc, number));
        }
Exemple #5
0
 private void Clear()
 {
     this.eventType = null;
     this.timeUtc   = DateTime.Today;
     this.direction = null;
     this.number    = 0;
     this.message   = null;
     this.payload   = null;
 }
Exemple #6
0
        public void PayloadTest()
        {
            AsyncPump.Run(
                async() =>
            {
#pragma warning disable SA1123 // Do not place regions within elements. Necessary so that tested code snippets can be included in the documentation.
                #region Payload Test
                var writtenMessage = new S101Message(0x00, new EmberData(0x01, 0x0A, 0x02));
                var writtenPayload = new byte[8192];
                this.Random.NextBytes(writtenPayload);

                using (var encodedStream = new MemoryStream())
                {
                    // First we create a writer, which can be used to write multiple messages.
                    // We specify which methods are used to write encoded output and flush it plus the size the internal
                    // buffer should have.
                    var writer = new S101Writer(encodedStream.WriteAsync);

                    // Next we write the message. In return we get a Stream object for the payload.
                    using (var payloadStream =
                               await writer.WriteMessageAsync(writtenMessage, CancellationToken.None))
                    {
                        // Now we write the payload.
                        await payloadStream.WriteAsync(writtenPayload, 0, writtenPayload.Length);
                        await payloadStream.DisposeAsync(CancellationToken.None);
                    }

                    await writer.DisposeAsync(CancellationToken.None);

                    // Reset the encoded stream to the beginning, so that we can read from it.
                    encodedStream.Position = 0;

                    // First we create a reader, which can be used to read multiple messages.
                    // We specify which methods are used to read encoded input.
                    var reader = new S101Reader(encodedStream.ReadAsync);
                    Assert.IsTrue(await reader.ReadAsync(CancellationToken.None));     // Read the first message
                    var readMessage = reader.Message;

                    // Assert the written and read messages are equal
                    Assert.AreEqual(writtenMessage.Slot, readMessage.Slot);
                    Assert.AreEqual(writtenMessage.Command, readMessage.Command);

                    using (var readPayload = new MemoryStream())
                    {
                        await reader.Payload.CopyToAsync(readPayload);     // Copy the payload.
                        // Assert that there is only one message
                        Assert.IsFalse(await reader.ReadAsync(CancellationToken.None));
                        CollectionAssert.AreEqual(writtenPayload, readPayload.ToArray());
                    }

                    await reader.DisposeAsync(CancellationToken.None);
                }
                #endregion
#pragma warning restore SA1123 // Do not place regions within elements
            });
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        internal static async Task<MessageEncodingStream> CreateAsync(
            WriteBuffer rawBuffer, S101Message message, CancellationToken cancellationToken)
        {
            message.PacketFlags =
                PacketFlags.FirstPacket | (message.CanHaveMultiplePackets ? PacketFlags.None : PacketFlags.LastPacket);
            var framingStream = await FramingStream.CreateAsync(rawBuffer, cancellationToken);
            var result = new MessageEncodingStream(message, rawBuffer, framingStream);
            await message.WriteToAsync(result.unframedBuffer, cancellationToken);
            return result;
        }
Exemple #8
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        internal static async Task <MessageEncodingStream> CreateAsync(
            WriteBuffer rawBuffer, S101Message message, CancellationToken cancellationToken)
        {
            message.PacketFlags =
                PacketFlags.FirstPacket | (message.CanHaveMultiplePackets ? PacketFlags.None : PacketFlags.LastPacket);
            var framingStream = await FramingStream.CreateAsync(rawBuffer, cancellationToken);

            var result = new MessageEncodingStream(message, rawBuffer, framingStream);
            await message.WriteToAsync(result.unframedBuffer, cancellationToken);

            return(result);
        }
Exemple #9
0
        private bool ReadCore()
        {
            while (this.logReader.IsStartElement(LogNames.Event))
            {
                switch (this.eventType = this.logReader.GetAttribute(LogNames.Type))
                {
                case LogNames.Message:
                    this.timeUtc   = this.ReadTime();
                    this.direction = this.logReader.GetAttribute(LogNames.Direction);
                    this.number    = int.Parse(
                        this.logReader.GetAttribute(LogNames.Number),
                        NumberStyles.None,
                        CultureInfo.InvariantCulture);
                    this.logReader.ReadStartElement(LogNames.Event);
                    this.logReader.ReadStartElement(LogNames.Slot);
                    var slot = ParseHex(this.logReader.ReadContentAsString());
                    this.logReader.ReadEndElement();
                    this.logReader.ReadStartElement(LogNames.Command);
                    var command = S101Command.Parse(this.logReader.ReadContentAsString());
                    this.logReader.ReadEndElement();
                    this.message = new S101Message(slot, command);

                    if (!this.logReader.IsStartElement(LogNames.Payload))
                    {
                        throw new XmlException("The Payload element is missing.");
                    }

                    this.payload = this.GetLogPayload();
                    return(true);

                case LogNames.OutOfFrameByte:
                    this.timeUtc   = this.ReadTime();
                    this.direction = this.logReader.GetAttribute(LogNames.Direction);
                    this.number    = 0;
                    this.message   = null;
                    this.logReader.ReadStartElement(LogNames.Event);
                    this.payload = new[] { ParseHex(this.logReader.ReadContentAsString()) };
                    this.logReader.ReadEndElement();
                    return(true);

                default:
                    this.logReader.Skip();
                    break;
                }
            }

            this.logReader.ReadEndElement();
            this.Clear();
            return(false);
        }
        private async Task <int> ReadFromCurrentPacketAsync(
            byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            int read;

            while (((read = await this.deframedBuffer.ReadAsync(buffer, offset, count, cancellationToken)) == 0) &&
                   (count > 0) && (this.message != null) && this.message.CanHavePayload &&
                   this.message.CanHaveMultiplePackets && ((this.message.PacketFlags & PacketFlags.LastPacket) == 0))
            {
                this.deframingStream.Dispose();
                this.deframingStream = new DeframingStream(this.rawBuffer, this.outOfFrameByteReceived);
                this.ValidateMessage(await S101Message.ReadFromAsync(this.deframedBuffer, cancellationToken));
            }

            return(read);
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        internal static async Task <MessageDecodingStream> CreateAsync(
            ReadBuffer rawBuffer,
            byte[] discardBuffer,
            Action <byte> outOfFrameByteReceived,
            CancellationToken cancellationToken)
        {
            var result     = new MessageDecodingStream(rawBuffer, discardBuffer, outOfFrameByteReceived);
            var newMessage = await S101Message.ReadFromAsync(result.deframedBuffer, cancellationToken);

            if ((newMessage != null) && newMessage.CanHaveMultiplePackets &&
                ((newMessage.PacketFlags & PacketFlags.FirstPacket) == 0))
            {
                throw new S101Exception(string.Format(
                                            CultureInfo.InvariantCulture, "Missing {0} flag in first packet.", PacketFlags.FirstPacket));
            }

            result.message = newMessage;
            return(result);
        }
Exemple #12
0
        private static async Task <byte[]> Encode(S101Message message, byte[] payload = null)
        {
            using (var asyncStream = new MemoryStream())
            {
                var writer = new S101Writer(
                    async(b, o, c, t) =>
                {
                    // This makes the read operation truly asynchronous, which helps to improve code coverage.
                    await Task.Delay(1);
                    await asyncStream.WriteAsync(b, o, c, t);
                },
                    1);

                using (var encodingStream = await writer.WriteMessageAsync(message, CancellationToken.None))
                {
                    Assert.AreEqual(encodingStream == null, payload == null);

                    if (encodingStream != null)
                    {
                        Assert.IsFalse(encodingStream.CanRead);
                        Assert.IsTrue(encodingStream.CanWrite);
                        await encodingStream.WriteAsync(payload, 0, payload.Length, CancellationToken.None);

                        await encodingStream.FlushAsync(CancellationToken.None);

                        await encodingStream.DisposeAsync(CancellationToken.None);

                        Assert.IsFalse(encodingStream.CanWrite);
                        await AssertThrowAsync <ObjectDisposedException>(
                            () => encodingStream.WriteAsync(new byte[] { 0 }, 0, 1, CancellationToken.None));
                        await AssertThrowAsync <ObjectDisposedException>(
                            () => encodingStream.FlushAsync(CancellationToken.None));
                    }
                }

                await writer.DisposeAsync(CancellationToken.None);

                return(asyncStream.ToArray());
            }
        }
        private async Task SendMessageCoreAsync(S101Message message, byte[] payload)
        {
            await this.EnqueueLogOperation(() => this.logger.LogMessage(LogNames.Send, message, payload));

            await this.sendQueue.Enqueue(
                async() =>
            {
                var payloadStream = await this.writer.WriteMessageAsync(message, this.source.Token);

                if ((payload == null) != (payloadStream == null))
                {
                    throw new ArgumentException(
                        "The payload requirements of the command of the passed message do not match the passed payload.",
                        nameof(payload));
                }

                if (payload != null)
                {
                    await payloadStream.WriteAsync(payload, 0, payload.Length, this.source.Token);
                    await payloadStream.DisposeAsync(this.source.Token);
                }
            });
        }
Exemple #14
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        internal MessageReceivedEventArgs(S101Message message, byte[] payload, bool isAnotherMessageAvailable)
        {
            this.message = message;
            this.payload = payload;
            this.isAnotherMessageAvailable = isAnotherMessageAvailable;
        }
 private MessageEncodingStream(S101Message message, WriteBuffer rawBuffer, FramingStream framingStream)
 {
     this.unframedBuffer = new WriteBuffer(this.WriteUnframedAsync, Constants.MessageHeaderMaxLength);
     this.message = message;
     this.rawBuffer = rawBuffer;
     this.framingStream = framingStream;
 }
Exemple #16
0
 public Task<NonSeekableStream> WriteMessageAsync(S101Message message, CancellationToken cancellationToken)
 {
     return this.taskSingleton.Execute(() => this.WriteMessageCoreAsync(message, cancellationToken));
 }
Exemple #17
0
        private async Task<NonSeekableStream> WriteMessageCoreAsync(
            S101Message message, CancellationToken cancellationToken)
        {
            this.AssertNotDisposed();

            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            if ((this.stream != null) && this.stream.CanWrite)
            {
                throw new InvalidOperationException(
                    "DisposeAsync() has not been called on the payload stream of the previous message.");
            }

            this.stream = await MessageEncodingStream.CreateAsync(this.writeBuffer, message, cancellationToken);

            if (!message.CanHavePayload)
            {
                await this.stream.DisposeAsync(cancellationToken);
                this.stream = null;
            }

            return this.stream;
        }
 public Task <NonSeekableStream> WriteMessageAsync(S101Message message, CancellationToken cancellationToken)
 {
     return(this.taskSingleton.Execute(() => this.WriteMessageCoreAsync(message, cancellationToken)));
 }
        public void PayloadTest()
        {
            AsyncPump.Run(
                async () =>
                {
#pragma warning disable SA1123 // Do not place regions within elements. Necessary so that tested code snippets can be included in the documentation.
                    #region Payload Test
                    var writtenMessage = new S101Message(0x00, new EmberData(0x01, 0x0A, 0x02));
                    var writtenPayload = new byte[8192];
                    this.Random.NextBytes(writtenPayload);

                    using (var encodedStream = new MemoryStream())
                    {
                        // First we create a writer, which can be used to write multiple messages.
                        // We specify which methods are used to write encoded output and flush it plus the size the internal
                        // buffer should have.
                        var writer = new S101Writer(encodedStream.WriteAsync);

                        // Next we write the message. In return we get a Stream object for the payload.
                        using (var payloadStream =
                            await writer.WriteMessageAsync(writtenMessage, CancellationToken.None))
                        {
                            // Now we write the payload.
                            await payloadStream.WriteAsync(writtenPayload, 0, writtenPayload.Length);
                            await payloadStream.DisposeAsync(CancellationToken.None);
                        }

                        await writer.DisposeAsync(CancellationToken.None);

                        // Reset the encoded stream to the beginning, so that we can read from it.
                        encodedStream.Position = 0;

                        // First we create a reader, which can be used to read multiple messages.
                        // We specify which methods are used to read encoded input.
                        var reader = new S101Reader(encodedStream.ReadAsync);
                        Assert.IsTrue(await reader.ReadAsync(CancellationToken.None)); // Read the first message
                        var readMessage = reader.Message;

                        // Assert the written and read messages are equal
                        Assert.AreEqual(writtenMessage.Slot, readMessage.Slot);
                        Assert.AreEqual(writtenMessage.Command, readMessage.Command);

                        using (var readPayload = new MemoryStream())
                        {
                            await reader.Payload.CopyToAsync(readPayload); // Copy the payload.
                            // Assert that there is only one message
                            Assert.IsFalse(await reader.ReadAsync(CancellationToken.None));
                            CollectionAssert.AreEqual(writtenPayload, readPayload.ToArray());
                        }

                        await reader.DisposeAsync(CancellationToken.None);
                    }
                    #endregion
#pragma warning restore SA1123 // Do not place regions within elements
                });
        }
 private void Clear()
 {
     this.eventType = null;
     this.timeUtc = DateTime.Today;
     this.direction = null;
     this.number = 0;
     this.message = null;
     this.payload = null;
 }
        private bool ReadCore()
        {
            while (this.logReader.IsStartElement(LogNames.Event))
            {
                switch (this.eventType = this.logReader.GetAttribute(LogNames.Type))
                {
                    case LogNames.Message:
                        this.timeUtc = this.ReadTime();
                        this.direction = this.logReader.GetAttribute(LogNames.Direction);
                        this.number = int.Parse(
                            this.logReader.GetAttribute(LogNames.Number),
                            NumberStyles.None,
                            CultureInfo.InvariantCulture);
                        this.logReader.ReadStartElement(LogNames.Event);
                        this.logReader.ReadStartElement(LogNames.Slot);
                        var slot = ParseHex(this.logReader.ReadContentAsString());
                        this.logReader.ReadEndElement();
                        this.logReader.ReadStartElement(LogNames.Command);
                        var command = S101Command.Parse(this.logReader.ReadContentAsString());
                        this.logReader.ReadEndElement();
                        this.message = new S101Message(slot, command);

                        if (!this.logReader.IsStartElement(LogNames.Payload))
                        {
                            throw new XmlException("The Payload element is missing.");
                        }

                        this.payload = this.GetLogPayload();
                        return true;
                    case LogNames.OutOfFrameByte:
                        this.timeUtc = this.ReadTime();
                        this.direction = this.logReader.GetAttribute(LogNames.Direction);
                        this.number = 0;
                        this.message = null;
                        this.logReader.ReadStartElement(LogNames.Event);
                        this.payload = new[] { ParseHex(this.logReader.ReadContentAsString()) };
                        this.logReader.ReadEndElement();
                        return true;
                    default:
                        this.logReader.Skip();
                        break;
                }
            }

            this.logReader.ReadEndElement();
            this.Clear();
            return false;
        }
Exemple #22
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        internal EventInfo LogMessage(DateTime timeUtc, string direction, S101Message message, byte[] payload)
        {
            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            if (direction == null)
            {
                throw new ArgumentNullException(nameof(direction));
            }

            this.WriteStartEvent(LogNames.Message, timeUtc);
            int number;
            this.messageCounts.TryGetValue(direction, out number);
            this.messageCounts[direction] = ++number;

            this.xmlLogWriter.WriteAttributeString(LogNames.Direction, direction);
            this.xmlLogWriter.WriteAttributeString(LogNames.Number, number.ToString(CultureInfo.InvariantCulture));
            this.xmlLogWriter.WriteElementString(
                LogNames.Slot, message.Slot.ToString("X2", CultureInfo.InvariantCulture));
            this.xmlLogWriter.WriteElementString(LogNames.Command, message.Command.ToString());
            this.xmlLogWriter.WriteStartElement(LogNames.Payload);

            if (payload != null)
            {
                this.converter.ToXml(payload, this.xmlLogWriter);
            }

            this.xmlLogWriter.WriteEndElement();
            this.WriteEndEvent();
            return new EventInfo(timeUtc, number);
        }
Exemple #23
0
 /// <inheritdoc/>
 public EventInfo LogMessage(string direction, S101Message message, byte[] payload) =>
     this.LogMessage(DateTime.UtcNow, direction, message, payload);
 public Task SendMessageAsync(S101Message message, byte[] payload)
 {
     this.AssertPreconditions();
     return(this.SendMessageCoreAsync(message, payload));
 }
        private void ValidateMessage(S101Message newMessage)
        {
            if (newMessage == null)
            {
                throw new S101Exception("Unexpected end of stream.");
            }

            if (this.message.Slot != newMessage.Slot)
            {
                throw new S101Exception("Inconsistent Slot in multi-packet message.");
            }

            if (!this.message.Command.Equals(newMessage.Command))
            {
                throw new S101Exception("Inconsistent Command in multi-packet message.");
            }

            if ((newMessage.PacketFlags & PacketFlags.FirstPacket) > 0)
            {
                throw new S101Exception(string.Format(
                    CultureInfo.InvariantCulture, "{0} flag in subsequent packet.", PacketFlags.FirstPacket));
            }

            this.message = newMessage;
        }
        private static async Task<byte[]> Encode(S101Message message, byte[] payload = null)
        {
            using (var asyncStream = new MemoryStream())
            {
                var writer = new S101Writer(
                    async (b, o, c, t) =>
                    {
                        // This makes the read operation truly asynchronous, which helps to improve code coverage.
                        await Task.Delay(1);
                        await asyncStream.WriteAsync(b, o, c, t);
                    },
                    1);

                using (var encodingStream = await writer.WriteMessageAsync(message, CancellationToken.None))
                {
                    Assert.AreEqual(encodingStream == null, payload == null);

                    if (encodingStream != null)
                    {
                        Assert.IsFalse(encodingStream.CanRead);
                        Assert.IsTrue(encodingStream.CanWrite);
                        await encodingStream.WriteAsync(payload, 0, payload.Length, CancellationToken.None);
                        await encodingStream.FlushAsync(CancellationToken.None);
                        await encodingStream.DisposeAsync(CancellationToken.None);
                        Assert.IsFalse(encodingStream.CanWrite);
                        await AssertThrowAsync<ObjectDisposedException>(
                            () => encodingStream.WriteAsync(new byte[] { 0 }, 0, 1, CancellationToken.None));
                        await AssertThrowAsync<ObjectDisposedException>(
                            () => encodingStream.FlushAsync(CancellationToken.None));
                    }
                }

                await writer.DisposeAsync(CancellationToken.None);
                return asyncStream.ToArray();
            }
        }
Exemple #27
0
 /// <inheritdoc/>
 public EventInfo LogMessage(string direction, S101Message message, byte[] payload) =>
 this.LogMessage(DateTime.UtcNow, direction, message, payload);
 /// <summary>Calls
 /// <see cref="SendMessageAsync(S101Message, byte[])">SendMessageAsync(<paramref name="message"/>, null)</see>.
 /// </summary>
 public Task SendMessageAsync(S101Message message) => this.SendMessageAsync(message, null);
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        internal MessageReceivedEventArgs(S101Message message, byte[] payload, bool isAnotherMessageAvailable)
        {
            this.message = message;
            this.payload = payload;
            this.isAnotherMessageAvailable = isAnotherMessageAvailable;
        }