首页新闻找找看学习计划

C# SerialPort 写读和收串包问题

0
悬赏园豆:30 [已关闭问题] 关闭于 2017-10-26 14:06

一个设备,三种指令 Cmd_Req、Cmd_Res、Cmd_Reprot。发送Cmd_Req设备返回Cmd_Res表示成功,之后人为可以对设备执行特定操作,当执行特定操作时设备主动上报Cmd_Reprot。

Cmd_Res是Write Cmd_Req之后主动读的,Cmd_Reprot能通过DataReceived被动接收的。

在运行时,会发生发送 Cmd_Req 后读到 Cmd_Reprot以及DataReceived时读到Cmd_Res的情况,要如何解决这个问题呢?还是说我这方式本身就是错误的。


主要代码是这样的

 

        //CMD_REPORT,CMD_RES,CMD_REQ 为方便写使用代码定义的三个readonly
        bool isSend = false;
        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if(isSend)
                return;
            byte[] Cmd_Report = new byte[serialPort.BytesToRead];
            serialPort.Read(Cmd_Report, 0, serialPort.BytesToRead);
            //实际运行时,Cmd_Report的内容有可能CMD_RES相同

            //DataEqual是判定两个BYTE[]的长度及其中的数据是相同
            if (DataEqual(Cmd_Report, CMD_REPORT))
            {
                //do something result
            }
        }

        public void DoWork()
        {
            
            //do something 
            byte[] Cmd_Req = new byte[10];
            byte[] Cmd_Res = new byte[CMD_RES.Length];
            isSend = true;
            serialPort.Write(Cmd_Req, 0, Cmd_Req.Length );
            stopWatch.Start();
            while(serialPort.BytesToRead < CMD_RES.Length &&  stopWatch.ElapsedMilliseconds< 1000)
                System.Threading.Thread.Sleep(50);
            
            int resCount = 0;
            if (serialPort.BytesToRead < CMD_RES.Length)
                resCount = serialPort.Read(Cmd_Res, 0, serialPort.BytesToRead);
            else
                resCount = serialPort.Read(Cmd_Res, 0, CMD_RES.Length);
            isSend = false;
            //实际运行时,Cmd_Res的内容有可能和CMD_REPORT相同

            if (DataEqual(Cmd_Res, CMD_RES))
            {
                //do something AA
            }
            else
            {
                //do something BB
            }
        }                    

 

 

Selway的主页 Selway | 初学一级 | 园豆:22
提问于:2017-10-20 23:56
< >
分享
所有回答(2)
0

不管多么简单,通讯首先要分包处理(接收方会设定超时机制),你这状态量完全是多余的 —— 除非你玩控制线,不过对于单片机,不少板子是未实现除235之外的接口。

花飘水流兮 | 园豆:10177 (专家六级) | 2017-10-23 09:03

谢谢,通过Stackoverflow一个兄弟的启发,我自己解决了。

支持(0) 反对(0) Selway | 园豆:22 (初学一级) | 2017-10-26 14:01
0

自己解决了把在Stackoverflow上的回答搬过来。


Inspired by the code of JohnEphraimTugado, I thought of the solution and passed the test in the application scenario.

复制代码
  1 using System;
  2 using System.Collections.Generic;
  3 using System.IO.Ports;
  4 using System.Linq;
  5 using System.Text;
  6 using System.Threading;
  7 
  8 public class SerialPortHandler
  9 {
 10     public delegate void OnReportHandler(byte[] data);
 11     public delegate void OnReadExceptionHander(Exception error);
 12     public delegate void OnHandlingExceptionHandler(Exception error);
 13 
 14     public SerialPortHandler(string portName, Predicate<byte[]> reportPredicate, Func<Queue<byte>, byte[]> dequeueFunc)
 15         : this(reportPredicate, dequeueFunc)
 16     {
 17         this._serialPort = new SerialPort(portName);
 18     }
 19 
 20     public SerialPortHandler(string portName, int baudRate, Predicate<byte[]> reportPredicate, Func<Queue<byte>, byte[]> dequeueFunc)
 21        : this(reportPredicate, dequeueFunc)
 22     {
 23         this._serialPort = new SerialPort(portName, baudRate);
 24     }
 25 
 26     public SerialPortHandler(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits, Predicate<byte[]> reportPredicate, Func<Queue<byte>, byte[]> dequeueFunc)
 27       : this(reportPredicate, dequeueFunc)
 28     {
 29         this._serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
 30     }
 31 
 32     private SerialPortHandler(Predicate<byte[]> reportPredicate, Func<Queue<byte>, byte[]> dequeueFunc)
 33     {
 34         _thrdRead = new Thread(new ThreadStart(Read));
 35         _thrdHandle = new Thread(new ThreadStart(DataHandling));
 36         _isRun = false;
 37         _quCmdRespone = new Queue<byte[]>();
 38         _quReceiveBuff = new Queue<byte>();
 39         _cmdResponseReset = new AutoResetEvent(false);
 40         _reportPredicate = reportPredicate;
 41         _dequeueFunc = dequeueFunc;
 42     }
 43 
 44     SerialPort _serialPort;
 45     Thread _thrdRead;
 46     Thread _thrdHandle;
 47     bool _isRun;
 48     /// <summary>
 49     /// Save all data read from the serial port
 50     /// </summary>
 51     Queue<byte> _quReceiveBuff;
 52     /// <summary>
 53     /// Save the response of the last command
 54     /// </summary>
 55     Queue<byte[]> _quCmdRespone;
 56     AutoResetEvent _cmdResponseReset;
 57     bool _isSending;
 58     /// <summary>
 59     /// A method to determine whether a byte[] is a spontaneous report of a serial port
 60     /// </summary>
 61     Predicate<byte[]> _reportPredicate;
 62     /// <summary>
 63     /// Dequeuing a command from the received data queue method
 64     /// </summary>
 65     Func<Queue<byte>, byte[]> _dequeueFunc;
 66 
 67     /// <summary>
 68     /// Called when the serial interface is actively reporting data.
 69     /// </summary>
 70     public event OnReportHandler OnReport;
 71     public event OnReadExceptionHander OnReadException;
 72     public event OnHandlingExceptionHandler OnHandlingException;
 73 
 74     public bool IsOpen
 75     {
 76         get { return this._serialPort == null ? false : this._serialPort.IsOpen; }
 77     }
 78 
 79     /// <summary>
 80     /// Read data from serial port.
 81     /// </summary>
 82     private void Read()
 83     {
 84         while (_isRun)
 85         {
 86             try
 87             {
 88                 if (this._serialPort == null || !this._serialPort.IsOpen || this._serialPort.BytesToRead == 0)
 89                 {
 90                     SpinWait.SpinUntil(() => this._serialPort != null && this._serialPort.IsOpen && this._serialPort.BytesToRead > 0, 10);
 91                     continue;
 92                 }
 93                 byte[] data = new byte[this._serialPort.BytesToRead];
 94                 this._serialPort.Read(data, 0, data.Length);
 95                 Array.ForEach(data, b => _quReceiveBuff.Enqueue(b));
 96             }
 97             catch (InvalidOperationException)
 98             {
 99                 if (!_isRun || this._serialPort ==null)
100                     return;
101                 else
102                     this._serialPort.Open();
103             }
104             catch (Exception ex)
105             {
106                 this.OnReadException?.BeginInvoke(new Exception(string.Format("An error occurred in the reading processing: {0}", ex.Message), ex), null, null);
107             }
108         }
109     }
110 
111     /// <summary>
112     /// Data processing
113     /// </summary>
114     private void DataHandling()
115     {
116         while (_isRun)
117         {
118             try
119             {
120                 if (_quReceiveBuff.Count == 0)
121                 {
122                     SpinWait.SpinUntil(() => _quReceiveBuff.Count > 0, 10);
123                     continue;
124                 }
125                 byte[] data = _dequeueFunc(_quReceiveBuff);
126                 if (data == null || data.Length == 0)
127                 {
128                     SpinWait.SpinUntil(() => false, 10);
129                     continue;
130                 }
131 
132                 if (_reportPredicate(data))
133                     OnReport?.BeginInvoke(data, null, null);    //If the data is spontaneously reported by the serial port, the OnReport event is called
134                 else
135                 {                                               //If the command response returned by the serial port, join the command response queue
136                     if (_quCmdRespone.Count > 0)
137                         _quCmdRespone.Clear();                  //The queue is cleared to ensure that if a command timed out does not affect subsequent command results
138 
139                     _quCmdRespone.Enqueue(data);
140                     _cmdResponseReset.Set();
141                 }
142             }
143             catch (Exception ex)
144             {
145                 this.OnHandlingException?.BeginInvoke(new Exception(string.Format("An error occurred in the data processing: {0}", ex.Message), ex), null, null);
146             }
147         }
148     }
149 
150     /// <summary>
151     /// Read the response of the last command.
152     /// </summary>
153     /// <param name="timeOut"></param>
154     /// <returns></returns>
155     private byte[] ReadCommandResponse(int timeOut)
156     {
157         byte[] buffer = null;
158         if (_cmdResponseReset.WaitOne(timeOut, false))
159             buffer = _quCmdRespone.Dequeue();
160         return buffer;
161     }
162 
163     /// <summary>
164     /// Send a command
165     /// </summary>
166     /// <param name="sendData">command buff</param>
167     /// <param name="receiveData">REF: response of command</param>
168     /// <param name="timeout">timeout(millseconds)</param>
169     /// <returns>count of response, -1: failure, -2: port is busy</returns>
170     public int SendCommand(byte[] sendData, ref byte[] receiveData, int timeout)
171     {
172         if (_isSending)
173             return -2;
174         if (this._serialPort.IsOpen)
175         {
176             try
177             {
178                 _isSending = true;
179                 this._serialPort.Write(sendData, 0, sendData.Length);
180                 int ret = 0;
181                 receiveData = ReadCommandResponse(timeout);
182                 ret = receiveData == null ? -1 : receiveData.Length;
183                 return ret;
184             }
185             catch (Exception ex)
186             {
187                 throw new Exception(string.Format("Send command is failure:{0}", ex.Message), ex);
188             }
189             finally
190             {
191                 _isSending = false;
192             }
193         }
194         return -1;
195     }
196 
197     public bool Open()
198     {
199 
200         if (this._serialPort == null || this._serialPort.IsOpen)
201             return false;
202         this._serialPort.Open();
203         _isRun = true;
204         _thrdRead.Start();
205         _thrdHandle.Start();
206         return true;
207     }
208 
209     public bool Close()
210     {
211         _isRun = false;
212         if (_thrdHandle.IsAlive)
213             _thrdHandle.Join();
214         if (_thrdRead.IsAlive)
215             _thrdRead.Join();
216         if (this._serialPort == null)
217             return false;
218         if (this._serialPort.IsOpen)
219             this._serialPort.Close();
220         return true;
221     }
222 }
复制代码

Usage

 1     SerialPortHandler spHandler;
 2     public void Init()
 3     {
 4         SerialPortHandler spHandler = new SerialPortHandler("COM1", IsReport, DequeueResponse);
 5         spHandler.OnReport += SpHandler_OnReport;
 6     }
 7 
 8     bool IsReport(byte[] data)
 9     {
10         //Determines whether the command is Cmd_Reprot
11         return true;
12     }
13 
14     byte[] DequeueResponse(Queue<byte> quReceive)
15     {
16         byte[] response = null;
17         //Dequeuing a valid response based protocol rules
18         return response;
19     }
20 
21     private void SpHandler_OnReport(byte[] data)
22     {
23         if (DataEqual(Cmd_Report, CMD_REPORT))
24         {
25             //do something X about data then save data
26         }
27     }
28 
29     public void DoWork()
30     {
31         //do something 
32         byte[] Cmd_Req = null;
33         byte[] Cmd_Res = new byte[CMD_RES.Length];
34         int ret = spHandler.SendCommand(Cmd_Req, Cmd_Req, 1000);
35 
36         if (ret > 0 && DataEqual(Cmd_Res, CMD_RES))
37         {
38             //create data, do something A about data
39         }
40         else
41         {
42             //do something B
43         }
44     }

 

我把它发布的nuget了,有需要可以拿来用

1 Install-Package SerialHandler -Version 0.0.1-beta3
View Code

 

                        
Selway | 园豆:22 (初学一级) | 2017-10-26 14:04
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册