C# serialport数据接收不全的问题?

zgiw 2011-10-08 10:41:15
C# serialport读取松下PLC的数据,用的是松下Mewtocol通讯协议,我在timer_tick事件中接收数据,因要实时读取数据,timer设置为1毫秒,数据接收时不全,代码如下:

clsPLC plc = new clsPLC();
string data = "%01#RDD0600006002";
data += plc.CalBcc(data) + (char)13;
serialPort.WriteLine(data);
string outdata = serialPort.ReadExisting();
if (outdata.Length >0)
{
if (outdata.Substring(0, 1) == "%"
&& outdata.Length >=19)
{
iTime += 1;
dgvData.Rows.Add(iTime.ToString(),outdata, outdata.Substring(6, 4), outdata.Substring(14, 4));

}
}
timer设置为100毫秒,可以正确接收数据,但PLC的数据变化快,100毫秒无法准确反应所有数据,不知如何来解决这个问题?

...全文
2289 32 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
32 条回复
切换为时间正序
请发表友善的回复…
发表回复
wingonline 2013-01-24
  • 打赏
  • 举报
回复
public string WriteAndRead(byte[] buffer, int offset, int count, string readFix, int timeout)
        {
            string response = string.Empty;
            bool lockToken = false;
            try
            {
                
                spinLock.Enter(ref lockToken);
                TimeSpan startSpan = new TimeSpan(DateTime.Now.Ticks);
                Write(buffer, 0, buffer.Length);
               

                do
                {

                    TimeSpan doSpan = new TimeSpan(DateTime.Now.Ticks);
                    double millis = doSpan.Subtract(startSpan).TotalMilliseconds;
                    response += Read();
                    
                    if (millis >= timeout)
                    {
                        
                        response = string.Empty;
                        break;
                    }
                }
                while (!response.EndsWith(readFix));
                
            }
            finally
            {
                if (lockToken)
                    spinLock.Exit(lockToken);
            }
            return response;
        }
我目前是这样解决的.
wingonline 2013-01-24
  • 打赏
  • 举报
回复
引用 28 楼 zgiw 的回复:
单纯从电脑上着手可能无法实现实时采集,所以准备用采集卡来处理,程序读取采集的数据来处理,谢谢各位,散分
我就是用电脑实时采集和解释协议的.完全无压力.
wingonline 2013-01-24
  • 打赏
  • 举报
回复
你需要不断在接受,若收到回车符后,就等于协议完整了.不然你永远都不会得到完整的数据.楼主不要想用多线程了.不现实的.虽然RS232可以全双工.但协议都是唔状态标识的.只能发一条,接收一条.
wangyong19072 2012-08-27
  • 打赏
  • 举报
回复
我也碰到这个问题,请教中。
zgiw 2011-11-09
  • 打赏
  • 举报
回复
单纯从电脑上着手可能无法实现实时采集,所以准备用采集卡来处理,程序读取采集的数据来处理,谢谢各位,散分
zgiw 2011-10-25
  • 打赏
  • 举报
回复
现在设备科对plc进行了修改,将数据存放在缓存区中,如一个数值一个寄存器,只要数值有变化就放在不同的寄存器中,可能有几百个寄存器,当然是连续的寄存器,我读取时发现一次只能取25个左右的寄存器数据,所以用循环发送指令(如RDD0010000119,下一个循环为RDD0012000139...),不过在放在语句中发现数据仍会有丢失,如将这个循环指令放在timer中,发现100毫秒则可以正常接收所有数据,如果时间间隔在100毫秒以下也会出现丢失数据,选择100毫秒的话,则1秒中只能取200个寄存器的值,如寄存器一多则速度有影响,不知道各位有没有更好的想法?另外,也想设置plc主动发现数据给pc,但不知道plc如何发送,pc程序如果设置接收?
wanghui0380 2011-10-16
  • 打赏
  • 举报
回复
恩,ls都说了,把任务分开就可以

我们通常是使用消息队列,直接把数据缓存入队列

而另一个线程负责处理队列。这样实际类似流水线操作,上个工序只管上个工序滴,下个工序也只管从总线上拿工件

至于这个中间肯定会有时间差,不过无所谓了要差都差了,也就是数据是连续滴,但是都总体延后了一点(这一点问题不太大,对计算机的运行速度来说,基本是可以承受滴)
zgiw 2011-10-16
  • 打赏
  • 举报
回复
谢谢各位热心解答,我再按各位的思路试试看
trentliu 2011-10-12
  • 打赏
  • 举报
回复
我读了一下FPX系列的相关资料 , modbus协议,可以当做主站,不停的向外传送数据。
如果你的要求很高的话,可以往这个方向看看
sdl2005lyx 2011-10-11
  • 打赏
  • 举报
回复
用楼上的办法试试。。。
mabaolin 2011-10-11
  • 打赏
  • 举报
回复
你把接受数据的程序改掉。只作输出,不作处理。看看是否接受全。
private void serialport_DataReceived(Object sender, SerialDataReceivedEventArgs e)
{
int bytesToRead = this.serialPort1.BytesToRead; //表示总共有多少数据要读取
byte[] ch = new byte[bytesToRead];
int bytesRead = 0;
bytesRead = this.serialPort1.Read(ch, 0, bytesToRead);
str = this.ByteArrayToHexString(ch).Replace(" ", "").Trim();
Console.WriteLine(str);
}
如果能接受全表示你处理数据部分存在耗时的问题。另外,你的程序中用了while处理性能不好,直接一次读取缓存中所有数据即可。
trentliu 2011-10-11
  • 打赏
  • 举报
回复
楼上讲得是,问询模式的极限也仅仅是趋近于线性反馈。 只有从模式上下手,改问询式为广播方式。 这需要你详细阅读PLC的设置说明
sdl2005lyx 2011-10-11
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 builderc 的回复:]

我有个严重的怀疑,你每1毫秒发送一次请求的意义在哪里?

若传输速率是1200波特(其中有一个起始位, 8 个数据位, 2 个停止位),那么,每秒所能传送的字符数是1200/(1+8+1+2)= 100个。

假设1个报文20个字符

你的通信极限也就是200毫秒1个报文。

如果是9600/(1+8+1+2) = 800 个

你的通信极限在 40 毫秒 1 个报文

……
[/Quote]

楼主,为什么要这么快的频率?没什么实际实际意义啊:
本来,上位机和下位机的数据就不能真正的实时同步的,PLC有可能达到纳秒级别,而上位机至少是毫秒级的,两者相差1000以上,其实,向你说的情况,一般是不需要上位机主动发命令获取数据,而是有下位机根据一定时间间隔主动向上位机发送数据,上位机负责接收、处理就可以了!
trentliu 2011-10-11
  • 打赏
  • 举报
回复
这么讲吧,如果你的PLC是半双工那就真没办法达到线性反馈了
如果是全双工,减小请求间隔也许会得到近似于线性的反馈。

你把你的PLC的型号贴出来,我看看能不能从硬件或者通信模式上改造达到线性反馈
zgiw 2011-10-11
  • 打赏
  • 举报
回复
plc型号是松下AFPX-C30T,我会从plc方面着手,让plc发送指令给上位机,上位机接收数据并处理,因今天没空处理此问题,所以来晚了,望各位高手见谅,明天上班继续处理。
trentliu 2011-10-11
  • 打赏
  • 举报
回复
根本问题不在接收上!!!!
trentliu 2011-10-10
  • 打赏
  • 举报
回复
全双工还是半双工?
如果是半双工,你就不太可能获得连续线性反馈。 因为一个请求处理过程也得几十毫秒

如果是全双工,我再帮你想想办法
zgiw 2011-10-10
  • 打赏
  • 举报
回复
为了感谢BuilderC的热心解答,我帖子已加分到100分,在问题解决后结贴
zgiw 2011-10-10
  • 打赏
  • 举报
回复
PLC中寄存器的值从1->150->60->150按顺序在变化,我发送指令的间隔分别设置为1ms,10ms,20ms,50ms,100ms,200ms,getdata()也已经放在另一个timer中,
thread.sleep(wTimer); //wTimer分别=200、500、1000

中断查看serialPort_DataReceived事件中的currentline的值:
%01$RD40000000400016%01$RD45000000450016.......

发现currentline的值就已经有间隔了,不是连续的,通过以下计算
int rom1 = Convert.ToInt32(revdata.Substring(8, 2) + revdata.Substring(6, 2),16);
int rom2 = Convert.ToInt32(revdata.Substring(16, 2) + revdata.Substring(14, 2), 16);

会发现rom1或rom2的值总是相差2-3值,如64、66、69...这样变化,这样就不能完整反应寄存器的变化,还有没有其它优化方法?

再次非常感谢BuilderC!
zgiw 2011-10-10
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 mabaolin 的回复:]
lz的理解有误。 serialport的接受数据事件是实时的,一旦有数据到达就会触发此事件。因此不需要你去定时1ms读。而应当单独定义一个timer,每1ms给plc写数据。
另外你接收数据的处理可以优化。

private void serialport_DataReceived(Object sender, SerialDataReceivedEventArgs e)
{
……
[/Quote]

一定要先发送读取指令给PLC,plc才会反馈数据,如果不发送读取指令则不可能触发datareceived事件的,你可以看我其它楼层的帖子,已经改了,不在timer中读取,是在timer中1毫秒发送一条读取指令,自动触发datareceived事件接收数据,现在数据是可以读取,不过会跳跃式读取,中间会有多个数值的间隔,不是连接的数值,所以还请各位高手帮忙
加载更多回复(12)

111,094

社区成员

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

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

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