问题
最近比较经常使用串口进行发送以及传输数据,但是笔者在刚开始接触SerialPort类时,对于Write之后去Read数据的时候,由于设备上面还没有返回数据,读取到的只能是空值。然而,再进行下一次Write操作的时候,可能返回上一次数据和这一次数据。这样使得笔者得到的数据难以保持准确性。
1、解决思路
对于上面的问题,由于数据要实时的进行返回。所以笔者想法就是在Write之后,使用线程等待(Thread.Sleep)去等待数据返回。但是最终等待多久,根据串口传输以及设备返回命令所决定。这个等待的时间是个纠结的事情。笔者就考虑:“可以通过重复相应的次数进行等待,如果等待之后还是没有获取到数据,直接舍去这个数据”。不知道园内有没有更好的解决方案。
2、解决源码
这个插入一个题外话:对于串口的编程,要注意及时的开关,但是笔者要实时获取数据使数据保持一种常开的状态,所以笔者所能想到的就是实现IDisposable接口进行清理。
笔者对于写入与获取数据的核心编码就是在串口数据获取类指定一个内部变量(_isWorking),如果_isWorking 正在工作,就让接下来的Write命令进行等待(等待时长必须长于重复次数的总时长),获取到数据之后,将_isWorking 设置为false。【这里存在一个问题,如果高数据命令情况下,会阻塞!!笔者还未找到好的解决方法】。
好了又扯了这么多,上主要的代码,这里有一点特别注意的,以下代码只能够解决的串口接收单个byte数据的内容,具有缺陷,不推荐多数据使用。
/// <summary> /// 串口写入数据,并且等待返回值 /// </summary> /// <param name="writeCommandByte">写入命令</param> /// <param name="replyData">返回数据</param> /// <returns>返回数据偏移位</returns> protected int WriteWithReplyData(byte[] writeCommandByte, out byte[] replyData) { if (writeCommandByte == null) { throw new ArgumentNullException("writeCommandByte"); } //等待其他发送数据的完成 var waitTimes = 0; while (waitTimes < 4 && _isWorking) { waitTimes++; Thread.Sleep(40); } //如果超时还是没有等待到,就直接返回 if (_isWorking) { replyData = null; return 0; } //开始工作,设置串口工作状态未true _isWorking = true; _port.Write(writeCommandByte, 0, writeCommandByte.Length); replyData = new byte[_port.ReadBufferSize]; int count = 0; var times = 0; //等待数据返回,并限制等待时间100ms while (times < 5 && count <= 0) { times++; count = _port.Read(replyData, 0, _port.ReadBufferSize); if (count <= 0) { Thread.Sleep(20); } } //等待得到数据,将工作状态设为false _isWorking = false; return count; }
3、最终解决方案
1)通过串口SerialPort 订阅DataReceived事件进行获取串口数据
2)当然也可以使用线程实时读串口数据(用容器收集起来)
具体的代码如下:
/// <summary> /// 串口数据接收时间 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void _port_DataReceived(object sender, SerialDataReceivedEventArgs e) { var byteCount = _port.BytesToRead; if (byteCount > 0 ) { byte[] reciveByteArr = new byte[byteCount]; int offsetCount = _port.Read(reciveByteArr, 0, byteCount); //接收字节集合 _recivedDataList.AddRange(reciveByteArr); //验证数据包是否完整,虚方法可以继承进行重写 while (CheckPortPackageComplete()) { //获取数据接收包的总长度,虚方法可以继承进行重写 int packageLength = GetCompletePackageLength(); var packageList = _recivedDataList.GetRange(0, packageLength); _recivedDataList.RemoveRange(0, packageLength); var packageArray = packageList.ToArray(); //解析算法,通过外部进行添加解析算法,可以保持解析的扩展性 foreach (var analyticalItem in _analyticalDic) { var obj = analyticalItem.Value(packageArray); if (obj != null) { //事件通知数据 OnReciveDataNotice(obj, analyticalItem.Key); break; } } } } }
(如果觉得不错请点赞下,有误的话请指出,卤鸽在此感谢)