/// <summary>
        /// Creates a <see cref="IFeigReader"/> based on the supplied settings.
        /// </summary>
        ///
        /// <param name="settings">
        /// The settings used to create the reader.</param>
        /// <param name="logger">
        /// The logger used to log operations of transport and reader.</param>
        ///
        /// <exception cref="ArgumentNullException">
        /// A null reference was passed to a method that did not accept it as a valid argument.</exception>
        public static IFeigReader Create(FeigReaderSettings settings, ILogger logger)
        {
            Verify.NotNull(settings, nameof(settings));
            Verify.NotNull(logger, nameof(logger));

            var copy      = new FeigReaderSettings(settings);
            var transport = new DefaultFeigTransport(copy.TransportSettings, logger);

            return(new DefaultFeigReader(copy, transport, logger));
        }
        public void ReceivedDataIgnored()
        {
            var settingsA = new SerialTransportSettings {
                PortName = "COMA"
            };

            var logger = LoggerFactory.Create(
                builder => {
                builder.SetMinimumLevel(LogLevel.Trace);
                builder.AddSimpleConsole();
            }
                )
                         .CreateLogger("Test");

            using (var transportA = new DefaultFeigTransport(settingsA, logger))
            {
                var settings = new SerialTransportSettings {
                    PortName  = "COMB",
                    Baud      = 38400,
                    DataBits  = 8,
                    Parity    = Parity.Even,
                    StopBits  = StopBits.One,
                    Handshake = Handshake.None,
                };

                using (var transportB = Transport.Create(settings))
                {
                    transportA.Open();
                    transportB.Open();

                    transportB.Send(
                        BufferSpan.From(
                            0x02,
                            0x00,
                            0x0f,
                            0x00,
                            0x65,
                            0x00,
                            0x03,
                            0x03,
                            0x00,
                            0x44,
                            0x53,
                            0x0d,
                            0x30,
                            0x74,
                            0x69
                            )
                        );

                    Thread.Sleep(2000);
                }
            }
        }
        /// <summary>
        /// Creates a <see cref="IFeigReader"/> based on the supplied settings.
        /// </summary>
        ///
        /// <param name="settings">
        /// The settings used to create the reader.</param>
        /// <param name="hooks">
        /// A hooks implementation being called for sent or received data.</param>
        ///
        /// <exception cref="ArgumentNullException">
        /// A null reference was passed to a method that did not accept it as a valid argument.</exception>
        public static IFeigReader Create(FeigReaderSettings settings, ITransportHooks hooks)
        {
            Verify.NotNull(settings, nameof(settings));
            Verify.NotNull(hooks, nameof(hooks));

            var copy      = new FeigReaderSettings(settings);
            var logger    = NullLogger.Instance;
            var transport = new DefaultFeigTransport(copy.TransportSettings, logger, hooks);

            return(new DefaultFeigReader(copy, transport, logger));
        }
        public void Open_Close_Dispose()
        {
            var settingsA = new SerialTransportSettings {
                PortName = "COMA"
            };

            var logger = LoggerFactory.Create(
                builder => {
                builder.SetMinimumLevel(LogLevel.Trace);
                builder.AddSimpleConsole();
            }
                )
                         .CreateLogger("Test");

            using (var transportA = new DefaultFeigTransport(settingsA, logger))
            {
                transportA.Open();
                transportA.Close();
            }
        }
        public async Task Success_ReceivedResponse_MultipleTimes()
        {
            var settingsA = new SerialTransportSettings {
                PortName = "COMA"
            };

            var logger = LoggerFactory.Create(
                builder => {
                builder.SetMinimumLevel(LogLevel.Trace);
                builder.AddSimpleConsole();
            }
                )
                         .CreateLogger("Test");

            using (var transportA = new DefaultFeigTransport(settingsA, logger))
            {
                var settings = new SerialTransportSettings {
                    PortName  = "COMB",
                    Baud      = 38400,
                    DataBits  = 8,
                    Parity    = Parity.Even,
                    StopBits  = StopBits.One,
                    Handshake = Handshake.None,
                };

                using (var transportB = Transport.Create(settings))
                {
                    transportA.Open();
                    transportB.Open();

                    transportB.Received.Subscribe(
                        data => {
                        if (data[0] == 0x02 &&
                            data[1] == 0x00 &&
                            data[2] == 0x07 &&
                            data[3] == 0xff &&
                            data[4] == 0x65 &&
                            data[5] == 0x6e &&
                            data[6] == 0x61)
                        {
                            transportB.Send(
                                BufferSpan.From(
                                    0x02,
                                    0x00,
                                    0x0f,
                                    0x00,
                                    0x65,
                                    0x00,
                                    0x03,
                                    0x03,
                                    0x00,
                                    0x44,
                                    0x53,
                                    0x0d,
                                    0x30,
                                    0x74,
                                    0x69
                                    )
                                );
                        }
                        else
                        {
                            Assert.Fail("Received unknown data");
                        }
                    }
                        );

                    for (var i = 0; i < 100; i++)
                    {
                        var result = await transportA.Transfer(
                            new FeigRequest { Command = FeigCommand.GetSoftwareVersion },
                            FeigProtocol.Advanced,
                            TimeSpan.FromMilliseconds(5000),
                            default
                            );

                        Check.That(result.Status).IsEqualTo(FeigTransferStatus.Success);
                        Check.That(result.Response.Address).IsEqualTo(0x00);
                        Check.That(result.Response.Command).IsEqualTo(FeigCommand.GetSoftwareVersion);
                        Check.That(result.Response.Status).IsEqualTo(FeigStatus.OK);

                        Check.That(result.Response.Data.ToArray())
                        .ContainsExactly(
                            0x03,
                            0x03,
                            0x00,
                            0x44,
                            0x53,
                            0x0d,
                            0x30
                            );
                    }
                }
            }
        }
        public async Task UnexpectedResponse()
        {
            var settingsA = new SerialTransportSettings {
                PortName = "COMA"
            };

            var logger = LoggerFactory.Create(
                builder => {
                builder.SetMinimumLevel(LogLevel.Trace);
                builder.AddSimpleConsole();
            }
                )
                         .CreateLogger("Test");

            using (var transportA = new DefaultFeigTransport(settingsA, logger))
            {
                var settings = new SerialTransportSettings {
                    PortName  = "COMB",
                    Baud      = 38400,
                    DataBits  = 8,
                    Parity    = Parity.Even,
                    StopBits  = StopBits.One,
                    Handshake = Handshake.None,
                };

                using (var transportB = Transport.Create(settings))
                {
                    transportA.Open();
                    transportB.Open();

                    transportB.Received.Subscribe(
                        data => {
                        if (data[0] == 0x02 &&
                            data[1] == 0x00 &&
                            data[2] == 0x07 &&
                            data[3] == 0xff &&
                            data[4] == 0x65 &&
                            data[5] == 0x6e &&
                            data[6] == 0x61)
                        {
                            transportB.Send(
                                BufferSpan.From(
                                    0x02,
                                    0x00,
                                    0x08,
                                    0xff,
                                    0x80,
                                    0x81,
                                    0x40,
                                    0x3a,
                                    0x02,
                                    0x00,
                                    0x08,
                                    0xff,
                                    0x80,
                                    0x82,
                                    0xdb,
                                    0x08,
                                    0x02,
                                    0x00,
                                    0x08,
                                    0xff,
                                    0x80,
                                    0x83,
                                    0x52
                                    )
                                );
                        }
                        else
                        {
                            Assert.Fail("Received unknown data");
                        }
                    }
                        );

                    var result = await transportA.Transfer(
                        new FeigRequest { Command = FeigCommand.GetSoftwareVersion },
                        FeigProtocol.Advanced,
                        TimeSpan.FromMilliseconds(5000),
                        default
                        );

                    Check.That(result.Status).IsEqualTo(FeigTransferStatus.UnexpectedResponse);
                    Check.That(result.Response.Address).IsEqualTo(0xff);
                    Check.That(result.Response.Command).IsEqualTo(FeigCommand.ReadConfiguration);
                    Check.That(result.Response.Status).IsEqualTo(FeigStatus.LengthError);
                }
            }
        }
        public async Task Canceled()
        {
            var settingsA = new SerialTransportSettings {
                PortName = "COMA"
            };

            var logger = LoggerFactory.Create(
                builder => {
                builder.SetMinimumLevel(LogLevel.Trace);
                builder.AddSimpleConsole();
            }
                )
                         .CreateLogger("Test");

            using (var transportA = new DefaultFeigTransport(settingsA, logger))
            {
                var settings = new SerialTransportSettings {
                    PortName  = "COMB",
                    Baud      = 38400,
                    DataBits  = 8,
                    Parity    = Parity.Even,
                    StopBits  = StopBits.One,
                    Handshake = Handshake.None,
                };

                using (var transportB = Transport.Create(settings))
                {
                    transportA.Open();
                    transportB.Open();

                    transportB.Received.Subscribe(
                        data => {
                        if (data[0] == 0x02 &&
                            data[1] == 0x00 &&
                            data[2] == 0x07 &&
                            data[3] == 0xff &&
                            data[4] == 0x65 &&
                            data[5] == 0x6e &&
                            data[6] == 0x61)
                        {
                            Thread.Sleep(500);

                            transportB.Send(
                                BufferSpan.From(
                                    0x02,
                                    0x00,
                                    0x0f,
                                    0x00,
                                    0x65,
                                    0x00,
                                    0x03,
                                    0x03,
                                    0x00,
                                    0x44,
                                    0x53,
                                    0x0d,
                                    0x30,
                                    0x74,
                                    0x69
                                    )
                                );
                        }
                        else
                        {
                            Assert.Fail("Received unknown data");
                        }
                    }
                        );

                    var cts = new CancellationTokenSource();

                    Task <FeigTransferResult> task = transportA.Transfer(
                        new FeigRequest {
                        Command = FeigCommand.GetSoftwareVersion
                    },
                        FeigProtocol.Advanced,
                        TimeSpan.FromMilliseconds(5000),
                        cts.Token
                        );

                    cts.Cancel();

                    var result = await task;

                    Check.That(result.Status).IsEqualTo(FeigTransferStatus.Canceled);
                    Check.That(result.Response).IsNull();

                    Thread.Sleep(1000);
                }
            }
        }