多线程发送,接收串口数据

平静大海 2011-08-26 09:58:14
目前多线程接收,解析数据处理好了,但用多线程发送数据时数据出了问题,一是有些指令没有数据返回,二是线程执行顺序是错了,我是用c#.NET 线程池处理的

小弟想实现这样的功能,用多线程发送数据,多线程接收数据,多线程解析数据,保证实时读取串口接收的数据。可下面贴出来的这些方法没有实现要的功能,琢磨了快一个星期了,希望各位大哥指教,能给好的指导思想,有串口多线程发送,接收的项目例子更好。

/// <summary>
/// 发送多条指令,用异步的方式接收数据
/// </summary>
/// <param name="vData">发送的数据和读取返回的数据)</param>
/// <param name="IsReturn">true 读取返回的数据 false 不返回数据)</param>
/// <returns></returns>
public bool SendData(string[] cmd, string tagData, bool fag)
{
DateTime a1 = DateTime.Now;
//for (int i = 0; i < 1000; i++)
//{
foreach (string s in cmd)
{
if (s == string.Empty || tagData == string.Empty)
return false;
if (!serialState)
this.InitPortCom();
tab = parms.PtlParms(s);
if (tab == null || tab.Rows.Count == 0)
return false;
DataRow frameData = tab.Select(string.Format("type='{0}' and name='{1}' ", "Output", "命令长度"))[0];
int frameLen = Convert.ToInt32(basefun.valtag(frameData["pms"].ToString(), "{命令长度}")) * 2 + 12;
byte[] buffData = analy.SetPtlData(tab, tagData);
serialPort.Write(buffData, 0, buffData.Length);
this.WaiteSendData(new SendTaskInfo(buffData, s));
this.WaiteReceiveData(new ReceiveTaskInfo(s, frameLen));
this.WaitAnalysisData(new ReceiveTaskInfo(s, frameLen));
}
return true;
}

/// <summary>
/// 开启线程循环发送数据
/// </summary>
private void WaiteSendData(object data)
{
if (!serialPort.IsOpen)
return;
SendTaskInfo state = data as SendTaskInfo;
System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(ThreadSend), state);
}

/// <summary>
/// 开启线程循环取帧
/// </summary>
private void WaiteReceiveData(object data)
{
if (!serialPort.IsOpen) return;
ReceiveTaskInfo state = data as ReceiveTaskInfo;
System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(ThreadReceive), state);
}

/// <summary>
/// 开启线程解析数据
/// </summary>
/// <param name="data"></param>
private void WaitAnalysisData(object data)
{
if (!serialPort.IsOpen) return;
ReceiveTaskInfo state = data as ReceiveTaskInfo;
System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(GetData), state);
}

/// <summary>
/// 开启一个线程发送数据
/// </summary>
/// <param name="state"></param>
private void ThreadSend(object state)
{
SendTaskInfo send = state as SendTaskInfo;
lock (ThreadObject)
{
Debug.Write("ThreadSendOne" + " " + send.cmdName + ":" + DateTime.Now.ToString("mm:ss.ffff") + "\r\n");
SendTaskInfo data = state as SendTaskInfo;
byte[] buffData = data.sendData;
serialPort.Write(buffData, 0, buffData.Length);
Debug.Write("ThreadSendTwo" + " " + System.Text.Encoding.ASCII.GetString(buffData) + ":" + DateTime.Now.ToString("mm:ss.ffff") + "\r\n");
Thread.Sleep(300);
}
}


/// <summary>
/// 开启一个线程接收数据
/// </summary>
/// <param name="Obj"></param>
private void ThreadReceive(object state)
{
ReceiveTaskInfo task = state as ReceiveTaskInfo;
List<byte> tmpList = new List<byte>();
Debug.Write("ThreadReceive1" + " " + task.cmdName + ":" + DateTime.Now.ToString("mm:ss.ffff") + "\r\n");
bool lenState = false;
//manulReadEvent.Reset();
//bool fag = manulReadEvent.WaitOne();
//if (!fag) return;
lock (ThreadObject)
{
while (!lenState)
{
if (serialPort.BytesToRead > 0)
{
byte[] buff = new byte[serialPort.BytesToRead];
serialPort.Read(buff, 0, buff.Length);
tmpList.AddRange(buff);
if (tmpList.Count != task.cmdLen)
continue;
else
{
lenState = true;
Debug.Write("ThreadReceive2" + " " + tmpList.Count + ":" + DateTime.Now.ToString("mm:ss.ffff") + "\r\n");
returnBuff.Enqueue(tmpList.GetRange(0, task.cmdLen).ToArray());
tmpList.Clear();

//manulWriteEvent.Set();
}
}
else
break;
}
}
}

/// <summary>
/// 解析数据
/// </summary>
/// <param name="data"></param>
private void WaitAnalysisData(object data)
{
if (!serialPort.IsOpen) return;
ReceiveTaskInfo state = data as ReceiveTaskInfo;
System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(GetData), state);
}

/// <summary>
/// 解析串口数据
/// </summary>
/// <param name="Obj"></param>
private void GetData(object state)
{
if (tab == null || tab.Rows.Count == 0 || returnBuff.Count == 0)
return;
ReceiveTaskInfo task = state as ReceiveTaskInfo;
Debug.Write("GetData:" + task.cmdName + ":" + DateTime.Now.ToString("mm:ss.ffff") + "\r\n");
byte[] buffAll = new byte[0];
lock (QueueObject)
{
Debug.Write("returnBuff.Count:" + returnBuff.Count + ":" + DateTime.Now.ToString("mm:ss.ffff") + "\r\n");
buffAll = returnBuff.Dequeue();
Debug.Write("buffAll.Count:" + buffAll.Length + ":" + DateTime.Now.ToString("mm:ss.ffff") + "\r\n");
string len = string.Empty;
int frameLen = 0;
byte[] buffTemp = new byte[4];
for (int i = 0; i < 4; i++)
{
buffTemp[i] = buffAll[i + 5];
}
len = GetAscByByte(buffTemp);
frameLen = Convert.ToInt16(len, 16) + 8;
if (buffAll.Length < frameLen)
return;

string returnStr = string.Empty;
returnStr = GetAscByByte(buffAll);
Debug.Write(returnStr + ":" + DateTime.Now.ToString("mm:ss.ffff") + "\r\n");
bool fag = analy.GetPtlData(tab, returnStr, ref data);
if (!fag) return;
if (returnData.ContainsKey(task.cmdName.Trim()))
{
returnData.Remove(task.cmdName.Trim());
returnData.Add(task.cmdName.Trim(), data);
}
else
{
returnData.Add(task.cmdName, data);
}
Debug.Write(data + "1:" + DateTime.Now.ToString("mm:ss.ffff") + "\r\n");
if (dataFromPromEventHandle != null)
dataFromPromEventHandle(this, null);
Thread.Sleep(300);
}
}
...全文
795 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
_学而时习之_ 2013-12-09
  • 打赏
  • 举报
回复
串口的读定是独占的,所以读写必须要协调好。一般是一个读一个写的线程或者任务就可以了。我写的是用两个task,一个读一个写。 因为com是独占性的(本质上是IO操作),所以多个线程一起写com时,很容易引起并发,而导致各种不可意料的情况出现,所以发数据部分使用一个task或者一个线程操作为上。 其次在读取com口的数据时,com数据是一段一段的。里面其实是一个内存空间,设备不断的往里写数据,然后我们不断的从com中读取数据,所以读取的数据是间歇性的,是片断性的。必须基于通讯的协议进行后续的重组处理,比如返回的数据中是以某个特殊的标志开始,如01,又以特殊的标志结束,如ff。 可能会疑问,那数据中有可能会有大量的01和ff这样的值,那怎么判断哪个是数据哪个是标志呢?这就是协议通信时的协议起到作用。需要在特定的位置增加一个长度,比如紧跟在01后的就是表示长度的,然后读取到这个长度的数据就是所要的数据了。再之后就是新的数据。
rtdb 2013-12-09
  • 打赏
  • 举报
回复
根本方向就错了。 串口是慢速硬件设备,不适合也没必要用多线程的,能够异步接收就差不多了。
_学而时习之_ 2013-12-09
  • 打赏
  • 举报
回复
引用 9 楼 xingxing513234072 的回复:
[quote=引用 8 楼 xxdddail 的回复:] 串口的读定是独占的,所以读写必须要协调好。一般是一个读一个写的线程或者任务就可以了。我写的是用两个task,一个读一个写。 因为com是独占性的(本质上是IO操作),所以多个线程一起写com时,很容易引起并发,而导致各种不可意料的情况出现,所以发数据部分使用一个task或者一个线程操作为上。 其次在读取com口的数据时,com数据是一段一段的。里面其实是一个内存空间,设备不断的往里写数据,然后我们不断的从com中读取数据,所以读取的数据是间歇性的,是片断性的。必须基于通讯的协议进行后续的重组处理,比如返回的数据中是以某个特殊的标志开始,如01,又以特殊的标志结束,如ff。 可能会疑问,那数据中有可能会有大量的01和ff这样的值,那怎么判断哪个是数据哪个是标志呢?这就是协议通信时的协议起到作用。需要在特定的位置增加一个长度,比如紧跟在01后的就是表示长度的,然后读取到这个长度的数据就是所要的数据了。再之后就是新的数据。
思路是对的,一个线程写数据是可行的,但当上位机发送命令后,下位机需要立即返回数据,该种处理方式不知道是否合理,没有测试过。用线程同步可以做到多线程发送,本质上还是你刚描述的,COM是独占的。[/quote] 嗯。我们开发的项目中是用两个线程,一个读一个写实现的,而且运行的效果还可以。
平静大海 2013-12-09
  • 打赏
  • 举报
回复
引用 8 楼 xxdddail 的回复:
串口的读定是独占的,所以读写必须要协调好。一般是一个读一个写的线程或者任务就可以了。我写的是用两个task,一个读一个写。 因为com是独占性的(本质上是IO操作),所以多个线程一起写com时,很容易引起并发,而导致各种不可意料的情况出现,所以发数据部分使用一个task或者一个线程操作为上。 其次在读取com口的数据时,com数据是一段一段的。里面其实是一个内存空间,设备不断的往里写数据,然后我们不断的从com中读取数据,所以读取的数据是间歇性的,是片断性的。必须基于通讯的协议进行后续的重组处理,比如返回的数据中是以某个特殊的标志开始,如01,又以特殊的标志结束,如ff。 可能会疑问,那数据中有可能会有大量的01和ff这样的值,那怎么判断哪个是数据哪个是标志呢?这就是协议通信时的协议起到作用。需要在特定的位置增加一个长度,比如紧跟在01后的就是表示长度的,然后读取到这个长度的数据就是所要的数据了。再之后就是新的数据。
思路是对的,一个线程写数据是可行的,但当上位机发送命令后,下位机需要立即返回数据,该种处理方式不知道是否合理,没有测试过。用线程同步可以做到多线程发送,本质上还是你刚描述的,COM是独占的。
平静大海 2013-12-08
  • 打赏
  • 举报
回复
之前已经解决了,不过现在没做这方面的技术
学无止境ab 2013-12-06
  • 打赏
  • 举报
回复
请问 现在解决了吗
lfqsy 2011-08-26
  • 打赏
  • 举报
回复
太长了,如果串口部分本身没有问题,写好线程池就可以了
萧炎 2011-08-26
  • 打赏
  • 举报
回复
LZ 表示很纠结 这个代码太长了点
你直接把你的错误代码部分用红色表示
平静大海 2011-08-26
  • 打赏
  • 举报
回复
自己 up 下

System.Threading.ThreadPool.QueueUserWorkItem()是不是先放进去的请求先执行,如果是的话,跟我监听的结果就不一致了。本因发送数据的请求在先,接收数据的请求在后,应当先执行发送请求,可实际上先执行接收数据请求
qiangqiang415 2011-08-26
  • 打赏
  • 举报
回复
加以关注

110,533

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧