2014-01-15 53 views
7

我有很多问题试图让串口接收正确的消息。它不断截断消息。这是我的代码,我会尽力详细说明代码。SerialPort类问题

public SerialComms(SerialPort sp) 
{ 
    this.sp = sp; 
    this.sp.Open(); 
    this.sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived); 

    do 
    { 
     Console.WriteLine("port open waiting message"); 
     Console.ReadKey(); 
    } while(!_terminate); 

    this.sp.Close(); 
} 

void sp_DataReceived(object sender, SerialDataReceivedEventArgs e) 
{ 
    string dataReceived; 
    StringComparer strComp = StringComparer.OrdinalIgnoreCase; 
    SerialPort sp = (SerialPort)sender; 
    int i = sp.BytesToRead; 
    byte[] _byte = new byte[i]; 
    char[] _char = new char[i]; 
    sp.read(_byte, 0, i); 
    dataReceived = Encoding.UTF8.GetString(_byte); 
    //dataReceived = new string(_char); 
    //dataReceived = sp.ReadExisting(); 

    if (strComp.Equals("00000000000000"), dataReceived)) 
     _terminate = true; 

    Console.WriteLine(dataReceived); 
} 

现在我有我们用来测试我们的串行网络公司在生产与传统的软件 - 我知道这运行正常的测试项目。我已将串行监视器连接到端口,并且通过传输的消息没有任何问题。当我第一次发送信息如时,它通常会很好,而在接收端,监视器显示它正在通过;但是,当它打印到控制台时,会在第一条消息之后被截断。我附上了通过端口监视器和控制台输出的消息的屏幕截图(显示的笑脸和心脏是正在转换的前缀字节 - 为什么它是我不知道的心脏和笑脸)

好吧,我无法发布图片,因为我没有足够的信誉来这样做。我会写下输出到下面的控制台(在这种情况下,由于某种原因它也截断了第一条消息):()

在串口监视器上传输的消息如下(我发送它三在每个消息发送之间有几秒'延迟时间'):
02 31 32 33 34 35 36 37 38 39 30 31 32 33 34 03 .123456789。
02 31 32 33 34 35 36 37 38 39 30 31 32 33 34 03 0.123456789。
02 31 32 33 34 35 36 37 38 39 30 31 32 33 34 03 0.123456789。

在控制台我收到了以下(该☺和♥字符是02和03 ,它们是STX和ETX信息是标准为我们传输):
☺123456
789♥

4♥

4♥

这个问题让我疯狂!请帮忙!遗留的是使用过时的MSCommLib,我们正在转向.Net 4

+0

我换成你的占位符与真实的符号,如果我扭转他们请纠正。对于截图,提供链接到像http://imgur.com/这样的网站,而更高级别的用户可以将链接转换为图像。 –

回答

5

这是在任何通信协议完全正常的和常见的。通过网络的TCP也具有此属性。字节作为传输,而不是数据分组。所以当你的DataReceived事件处理器触发时,你只知道你有一些可用的字节。在处理它之前,将接收字节组装成完整的响应取决于您。

这需要协议,这是一种识别您得到完整响应的方法。你有一个,那些STX和ETX字节告诉你。特别是ETX,STX是一种过滤掉连接设备时可能获得的噪音字节的方法。

一个非常简单的方法来得到它去,是到新行属性设置为(char)3并调用的ReadLine()。

一个更好的办法来做到这一点也滤除噪声STX帮助您消除,也避免死锁情况:

private const int MaxResponse = 42; 
private const int STX = 2; 
private const int ETX = 3; 
private byte[MaxResponse] response; 
private int responseLength; 

void sp_DataReceived(object sender, SerialDataReceivedEventArgs e) 
{ 
    var sp = (SerialPort)sender; 
    int cnt = sp.BytesToReceive; 
    for (int ix = 0; ix < cnt; ++ix) { 
     byte b = (byte)sp.ReadByte(); 
     if (responseLength == 0 && b != STX) continue; 
     if (b != ETX) response[responseLength++] = b; 
     else { 
      var str = Encoding.ASCII.GetString(response, 0, responseLength); 
      HandleResponse(str); 
      responseLength = 0; 
     } 
    } 
} 

和写入用handleResponse()方法来处理您接收到的数据。

+0

谢谢你,这给了我很多工作!题?你为什么要++响应长度并将响应[]的索引偏移1?此外,为了避免意外清除响应[](如果它只接收传输的第一部分,然后通过另一个dataReceived调用返回)不应该是if(b == ETX),然后是否则不要继续; ?再次感谢你,这是一个重要的见解! – alykins

+0

另外,我知道来自PLCs的消息总是16字节(stx/etx为2个字节,然后为14个字节的消息)......它是否会导致将MaxREsponse int更改为16而不是42的问题? – alykins

+0

我不会用1来抵消任何东西,它会在*之后增加*。使用响应[++ responseLength]将是错误的。没有东西被清除,响应[]不是局部变量。为了便于阅读,我使用了b!= ETX。当然,使用16. 42只是生命,宇宙和一切的答案。 –

3

有两个问题的可能组合。

首先,一个常见的错误时流的工作是,仅仅因为你要求从Readi字节并不意味着Read将实际读取的字节数,这是不太可能你的问题,但你应该意识到这一点,see this question's answer for the proper pattern

第二个问题是流是流而不是消息。它不知道您发送了☺123456789♥,它只知道它在上次检查和现在之间收到☺123456,并且它应该将该信息报告给用户。

您需要修改你的阅读代码保持,直到它收到下然后采取全缓冲的字节数组,并将其发送给用户的缓冲数据。这就是沿着线路发送的全部原因,它允许接收器能够检测它何时已经到达消息的开始或结束。

+0

+1这正是我要建议的。但是为什么当有人已经把它放得如此雄辩呢? :) – itsme86

+0

感谢您的反馈!我正在用上面的代码示例加入这个理论 - 它现在变得更有意义了!我正在考虑午餐时间,我可能会检查开始/停止字节,这几乎是你所说的:P – alykins