/// <summary>Creates a SerialDeviceEndpoint from a valid connection string.</summary>
        /// <param name="connectionString">The connection string.</param>
        /// <returns>A populated <see cref="SerialDeviceEndpoint" />.</returns>
        /// <exception cref="ArgumentException"></exception>
        /// <remarks>
        ///     <para>
        ///         Valid connection strings must match the following regular expression grammar:
        ///         <code lang="regex">
        ///         ^((COM|com)\d{1,3})(:((\d{3,7})(,(None|Even|Odd|Mark|Space))?(,(7|8))?(,(Zero|OnePointFive|One|Two))?(,(nodtr|dtr))?(,(norts|rts))?(,(None|XOnXOff|RequestToSend|RequestToSendXOnXOff))?)?)?$
        ///         </code>
        ///     </para>
        ///     <para>
        ///         Since that is a rather unfriendly expression to human eyes, one way to determine correct
        ///         connection string syntax is to create a specimen endpoint, passing in the required
        ///         configuration as constructor parameters. Then, write the object to an output stream. The
        ///         ToString method will write out a syntactically correct connection string. There are many
        ///         ways to accomplish this. For example, you can use the C# Interactive window in Visual
        ///         Studio, write a unit test, use Windows PowerShell or an utility such as LINQPad. In
        ///         LINQPad, the following fragment will suffice:
        ///         <code lang="csharp">
        ///             var endpoint = new SerialDeviceEndpoint("COM1"); // Pass in whatever parameters you need
        ///             endpoint.Dump();
        ///         </code>
        ///     </para>
        ///     <para>
        ///         It is probably best to avoid creating connection strings directly as this will tend to be
        ///         error prone, especially if it is from user-supplied input. Instead, create a user interface
        ///         that gathers all of the serial parameters from the user. For an ASCOM driver, this will
        ///         likely be part of the Setup Dialog. Save all the user-supplied data in your settings. Then
        ///         generate a connection string by creating a <see cref="SerialDeviceEndpoint" />, passing the
        ///         user settings in as constructor parameters. Finally, use <see cref="ToString" /> or the
        ///         <see cref="DeviceEndpoint.DeviceAddress" /> property to retrieve the corresponding
        ///         connection string. The connection string can either be saved along with user settings and
        ///         used 'as-is' in the next session, or it can be dynamically regenerated on demand using the
        ///         above technique - the choice is yours. It is also up to you whether or not you decide to
        ///         make the connection string visible to the user.
        ///     </para>
        /// </remarks>
        public static SerialDeviceEndpoint FromConnectionString(string connectionString)
        {
            Contract.Requires(!string.IsNullOrWhiteSpace(connectionString));
            Contract.Ensures(Contract.Result <DeviceEndpoint>() != null);
            var matches = SerialRegex.Match(connectionString);

            if (!matches.Success)
            {
                throw new ArgumentException(
                          "Not a valid serial connection string; Example: COM127:9600,None,8,One,dtr,norts",
                          nameof(connectionString));
            }
            var portName  = matches.Groups["PortName"].Value;
            var baud      = CaptureGroupOrDefault(matches, "Baud", 9600);
            var parity    = CaptureGroupOrDefault(matches, "Parity", Parity.None);
            var databits  = CaptureGroupOrDefault(matches, "DataBits", 8);
            var stopbits  = CaptureGroupOrDefault(matches, "StopBits", StopBits.One);
            var assertDtr = CaptureGroupOrDefault(matches, "DTR", "dtr")
                            .Equals("dtr", StringComparison.InvariantCultureIgnoreCase);
            var assertRts = CaptureGroupOrDefault(matches, "RTS", "rts")
                            .Equals("rts", StringComparison.InvariantCultureIgnoreCase);
            var handshake = CaptureGroupOrDefault(matches, "Handshake", Handshake.None);
            var endpoint  = new SerialDeviceEndpoint(portName, baud, parity, databits, stopbits, assertDtr, assertRts,
                                                     handshake);

            return(endpoint);
        }
 /// <summary>Initializes a new instance of the <see cref="SerialCommunicationChannel" /> class.</summary>
 /// <param name="endpoint">The device endpoint.</param>
 /// <param name="port">The port.</param>
 /// <exception cref="System.ArgumentException">Expected a SerialDeviceEndpoint</exception>
 public SerialCommunicationChannel(DeviceEndpoint endpoint, ISerialPort port = null)
 {
     if (!(endpoint is SerialDeviceEndpoint))
     {
         throw new ArgumentException("Expected a SerialDeviceEndpoint");
     }
     this.endpoint             = endpoint as SerialDeviceEndpoint;
     Port                      = port ?? CreateSerialPort(this.endpoint);
     observableReceiveSequence = Port.ToObservableCharacterSequence()
                                 .Trace("Serial Receive")
                                 .Publish();
 }
        private ISerialPort CreateSerialPort(SerialDeviceEndpoint endpoint)
        {
            Contract.Requires(endpoint != null);
            var port = new SerialPort
            {
                PortName  = endpoint.PortName,
                BaudRate  = endpoint.BaudRate,
                Parity    = endpoint.Parity,
                DataBits  = endpoint.DataBits,
                StopBits  = endpoint.StopBits,
                RtsEnable = endpoint.RtsEnable,
                DtrEnable = endpoint.DtrEnable,
                Encoding  = endpoint.Encoding,
                Handshake = endpoint.Handshake
            };

            return(port);
        }