C# 使用两个串行端口(本机RS485和USB RS232)的应用程序接收延迟响应

C# 使用两个串行端口(本机RS485和USB RS232)的应用程序接收延迟响应,c#,windows,serial-port,rs485,C#,Windows,Serial Port,Rs485,我正在使用一个遗留应用程序(MSVC19,C#在Windows 10上使用.NET 4.5.2),它充当一个无趣设备的前端。为了方便与设备的通信,建立了两个串行连接。MCU板的串行RS485(115200波特8N1)和连接第三方传感器平台的usb FTDI RS232至TTL(9600波特8N1)适配器 我试图诊断的具体问题是,在其他串行端口发生任何通信后不久,应用程序通过USB串行端口连接的传感器平台的响应时间延迟。通常,传感器平台的响应时间在10到100毫秒之间保持一致。在性能不佳的设置中,

我正在使用一个遗留应用程序(MSVC19,C#在Windows 10上使用.NET 4.5.2),它充当一个无趣设备的前端。为了方便与设备的通信,建立了两个串行连接。MCU板的串行RS485(115200波特8N1)和连接第三方传感器平台的usb FTDI RS232至TTL(9600波特8N1)适配器

我试图诊断的具体问题是,在其他串行端口发生任何通信后不久,应用程序通过USB串行端口连接的传感器平台的响应时间延迟。通常,传感器平台的响应时间在10到100毫秒之间保持一致。在性能不佳的设置中,响应延迟可能高达20秒

为了隔离问题,我将所有通信提取到一个最小的应用程序中。我还用基于arduino系统的诱饵替换了MCU和传感器平台,以确保MCU和传感器平台都不是原因

在采取这些预防措施后,我可以通过本机RS485端口连接设备的MCU诱饵来触发测试代码问题。我可以通过USB RS485收发器连接MCU诱饵来防止问题。下面是显示问题的日志摘录

良好的设置(USB RS485、USB RS232)

设置不当(本机车载RS485、USB RS232)

很明显,在性能不佳的设置中,每当向MCU平台发送消息时,传感器平台的下一个响应都会延迟几秒钟。我通过检查我的arduino诱饵的输出来验证,发送给传感器平台(“Q”)的请求消息没有延迟,并且立即发送响应。因此,延迟必须发生在PC或我的应用程序的接收路径上

我还尝试只从应用程序连接传感器平台,然后使用终端执行与MCU平台的手动通信。在这种情况下,不会发生延迟

由于这是我第一次连接基于RS485的串行端口,有什么需要注意的吗?我已经检查了计算机的BIOS中是否有任何其他设置,但除了协议(RS232/RS485)以及通常的地址和中断号之外,没有任何设置

Windows对串行端口的处理是否存在已知错误?由于USB转换器的普及,用谷歌搜索这个问题非常困难。我不相信我的代码中存在这个问题,因为它在性能良好的开发人员设置中工作得很好

测试应用程序的来源如下:;如果有人感兴趣,我也可以提供诱饵的来源。不幸的是,为了不超过文章长度,我不得不删除很多评论和日志输出。如果有必要,我可以找到一个不同的地方上传代码片段

更新1

我已经将最小样本裁剪为更小的样本,同时仍然表现出这种行为。我改变了PC BIOS设置,使PC现在在所有COM端口上使用RS232,并使用外部RS232RS485桥接器将MCU连接到COM1。然后,我将RS232TTL转换器连接到COM2,并将传感器连接到COM2。最终的结果是不再涉及USB转换器,只涉及普通的旧板载COM端口

我希望BIOS中接近“默认”的配置能很好地工作。但事实似乎并非如此。即使如此,延迟响应仍然存在

我还将所有连接的比特率更改为一致9600,以确保这不是比特率转换问题

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;

namespace MultiSerial
{
    public class MultiSerialTest
    {
        public string TAG { get { return "Test"; } }
        const int ciQueryInterval = 1000;
        LogCollector logger;

        SerialComm mcucomm;
        COMSettings mcuComSettings;
        SerialStreamDecoder mcuDecoder;

        SerialComm Sensorcomm;
        COMSettings SensorComSettings;
        SerialStreamDecoder SensorDecoder;

        ManualResetEvent evtSensorConnected;
        ManualResetEvent evtMCUConnected;

        public void run(string mcu_port, string sensor_port)
        {
            evtSensorConnected = new ManualResetEvent(false);
            evtMCUConnected = new ManualResetEvent(false);

            logger = new LogCollector();
            logger.InitLog("CommTest.log");

            mcuComSettings = new COMSettings(9600, Parity.None, 8, StopBits.One);
            mcuDecoder = new SerialStreamDecoder(
                MCUCommMessage.preamble,
                MCUCommMessage.delimiter,
                MCUCommMessage.terminator
            );
            mcucomm = new SerialComm(
                mcuComSettings,
                mcuDecoder,
                VerifyMCU,
                false,
                logger,
                mcu_port,
                "MCU"
            );

            SensorComSettings = new COMSettings(9600, Parity.None, 8, StopBits.One);
            SensorDecoder = new SerialStreamDecoder(
                SensorCommMessage.preamble,
                SensorCommMessage.delimiter,
                SensorCommMessage.terminator
            );
            Sensorcomm = new SerialComm(
                SensorComSettings,
                SensorDecoder,
                VerifySensor,
                false,
                logger,
                sensor_port,
                "SENSOR"
            );

            Sensorcomm.ConnectDevice();
            mcucomm.ConnectDevice();
            int counter = 0;
            while (true)
            {                
                if (counter == 5)
                {
                    VersionRequest verReq = new VersionRequest(mcucomm);
                    verReq.Send();
                    verReq.AwaitCompletion();
                    counter = 0;
                }
                else
                {

                    FetchRequest fetchReq = new FetchRequest(Sensorcomm);
                    fetchReq.Send();
                    fetchReq.AwaitCompletion();
                }

                counter++;
                System.Threading.Thread.Sleep(ciQueryInterval);

            }

        }

        void VerifySensor(SerialComm comm, out bool bSuccess)
        {
            comm.Log(TAG, "Servicing verification request for SensorComm");
            FetchRequest fetchMessage = new FetchRequest(comm);
            fetchMessage.Send();
            fetchMessage.AwaitCompletion();

            if (fetchMessage.result == 0)
            {
                comm.Log(TAG, "Verification for SensorComm successful");
                bSuccess = true;
            }
            else
            {
                comm.Log(TAG, "Verification for SensorComm failed");
                bSuccess = false;
            }
            return;
        }

        void VerifyMCU(SerialComm comm, out bool bSuccess)
        {
            comm.Log(TAG, "Servicing verification request for mcuComm");
            VersionRequest versionMessage = new VersionRequest(comm);
            versionMessage.Send();
            versionMessage.AwaitCompletion();
            if (versionMessage.result == 0)
            {
                comm.Log(TAG, "Verification for mcuComm successful");
                bSuccess = true;
            }
            else
            {
                comm.Log(TAG, "Verification for mcuComm failed");
                bSuccess = false;
            }
            return;
        }
    }

    class Program
    {

        static void Main(string[] args)
        {
            MultiSerialTest test = new MultiSerialTest();
            test.run(args[0], args[1]);
        }
    }

    public class FetchRequest : SensorCommMessage
    {
        new string TAG { get { return "SensorComm:FetchRequest"; } }

        // Request = "Q\r\n"
        // Response = [<identifier><whitespace><value>]^n
        public FetchRequest(SerialComm comm)
            : base(comm,fetch_instruction)
        {
        }

        protected override bool ParseReplyContents(List<string> tokens)
        {
            // Dummy validation
            if (tokens.Count < 2 ||
                 tokens.Count % 2 != 0) { return false; }
            return true;
        }
    }

    public abstract class SensorCommMessage : BaseMessage
    {
        new string TAG { get { return "SensorCommMessage "; } }

        public const char preamble_ch = 'Z';
        public const char delimiter_ch = ' ';

        public static readonly string preamble = preamble_ch.ToString();
        public static readonly char delimiter = delimiter_ch;
        public static readonly string terminator = "\r\n";

        public const string fetch_instruction = "Q";

        private string instructionToken;
        private List<string> userTokens;

        public SensorCommMessage(SerialComm _comm, string _instructionToken) 
            : base(_comm)
        {
            instructionToken = _instructionToken;
        }


        public override string ToWireString()
        {
            string message =
                instructionToken;

            if (userTokens != null)
            {
                foreach (string token in userTokens)
                {
                    message += token;
                    message += delimiter;
                }
            }            
            return message;
        }

        public override bool ParseReplyFrame(List<string> replyTokens)
        {
            bool bHandled = false;            
            bHandled = ParseReplyContents(replyTokens);
            if (bHandled) { MessageCompleted(); }            
            return bHandled;
        }

        protected abstract bool ParseReplyContents(List<string> replyContentTokens);
    }

    public class VersionRequest : MCUCommMessage
    {
        public string DeviceId { get { return strDeviceId; } }
        string strDeviceId;
        public string ProtocolVersion { get { return strProtocollVersion; } }
        string strProtocollVersion;
        public string HardwareVersion { get { return strHardwareVersion; } }
        string strHardwareVersion;


        // Request = $;version;*
        // Response = $;version;pass;DeviceId;ProtocolVersion;HardwareVersion;*
        public VersionRequest(SerialComm comm)
            : base(comm,version_instruction)
        {
        }

        protected override bool ParseReplyContents(List<string> tokens)
        {
            // Expect precisely 3 remaining tokens
            if (tokens.Count != 3)
            {
                return false;
            }
            strDeviceId = tokens[0];
            strProtocollVersion = tokens[1];
            strHardwareVersion = tokens[2];

            return true;
        }
    }

    public class ParseHelper
    {
        public static bool ParseFloatToken(string strToken, float fMin, float fMax, out float fValue)
        {
            fValue = float.NaN;
            if (float.TryParse(strToken, out fValue) &&
                fValue >= fMin &&
                fValue <= fMax)
            {
                return true;
            }
            return false;
        }

        public static bool ParseIntToken(string strToken, int iMin, int iMax, out int iValue)
        {
            iValue = 0;
            if (int.TryParse(strToken, out iValue) &&
                iValue >= iMin &&
                iValue <= iMax)
            {
                return true;
            }
            return false;
        }
    }
    public class SerialStreamDecoder
    {
        public string TAG { get { return "SerialStreamDecoder"; } }

        public string preamble;
        public char delimiter;
        public string terminator;

        public SerialStreamDecoder(string _preamble, char _delimiter, string _terminator)
        {
            preamble = _preamble;
            delimiter = _delimiter;
            terminator = _terminator;
        }

        public bool DecodeMessageFromStream(SerialComm comm, out string message, out List<string> tokens)
        {
            string strRx = "";
            message = "";
            tokens = null;
            // Read until terminator     
            strRx = comm.usart.ReadTo(terminator);
            // Re-append terminator because it does not get included in the read
            strRx += terminator;

            // If a preamble is set, scan input sequence for preamble
            int preampleIndex = 0;
            if (    preamble != "" &&
                (   preampleIndex = strRx.IndexOf(preamble)) > 0)
            { 
                strRx = strRx.Substring(preampleIndex, strRx.Length - preampleIndex);
            }
            message = strRx;
            tokens = strRx.Split(delimiter).ToList<string>();

            return true;
        }

    }

    public abstract class MCUCommMessage : BaseMessage
    {
        public string TAG { get { return "MCUCommMessage "; } }

        public const char preamble_ch = '$';
        public const char delimiter_ch = ';';
        public const char terminator_ch = '*';

        public static readonly string preamble = preamble_ch.ToString();
        public static readonly char delimiter = delimiter_ch;
        public static readonly string terminator = terminator_ch.ToString();

        public const string version_instruction = "version";

        public const string okay_result = "pass";
        public const string fail_result = "fail";

        private string instructionToken;
        private List<string> userTokens;

        public MCUCommMessage(SerialComm _comm, string _instructionToken ) 
            : base(_comm)
        {
            instructionToken = _instructionToken;
        }

        public override string ToWireString()
        {
            string message = preamble + delimiter + instructionToken + delimiter;
            if (userTokens != null)
            {
                foreach (string token in userTokens)
                {
                    message += token;
                    message += delimiter;
                }
            }
            message += terminator;
            return message;
        }

        public override bool ParseReplyFrame(List<string> replyTokens)
        {
            bool bHandled = false;            
            string lastToken = replyTokens.ElementAt(replyTokens.Count - 1);
            if (replyTokens.Count < 4 ||
                replyTokens[0] != preamble ||
                replyTokens[1] != instructionToken ||
                lastToken != terminator)
            {
                return false;
            }

            if (!bHandled &&
                replyTokens[2] == fail_result &&
                replyTokens.Count == 6
            )
            {
                ParseHelper.ParseIntToken(replyTokens[4], int.MinValue, int.MaxValue, out result);
                bHandled = true;
            }

            if (!bHandled &&
                replyTokens[2] == okay_result)
            {
                List<string> payloadTokens = replyTokens.GetRange(3, replyTokens.Count - 4);
                bHandled = ParseReplyContents(payloadTokens);
            }
            if (bHandled)
            {
                MessageCompleted();
            }

            return bHandled;
        }

        protected abstract bool ParseReplyContents(List<string> replyContentTokens);

        public void AddTokens(IEnumerable<string> tokens)
        {
            if (userTokens == null) userTokens = new List<string>();
            foreach (string token in tokens)
            {
                userTokens.Add(token);
            }
        }
    }

    public abstract class BaseMessage
    {
        public string TAG
        {
            get { return "BaseMessage"; }
        }

        public ManualResetEvent evtMessageRelease;

        public delegate void MessageCompletedEventHandler();
        public event MessageCompletedEventHandler EvtMessageCompleted;

        protected SerialComm comm;

        DateTime timestampRequest;
        DateTime timestampReply;

        public int result;

        public BaseMessage(
            SerialComm _comm
        )
        {
            comm = _comm;
            evtMessageRelease = new ManualResetEvent(false);
        }

        public int Send()
        {
            timestampRequest = DateTime.Now;
            comm.TransmitSync(this);
            return result;
        }

        public void AwaitCompletion()
        {
            evtMessageRelease.WaitOne();
        }


        public abstract string ToWireString();

        protected void MessageCompleted()
        {
            timestampReply = DateTime.Now;
            TimeSpan duration = timestampReply - timestampRequest;
            comm.Log(TAG, "Message completed: (" + string.Format("{0,8:F1} ms", duration.TotalMilliseconds) + ") [" + this.ToWireString() + "]");

            if (EvtMessageCompleted != null)
                EvtMessageCompleted();

            CleanupMessage();
        }

        private void CleanupMessage()
        {
            evtMessageRelease.Set();
        }

        public abstract bool ParseReplyFrame(List<string> tokens);

    }
    public class LogCollector
    {
        StreamWriter streamwriter;

        public void InitLog(string file)
        {
            Directory.CreateDirectory(".\\logs");
            FileStream filestream = new FileStream(".\\logs\\" + file, FileMode.Create);
            streamwriter = new StreamWriter(filestream);
            streamwriter.AutoFlush = false;

        }

        public void Log(string tag, string message)
        {
            string logMessage = "[" + DateTime.Now.ToString() + "] " + tag + ": " + message;
            streamwriter.WriteLine(logMessage);
            streamwriter.Flush();
            Console.WriteLine(logMessage);
        }
    }

    public class COMSettings
    {
        public int baudrate;
        public Parity parity;
        public int databits;
        public StopBits stopbits;

        public COMSettings(int _baudrate, Parity _parity, int _databits, StopBits _stopbits)
        {
            baudrate = _baudrate;
            parity = _parity;
            databits = _databits;
            stopbits = _stopbits;
        }

    }

    public class SerialComm
    {

        public string TAG { get { return strTag; } }
        string strTag;

        LogCollector logger;
        public void Log(string tag, string message)
        {
            if (logger != null)
                logger.Log(tag, message);
        }

        COMSettings comSettings;
        public SerialPort usart;

        private string preferredPort;
        private BaseMessage currentMessage = null;
        private SerialStreamDecoder decoder;

        public delegate void VerifyDeviceResponseDelegate(SerialComm comm, out bool bSuccess);
        private event VerifyDeviceResponseDelegate DeviceResponseVerification;

        public delegate void MessageReceivedDelegate();
        public event MessageReceivedDelegate EvtMessageReceived;

        Thread connectThread;

        public SerialComm(
            COMSettings _comSettings,
            SerialStreamDecoder _decoder,
            VerifyDeviceResponseDelegate d,
            bool _bReconnectEnabled,
            LogCollector _logger,
            string _preferredPort,
            string tag = "SerialComm"

        )
        {
            comSettings = _comSettings;
            decoder = _decoder;
            DeviceResponseVerification = d;
            logger = _logger;
            preferredPort = _preferredPort;
            strTag = tag;               
        }


        public bool ConnectDevice()
        {
            bool bDeviceVerified = false;
            try
            {
                usart =
                new SerialPort(
                    preferredPort,
                    comSettings.baudrate,
                    comSettings.parity,
                    comSettings.databits,
                    comSettings.stopbits
                );
                usart.Open();
                usart.DataReceived += new SerialDataReceivedEventHandler(OnDataReceived);


                DeviceResponseVerification(this, out bDeviceVerified);
            }
            catch (Exception e)
            {
                bDeviceVerified = false;
            }
            if (!bDeviceVerified)
            {
                if (usart != null)
                {
                    usart.Close();
                    usart.DataReceived -= OnDataReceived;
                    usart = null;
                }

            }

            return usart != null;
        }

        /* OnDataReceived
            * Handles received serial communication
            */
        private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                // Read until terminator
                if (usart == null) return;
                List<string> tokens = null;
                string message = "";
                if (!decoder.DecodeMessageFromStream(this, out message, out tokens))
                {
                    return;
                }

                Log(TAG, "RX: [" + message.Trim() + "]");
                bool bHandled = false;
                // Check if we received a response to the current message
                if (!bHandled && this.currentMessage != null)
                {
                    bHandled = currentMessage.ParseReplyFrame(tokens);
                }

                if (bHandled)
                {
                    if (EvtMessageReceived != null)
                        EvtMessageReceived();
                }
            }
            catch (Exception ex)
            {
            }
        }


        public void TransmitSync(BaseMessage message)
        {            
            currentMessage = message;                        
            WriteCurrentMessage();
        }


        public void WriteCurrentMessage()
        {
            usart.Write(currentMessage.ToWireString() + "\r\n");
            Log(TAG, "TX: " + currentMessage.ToWireString());
        }
    }

}
使用系统;
使用System.Collections.Generic;
使用System.IO;
使用System.IO.Ports;
使用System.Linq;
使用系统文本;
使用系统线程;
使用System.Threading.Tasks;
使用系统计时器;
名称空间多序列
{
公共类多列测试
{
公共字符串标记{get{return“Test”;}
常数int ciQueryInterval=1000;
日志采集器记录器;
串行通信mcucomm;
通信设置mcuComSettings;
串行流译码器;
串行通信传感器通信;
通讯设置传感器通讯设置;
串行流译码器;
手动复位事件EVT传感器已连接;
手动复位事件EVTMC已连接;
公共无效运行(字符串mcu\u端口、字符串传感器\u端口)
{
EVT传感器连接=新手动重置事件(错误);
evtMCUConnected=新手动重置事件(假);
logger=新的LogCollector();
logger.InitLog(“CommTest.log”);
mcuComSettings=新的COMSettings(9600,奇偶校验。无,8,停止位。1);
mcuDecoder=新的串行流解码器(
mcucommmmessage.preamble,
MCUCommMessage.delimiter,
MCUCommMessage.terminator
);
mcucomm=新串行通信(
mcuComSettings,
mcuDecoder,
验证MCU,
假,,
记录器,
mcu_端口,
“微控制器”
);
SensorComSettings=新的COMSettings(9600,奇偶校验。无,8,停止位。1);
SensorDecoder=新的SerialStreamDecoder(
SensorCommMessage.序言,
SensorCommMessage.delimiter,
SensorCommMessage.terminator
);
Sensorcomm=新串行通信(
传感器通信设置,
传感器解码器,
验证传感器,
假,,
记录器,
传感器(u)端口,,
“传感器”
);
Sensorcomm.ConnectDevice();
mcucomm.ConnectDevice();
int计数器=0;
while(true)
{                
如果(计数器==5)
[27.11.2019 11:19:56] SENSOR: TX: Q
[27.11.2019 11:19:56] SENSOR: RX: [Z 00088 z 00084]
[27.11.2019 11:19:56] BaseMessage: Message completed: (    15,6 ms) [Q]
[27.11.2019 11:19:57] MCU: TX: $;version;*
[27.11.2019 11:19:57] MCU: RX: [$;version;pass;MCU;Proto4;Hw2;*]
[27.11.2019 11:19:57] BaseMessage: Message completed: (     0,0 ms) [$;version;*]
[27.11.2019 11:19:58] SENSOR: TX: Q
[27.11.2019 11:20:06] SENSOR: RX: [Z 00088 z 00095]
[27.11.2019 11:20:06] BaseMessage: Message completed: (  7997,9 ms) [Q]
[27.11.2019 11:20:07] SENSOR: TX: Q
[27.11.2019 11:20:07] SENSOR: RX: [Z 00088 z 00090]
[27.11.2019 11:20:07] BaseMessage: Message completed: (    31,2 ms) [Q]
[27.11.2019 11:20:08] SENSOR: TX: Q
[27.11.2019 11:20:08] SENSOR: RX: [Z 00089 z 00097]
[27.11.2019 11:20:08] BaseMessage: Message completed: (    15,6 ms) [Q]
[27.11.2019 11:20:09] SENSOR: TX: Q
[27.11.2019 11:20:09] SENSOR: RX: [Z 00089 z 00089]
[27.11.2019 11:20:09] BaseMessage: Message completed: (    31,2 ms) [Q]
[27.11.2019 11:20:10] MCU: TX: $;temp;*
[27.11.2019 11:20:10] MCU: RX: [$;temp;pass;55;0;0;0;0;0;*]
[27.11.2019 11:20:10] BaseMessage: Message completed: (     0,0 ms) [$;temp;*]
[27.11.2019 11:20:11] SENSOR: TX: Q
[27.11.2019 11:20:16] SENSOR: RX: [Z 00089 z 00091]
[27.11.2019 11:20:16] BaseMessage: Message completed: (  4967,8 ms) [Q]
[27.11.2019 11:20:17] SENSOR: TX: Q
[27.11.2019 11:20:17] SENSOR: RX: [Z 00089 z 00092]
[27.11.2019 11:20:17] BaseMessage: Message completed: (    15,6 ms) [Q]
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;

namespace MultiSerial
{
    public class MultiSerialTest
    {
        public string TAG { get { return "Test"; } }
        const int ciQueryInterval = 1000;
        LogCollector logger;

        SerialComm mcucomm;
        COMSettings mcuComSettings;
        SerialStreamDecoder mcuDecoder;

        SerialComm Sensorcomm;
        COMSettings SensorComSettings;
        SerialStreamDecoder SensorDecoder;

        ManualResetEvent evtSensorConnected;
        ManualResetEvent evtMCUConnected;

        public void run(string mcu_port, string sensor_port)
        {
            evtSensorConnected = new ManualResetEvent(false);
            evtMCUConnected = new ManualResetEvent(false);

            logger = new LogCollector();
            logger.InitLog("CommTest.log");

            mcuComSettings = new COMSettings(9600, Parity.None, 8, StopBits.One);
            mcuDecoder = new SerialStreamDecoder(
                MCUCommMessage.preamble,
                MCUCommMessage.delimiter,
                MCUCommMessage.terminator
            );
            mcucomm = new SerialComm(
                mcuComSettings,
                mcuDecoder,
                VerifyMCU,
                false,
                logger,
                mcu_port,
                "MCU"
            );

            SensorComSettings = new COMSettings(9600, Parity.None, 8, StopBits.One);
            SensorDecoder = new SerialStreamDecoder(
                SensorCommMessage.preamble,
                SensorCommMessage.delimiter,
                SensorCommMessage.terminator
            );
            Sensorcomm = new SerialComm(
                SensorComSettings,
                SensorDecoder,
                VerifySensor,
                false,
                logger,
                sensor_port,
                "SENSOR"
            );

            Sensorcomm.ConnectDevice();
            mcucomm.ConnectDevice();
            int counter = 0;
            while (true)
            {                
                if (counter == 5)
                {
                    VersionRequest verReq = new VersionRequest(mcucomm);
                    verReq.Send();
                    verReq.AwaitCompletion();
                    counter = 0;
                }
                else
                {

                    FetchRequest fetchReq = new FetchRequest(Sensorcomm);
                    fetchReq.Send();
                    fetchReq.AwaitCompletion();
                }

                counter++;
                System.Threading.Thread.Sleep(ciQueryInterval);

            }

        }

        void VerifySensor(SerialComm comm, out bool bSuccess)
        {
            comm.Log(TAG, "Servicing verification request for SensorComm");
            FetchRequest fetchMessage = new FetchRequest(comm);
            fetchMessage.Send();
            fetchMessage.AwaitCompletion();

            if (fetchMessage.result == 0)
            {
                comm.Log(TAG, "Verification for SensorComm successful");
                bSuccess = true;
            }
            else
            {
                comm.Log(TAG, "Verification for SensorComm failed");
                bSuccess = false;
            }
            return;
        }

        void VerifyMCU(SerialComm comm, out bool bSuccess)
        {
            comm.Log(TAG, "Servicing verification request for mcuComm");
            VersionRequest versionMessage = new VersionRequest(comm);
            versionMessage.Send();
            versionMessage.AwaitCompletion();
            if (versionMessage.result == 0)
            {
                comm.Log(TAG, "Verification for mcuComm successful");
                bSuccess = true;
            }
            else
            {
                comm.Log(TAG, "Verification for mcuComm failed");
                bSuccess = false;
            }
            return;
        }
    }

    class Program
    {

        static void Main(string[] args)
        {
            MultiSerialTest test = new MultiSerialTest();
            test.run(args[0], args[1]);
        }
    }

    public class FetchRequest : SensorCommMessage
    {
        new string TAG { get { return "SensorComm:FetchRequest"; } }

        // Request = "Q\r\n"
        // Response = [<identifier><whitespace><value>]^n
        public FetchRequest(SerialComm comm)
            : base(comm,fetch_instruction)
        {
        }

        protected override bool ParseReplyContents(List<string> tokens)
        {
            // Dummy validation
            if (tokens.Count < 2 ||
                 tokens.Count % 2 != 0) { return false; }
            return true;
        }
    }

    public abstract class SensorCommMessage : BaseMessage
    {
        new string TAG { get { return "SensorCommMessage "; } }

        public const char preamble_ch = 'Z';
        public const char delimiter_ch = ' ';

        public static readonly string preamble = preamble_ch.ToString();
        public static readonly char delimiter = delimiter_ch;
        public static readonly string terminator = "\r\n";

        public const string fetch_instruction = "Q";

        private string instructionToken;
        private List<string> userTokens;

        public SensorCommMessage(SerialComm _comm, string _instructionToken) 
            : base(_comm)
        {
            instructionToken = _instructionToken;
        }


        public override string ToWireString()
        {
            string message =
                instructionToken;

            if (userTokens != null)
            {
                foreach (string token in userTokens)
                {
                    message += token;
                    message += delimiter;
                }
            }            
            return message;
        }

        public override bool ParseReplyFrame(List<string> replyTokens)
        {
            bool bHandled = false;            
            bHandled = ParseReplyContents(replyTokens);
            if (bHandled) { MessageCompleted(); }            
            return bHandled;
        }

        protected abstract bool ParseReplyContents(List<string> replyContentTokens);
    }

    public class VersionRequest : MCUCommMessage
    {
        public string DeviceId { get { return strDeviceId; } }
        string strDeviceId;
        public string ProtocolVersion { get { return strProtocollVersion; } }
        string strProtocollVersion;
        public string HardwareVersion { get { return strHardwareVersion; } }
        string strHardwareVersion;


        // Request = $;version;*
        // Response = $;version;pass;DeviceId;ProtocolVersion;HardwareVersion;*
        public VersionRequest(SerialComm comm)
            : base(comm,version_instruction)
        {
        }

        protected override bool ParseReplyContents(List<string> tokens)
        {
            // Expect precisely 3 remaining tokens
            if (tokens.Count != 3)
            {
                return false;
            }
            strDeviceId = tokens[0];
            strProtocollVersion = tokens[1];
            strHardwareVersion = tokens[2];

            return true;
        }
    }

    public class ParseHelper
    {
        public static bool ParseFloatToken(string strToken, float fMin, float fMax, out float fValue)
        {
            fValue = float.NaN;
            if (float.TryParse(strToken, out fValue) &&
                fValue >= fMin &&
                fValue <= fMax)
            {
                return true;
            }
            return false;
        }

        public static bool ParseIntToken(string strToken, int iMin, int iMax, out int iValue)
        {
            iValue = 0;
            if (int.TryParse(strToken, out iValue) &&
                iValue >= iMin &&
                iValue <= iMax)
            {
                return true;
            }
            return false;
        }
    }
    public class SerialStreamDecoder
    {
        public string TAG { get { return "SerialStreamDecoder"; } }

        public string preamble;
        public char delimiter;
        public string terminator;

        public SerialStreamDecoder(string _preamble, char _delimiter, string _terminator)
        {
            preamble = _preamble;
            delimiter = _delimiter;
            terminator = _terminator;
        }

        public bool DecodeMessageFromStream(SerialComm comm, out string message, out List<string> tokens)
        {
            string strRx = "";
            message = "";
            tokens = null;
            // Read until terminator     
            strRx = comm.usart.ReadTo(terminator);
            // Re-append terminator because it does not get included in the read
            strRx += terminator;

            // If a preamble is set, scan input sequence for preamble
            int preampleIndex = 0;
            if (    preamble != "" &&
                (   preampleIndex = strRx.IndexOf(preamble)) > 0)
            { 
                strRx = strRx.Substring(preampleIndex, strRx.Length - preampleIndex);
            }
            message = strRx;
            tokens = strRx.Split(delimiter).ToList<string>();

            return true;
        }

    }

    public abstract class MCUCommMessage : BaseMessage
    {
        public string TAG { get { return "MCUCommMessage "; } }

        public const char preamble_ch = '$';
        public const char delimiter_ch = ';';
        public const char terminator_ch = '*';

        public static readonly string preamble = preamble_ch.ToString();
        public static readonly char delimiter = delimiter_ch;
        public static readonly string terminator = terminator_ch.ToString();

        public const string version_instruction = "version";

        public const string okay_result = "pass";
        public const string fail_result = "fail";

        private string instructionToken;
        private List<string> userTokens;

        public MCUCommMessage(SerialComm _comm, string _instructionToken ) 
            : base(_comm)
        {
            instructionToken = _instructionToken;
        }

        public override string ToWireString()
        {
            string message = preamble + delimiter + instructionToken + delimiter;
            if (userTokens != null)
            {
                foreach (string token in userTokens)
                {
                    message += token;
                    message += delimiter;
                }
            }
            message += terminator;
            return message;
        }

        public override bool ParseReplyFrame(List<string> replyTokens)
        {
            bool bHandled = false;            
            string lastToken = replyTokens.ElementAt(replyTokens.Count - 1);
            if (replyTokens.Count < 4 ||
                replyTokens[0] != preamble ||
                replyTokens[1] != instructionToken ||
                lastToken != terminator)
            {
                return false;
            }

            if (!bHandled &&
                replyTokens[2] == fail_result &&
                replyTokens.Count == 6
            )
            {
                ParseHelper.ParseIntToken(replyTokens[4], int.MinValue, int.MaxValue, out result);
                bHandled = true;
            }

            if (!bHandled &&
                replyTokens[2] == okay_result)
            {
                List<string> payloadTokens = replyTokens.GetRange(3, replyTokens.Count - 4);
                bHandled = ParseReplyContents(payloadTokens);
            }
            if (bHandled)
            {
                MessageCompleted();
            }

            return bHandled;
        }

        protected abstract bool ParseReplyContents(List<string> replyContentTokens);

        public void AddTokens(IEnumerable<string> tokens)
        {
            if (userTokens == null) userTokens = new List<string>();
            foreach (string token in tokens)
            {
                userTokens.Add(token);
            }
        }
    }

    public abstract class BaseMessage
    {
        public string TAG
        {
            get { return "BaseMessage"; }
        }

        public ManualResetEvent evtMessageRelease;

        public delegate void MessageCompletedEventHandler();
        public event MessageCompletedEventHandler EvtMessageCompleted;

        protected SerialComm comm;

        DateTime timestampRequest;
        DateTime timestampReply;

        public int result;

        public BaseMessage(
            SerialComm _comm
        )
        {
            comm = _comm;
            evtMessageRelease = new ManualResetEvent(false);
        }

        public int Send()
        {
            timestampRequest = DateTime.Now;
            comm.TransmitSync(this);
            return result;
        }

        public void AwaitCompletion()
        {
            evtMessageRelease.WaitOne();
        }


        public abstract string ToWireString();

        protected void MessageCompleted()
        {
            timestampReply = DateTime.Now;
            TimeSpan duration = timestampReply - timestampRequest;
            comm.Log(TAG, "Message completed: (" + string.Format("{0,8:F1} ms", duration.TotalMilliseconds) + ") [" + this.ToWireString() + "]");

            if (EvtMessageCompleted != null)
                EvtMessageCompleted();

            CleanupMessage();
        }

        private void CleanupMessage()
        {
            evtMessageRelease.Set();
        }

        public abstract bool ParseReplyFrame(List<string> tokens);

    }
    public class LogCollector
    {
        StreamWriter streamwriter;

        public void InitLog(string file)
        {
            Directory.CreateDirectory(".\\logs");
            FileStream filestream = new FileStream(".\\logs\\" + file, FileMode.Create);
            streamwriter = new StreamWriter(filestream);
            streamwriter.AutoFlush = false;

        }

        public void Log(string tag, string message)
        {
            string logMessage = "[" + DateTime.Now.ToString() + "] " + tag + ": " + message;
            streamwriter.WriteLine(logMessage);
            streamwriter.Flush();
            Console.WriteLine(logMessage);
        }
    }

    public class COMSettings
    {
        public int baudrate;
        public Parity parity;
        public int databits;
        public StopBits stopbits;

        public COMSettings(int _baudrate, Parity _parity, int _databits, StopBits _stopbits)
        {
            baudrate = _baudrate;
            parity = _parity;
            databits = _databits;
            stopbits = _stopbits;
        }

    }

    public class SerialComm
    {

        public string TAG { get { return strTag; } }
        string strTag;

        LogCollector logger;
        public void Log(string tag, string message)
        {
            if (logger != null)
                logger.Log(tag, message);
        }

        COMSettings comSettings;
        public SerialPort usart;

        private string preferredPort;
        private BaseMessage currentMessage = null;
        private SerialStreamDecoder decoder;

        public delegate void VerifyDeviceResponseDelegate(SerialComm comm, out bool bSuccess);
        private event VerifyDeviceResponseDelegate DeviceResponseVerification;

        public delegate void MessageReceivedDelegate();
        public event MessageReceivedDelegate EvtMessageReceived;

        Thread connectThread;

        public SerialComm(
            COMSettings _comSettings,
            SerialStreamDecoder _decoder,
            VerifyDeviceResponseDelegate d,
            bool _bReconnectEnabled,
            LogCollector _logger,
            string _preferredPort,
            string tag = "SerialComm"

        )
        {
            comSettings = _comSettings;
            decoder = _decoder;
            DeviceResponseVerification = d;
            logger = _logger;
            preferredPort = _preferredPort;
            strTag = tag;               
        }


        public bool ConnectDevice()
        {
            bool bDeviceVerified = false;
            try
            {
                usart =
                new SerialPort(
                    preferredPort,
                    comSettings.baudrate,
                    comSettings.parity,
                    comSettings.databits,
                    comSettings.stopbits
                );
                usart.Open();
                usart.DataReceived += new SerialDataReceivedEventHandler(OnDataReceived);


                DeviceResponseVerification(this, out bDeviceVerified);
            }
            catch (Exception e)
            {
                bDeviceVerified = false;
            }
            if (!bDeviceVerified)
            {
                if (usart != null)
                {
                    usart.Close();
                    usart.DataReceived -= OnDataReceived;
                    usart = null;
                }

            }

            return usart != null;
        }

        /* OnDataReceived
            * Handles received serial communication
            */
        private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                // Read until terminator
                if (usart == null) return;
                List<string> tokens = null;
                string message = "";
                if (!decoder.DecodeMessageFromStream(this, out message, out tokens))
                {
                    return;
                }

                Log(TAG, "RX: [" + message.Trim() + "]");
                bool bHandled = false;
                // Check if we received a response to the current message
                if (!bHandled && this.currentMessage != null)
                {
                    bHandled = currentMessage.ParseReplyFrame(tokens);
                }

                if (bHandled)
                {
                    if (EvtMessageReceived != null)
                        EvtMessageReceived();
                }
            }
            catch (Exception ex)
            {
            }
        }


        public void TransmitSync(BaseMessage message)
        {            
            currentMessage = message;                        
            WriteCurrentMessage();
        }


        public void WriteCurrentMessage()
        {
            usart.Write(currentMessage.ToWireString() + "\r\n");
            Log(TAG, "TX: " + currentMessage.ToWireString());
        }
    }

}