c# - 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。我无法使用上面发布的相同初始代码在我的开发环境中重现这种字节同步错误。
尝试通过几个建议优化我的代码:
- 将处理/转换移至日志写入器线程;
- 消除了 TAP 和线程的混合,只留下线程并将写入器的优先级设置为低于 COM 读取器。
- 通过将 ConcurrentQueue 替换为 BlockingCollection 摆脱了紧密循环;
结果:
- 开发环境没有变化,几乎是高端设置;
- 旧笔记本电脑上仍然出现相同的字节同步错误;
我想那时简单地升级我的硬件会更容易。
解决方案
推荐阅读
- excel - 如何从 Excel 条形图中删除轴标签?
- python - 如何在xpath中打印前面的兄弟姐妹
- python - 如何比较两个不同熊猫数据框中的两列?
- node.js - Docker Build CLI 不更新代码(TS、节点)
- c++ - 带有while循环条件比较字符串的问题
- c++ - C++ 对象如何处理被分配一个数组字面量?
- matlab - 神经网络中的反向传播 one while 循环
- security - 如何检查 socket.io 请求来源的 SSL
- reactjs - 如何将函数从父组件传递给具有可分配类型的子组件
- sqlalchemy - 使用Sqlalchemy查询全部时,为什么不返回具体值,如何处理