/// <summary>
 /// Sends the specified command to the Paradox PRT3 module.
 /// </summary>
 /// <param name="command">The command.</param>
 public void SendCommand(string command)
 {
     if (this.IsConnected)
     {
         try
         {
             // Write the command to the serial port and wait 10ms
             this.serialPort.WriteLine(command);
             Thread.Sleep(10);
             // Log obfuscation for PIN code
             if (command.StartsWith("AA") || command.StartsWith("AD"))
             {
                 command = command.Substring(0, command.Length - 4) + "****";
             }
             // Raise the MessageSent event
             if (this.MessageSent != null)
             {
                 var message = new ParadoxMessageEventArgs()
                 {
                     Date = DateTime.Now, Message = command
                 };
                 Delegate[] receivers = this.MessageSent.GetInvocationList();
                 foreach (EventHandler <ParadoxMessageEventArgs> receiver in receivers)
                 {
                     receiver.BeginInvoke(this, message, null, null);
                 }
             }
         }
         catch (Exception ex)
         {
             if (this.InterfaceError != null)
             {
                 this.InterfaceError(this, new InterfaceErrorEventArgs()
                 {
                     Exception = ex
                 });
             }
             this.IsConnected = false;
         }
     }
     else
     {
         throw new InvalidOperationException("The interface is not connected !");
     }
 }
        /// <summary>
        /// Handles when message is received from the PRT3 module.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="ParadoxMessageEventArgs"/> instance containing the raw message.</param>
        private void OnMessageReceived(object sender, ParadoxMessageEventArgs e)
        {
            // Find the message handler
            var eventToRaise = this.GetType().GetEvents()
                               .Select(ev => new
            {
                Description = ev.GetCustomAttribute <DescriptionAttribute>(),
                EventInfo   = ev
            })
                               .Where(ev => ev.Description != null && e.Message.StartsWith(ev.Description.Description) && ev.EventInfo != null)
                               .Select(ev => new
            {
                EventName     = ev.EventInfo.Name,
                EventArgsType = ev.EventInfo.EventHandlerType.GenericTypeArguments.FirstOrDefault()
            })
                               .SingleOrDefault();

            // If the event exist
            if (eventToRaise != null && eventToRaise.EventArgsType != null)
            {
                try
                {
                    // Process the message
                    var eventArgs = (ParadoxBaseEventArgs)Activator.CreateInstance(eventToRaise.EventArgsType);
                    eventArgs.ProcessMessage(e.Message);
                    // Invoke handler(s)
                    var eventDelegate = (MulticastDelegate)this.GetType().GetField(eventToRaise.EventName, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this);
                    if (eventDelegate != null)
                    {
                        foreach (var handler in eventDelegate.GetInvocationList())
                        {
                            handler.Method.Invoke(handler.Target, new object[] { this, eventArgs });
                        }
                    }
                }
                catch { }
            }
        }
        /// <summary>
        /// Internal loop to read the serial port.
        /// </summary>
        private void ReadSerialPort()
        {
            while (this.IsConnected)
            {
                try
                {
                    // If there is data to read
                    if (this.serialPort.BytesToRead > 0)
                    {
                        // Read incoming datas to the string buffer
                        byte[] buffer = new byte[this.serialPort.BytesToRead];
                        this.serialPort.Read(buffer, 0, this.serialPort.BytesToRead);
                        this.strBuffer        += ASCIIEncoding.ASCII.GetString(buffer);
                        this.lastReceivedDatas = DateTime.Now;
                        int nlIndex = -1;
                        // Read each line from the string buffer
                        while ((nlIndex = this.strBuffer.IndexOf('\r')) > 0)
                        {
                            // If there are subscribers
                            if (this.MessageReceived != null)
                            {
                                // Extract the line and build the ParadoxMessageEventArgs
                                var message = new ParadoxMessageEventArgs()
                                {
                                    Date = DateTime.Now, Message = this.strBuffer.Substring(0, nlIndex)
                                };
                                // Raise MessageReceived event for each subscribers
                                Delegate[] receivers = this.MessageReceived.GetInvocationList();
                                foreach (EventHandler <ParadoxMessageEventArgs> receiver in receivers)
                                {
                                    receiver.BeginInvoke(this, message, null, null);
                                }
                            }
                            // Remove the message processed from the buffer
                            this.strBuffer = this.strBuffer.Substring(nlIndex + 1);
                        }
                    }

                    // Check anormal buffer content (= data without '\r' in the string buffer since 2000ms)
                    if (this.strBuffer.Length > 0 && this.strBuffer.IndexOf('\r') < 0 && DateTime.Now.Subtract(lastReceivedDatas).TotalMilliseconds >= BUFFER_TIMEOUT)
                    {
                        // Raise error
                        if (this.InterfaceError != null)
                        {
                            this.InterfaceError(this, new InterfaceErrorEventArgs()
                            {
                                Exception = new TimeoutException($"String buffer timeout. The buffer content was '{this.strBuffer}'.")
                            });
                        }
                        // Reset string buffer
                        this.strBuffer = string.Empty;
                    }

                    // Pause at each iteration
                    Thread.Sleep(10);
                }
                catch (Exception ex)
                {
                    if (this.InterfaceError != null)
                    {
                        this.InterfaceError(this, new InterfaceErrorEventArgs()
                        {
                            Exception = ex
                        });
                    }
                    this.IsConnected = false;
                }
            }
        }