首页 > 解决方案 > RS232 串行端口嗅探器字节乱序

问题描述

这是一个简单的 RS232 串行端口嗅探器,可以逐字节记录并指示方向。有A和B两端,数据包结构为[STX] [DATA] [ETX] [LRC(check-sum)],如果[LRC(check-sum)]正确,则对方回复[ACK]。问题是概率约为 50/50 的字节出现故障。

输出正常

字节顺序正确 [STX] [DATA] [ETX] [LRC],然后是来自另一侧的 [ACK]

<2019-12-03 14:49:23.7629776>;A;STX
<2019-12-03 14:49:23.7629776>;A;00
<2019-12-03 14:49:23.7629776>;A;88
<2019-12-03 14:49:23.7629776>;A;11
<2019-12-03 14:49:23.7629776>;A;3D
<2019-12-03 14:49:23.7629776>;A;5D
<2019-12-03 14:49:23.7629776>;A;AF
<2019-12-03 14:49:23.7629776>;A;79
<2019-12-03 14:49:23.7629776>;A;D3
<2019-12-03 14:49:23.7629776>;A;5F
<2019-12-03 14:49:23.7785776>;A;EB
<2019-12-03 14:49:23.7785776>;A;0E
<2019-12-03 14:49:23.7785776>;A;0A
<2019-12-03 14:49:23.7785776>;A;4C
<2019-12-03 14:49:23.7785776>;A;54
<2019-12-03 14:49:23.7785776>;A;50
<2019-12-03 14:49:23.7785776>;A;43
<2019-12-03 14:49:23.7785776>;A;ETX2
<2019-12-03 14:49:23.7785776>;A;44

<2019-12-03 14:49:23.7785776>;B;ACK2

输出不正常

字节乱序,[STX] [DATA] [ETX],然后是来自另一端的 [ACK],之后只有 [LRC]。这是不可能的,因为仅当 [LRC] 正确时才返回 [ACK]。否则,返回将是 [NAK]。那里的记录器一定有问题。

<2019-12-03 14:49:23.8877778>;A;STX
<2019-12-03 14:49:23.8877778>;A;50
<2019-12-03 14:49:23.8877778>;A;4E
<2019-12-03 14:49:23.8877778>;A;2D
<2019-12-03 14:49:23.8877778>;A;53
<2019-12-03 14:49:23.8877778>;A;30
<2019-12-03 14:49:23.8877778>;A;37
<2019-12-03 14:49:23.8877778>;A;50
<2019-12-03 14:49:23.8877778>;A;8E
<2019-12-03 14:49:23.8877778>;A;22
<2019-12-03 14:49:23.8877778>;A;59
<2019-12-03 14:49:23.8877778>;A;DLE
<2019-12-03 14:49:23.8877778>;A;33
<2019-12-03 14:49:23.8877778>;A;19
<2019-12-03 14:49:23.8877778>;A;A0
<2019-12-03 14:49:23.8877778>;A;19
<2019-12-03 14:49:23.8877778>;A;12
<2019-12-03 14:49:23.8877778>;A;DLE
<2019-12-03 14:49:23.9033779>;A;33
<2019-12-03 14:49:23.9033779>;A;ETX1

<2019-12-03 14:49:23.9033779>;B;ACK1

<2019-12-03 14:49:23.9033779>;A;72

代码

我们打开 2 个 COM 端口并开始记录线程:

        OpenComPortA();
        OpenComPortB();

        Task.Factory.StartNew(AddToFile);

COM口开放和worker启动:

        private void OpenComPortA()
        {
            _sideAManager = new SerialWorker();    
            _sideAManager.OnException += OnExceptionSideA;   
            new Thread(() => _sideAManager.Start(_sideASettings)).Start();
        }
        public void Start(ComPortSettings settings)
        {
            _side = settings.SideName;
            EventHandler<Exception> onException = OnException;
            try
            {
                CreateCom(settings);
                OpenCom();
            }
            catch (Exception ex)
            {
                onException?.Invoke(this, ex);
            }

            Task.Factory.StartNew(ReadComPort);
        }

等待字节读取,然后将读取参数添加到并发队列的主要过程:

        private void ReadComPort()
        {
            while (ComPort.IsOpen)
            {
                EventHandler<Exception> onException = OnException;
                try
                {
                    byte byt = (byte)ComPort.ReadByte();
                    ThreadSafe.Queue.Enqueue(new ReadArgument
                    {
                        Side = _side,
                        Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fffffff"),
                        Value = ControlCharacters.Convert(BitConverter.ToString(new[] { byt }))
                    });
                }
                catch (Exception ex)
                {
                    onException?.Invoke(this, ex);
                }
            }
        }

由 A、B 侧附加并由 logwriter 读取的队列:

        public static class ThreadSafe
        {
            public static ConcurrentQueue<ReadArgument> Queue = new ConcurrentQueue<ReadArgument>();
        }

旋转循环,等待传入数据记录,线程从上面的第一个代码片段开始:

        private void AddToFile()
        {
            while (true)
            {
                if (ThreadSafe.Queue.TryDequeue(out ReadArgument ra))
                {
                    _serialLogWriter.Write(ra);
                }
            }
        }

日志写入:

        private readonly StreamWriter _writer;
        private string _lastSide;

        public SerialLogWriter(string path, bool append)
        {
            _writer = new StreamWriter(path, append);
        }

        public void Write(ReadArgument ra)
        {
            lock (_writer)
            {
                if (_lastSide != null && _lastSide != ra.Side)
                {
                    _writer.Write(Environment.NewLine);
                }

                _writer.Write($"<{ra.Time}>;{ra.Side};{ra.Value}{Environment.NewLine}");
                _writer.Flush();

                _lastSide = ra.Side;
            }
        }

关于代码中的同步错误的任何见解?

编辑

似乎更多的是硬件错误,而不是软件错误。嗅探器软件用于老式笔记本电脑,空闲 CPU 使用率为 50-70%,在 64 位 WIN7 上使用 1,7~/2 RAM。我无法使用上面发布的相同初始代码在我的开发环境中重现这种字节同步错误。

尝试通过几个建议优化我的代码:

结果:

我想那时简单地升级我的硬件会更容易。

标签: c#.netserial-port

解决方案


推荐阅读