2010-12-22 87 views
1

我寫使用C#SerialPort類與經由RS232電纜連接的剝機交互的串行通信程序.NET的SerialPort DataReceived事件檢索螺紋干涉。當我將命令發送到機器時,它根據命令響應一些字節。就像我發送一個「\ D」命令一樣,我期待以連續字符串的形式下載180字節的機器程序數據。根據機器的手冊,它建議發送諸如逗號(,)字符等未識別字符的最佳做法,以確保在發送週期中的第一個命令之前初始化機器。我的串口通信代碼如下:與主線程

public class SerialHelper 
{ 
    SerialPort commPort = null; 
    string currentReceived = string.Empty; 
    string receivedStr = string.Empty; 
    private bool CommInitialized() 
    { 
     try 
     { 
      commPort = new SerialPort(); 
      commPort.PortName = "COM1"; 
      if (!commPort.IsOpen) 
       commPort.Open(); 
      commPort.BaudRate = 9600; 
      commPort.Parity = System.IO.Ports.Parity.None; 
      commPort.StopBits = StopBits.One; 
      commPort.DataBits = 8; 
      commPort.RtsEnable = true; 
      commPort.DtrEnable = true; 

      commPort.DataReceived += new SerialDataReceivedEventHandler(commPort_DataReceived); 
      return true; 
     } 
     catch (Exception ex) 
     { 
      return false; 
     } 
    } 

    void commPort_DataReceived(object sender, SerialDataReceivedEventArgs e) 
    { 
     SerialPort currentPort = (SerialPort)sender; 
     currentReceived = currentPort.ReadExisting(); 
     receivedStr += currentReceived; 
    } 

    internal int CommIO(string outString, int outLen, ref string inBuffer, int inLen) 
    { 
     receivedStr = string.Empty; 
     inBuffer = string.Empty; 
     if (CommInitialized()) 
     { 
      commPort.Write(outString); 
     } 

     System.Threading.Thread.Sleep(1500); 

     int i = 0; 
     while ((receivedStr.Length < inLen) && i < 10) 
     { 
      System.Threading.Thread.Sleep(500); 
      i += 1; 
     } 

     if (!string.IsNullOrEmpty(receivedStr)) 
     { 
      inBuffer = receivedStr; 
     } 
     commPort.Close(); 

     return inBuffer.Length; 

    } 
} 

我打電話從窗戶形式的代碼如下:

len = SerialHelperObj.CommIO(",",1,ref inBuffer, 4) 
len = SerialHelperObj.CommIO(",",1,ref inBuffer, 4) 
If(inBuffer == "!?*O") 
{ 
    len = SerialHelperObj.CommIO("\D",2,ref inBuffer, 180) 
} 

從串口一個有效的返回值如下: \ D00000010000000000010 550 3250 0000256000等等...

我得到一些像這樣的事情: \ D00000010D ,, 000 550 d ,,等等...

我覺得當我發送命令時,我的通信調用正在被一個調用。但我試圖確保逗號命令的結果,然後啓動實際的命令。但接收到的線程正在插入前一個通信週期的字節。

任何一個可以請一些啓發到這個......? 爲了完成這項工作,我失去了一些頭髮。我不知道我在哪裏做錯了

+1

這可能並不能解決你的問題,但它看起來像你正在使用Thread.Sleep,以便等待從IO獲得響應,而不是這樣做,你應該使用線程信號。查看System.Threading.ManualReset事件。 – 2010-12-22 01:28:16

+0

您需要更好地記錄預期與實際響應。首先你需要做的是在程序啓動時只打開端口,當它結束時關閉它。不斷打開/關閉不是一個很好的支持情況。 – 2010-12-22 01:31:17

回答

0

爲什麼你每次都重新初始化的端口?我會認爲一個就足夠了。

0

我看到了一些與你的代碼的問題。

  1. 永遠不要忽視例外。返回false忽略了一個異常。當你這樣做時,你不知道發生了什麼異常。
  2. 特別是,您的commPort對象可能永遠不會被實例化。它可能處於任何狀態,但是你忽略了異常並繼續訪問潛在無效的對象。
  3. DataReceived事件可能在端口打開的任何時候發生。它可能會也可能不會在同一個線程中提出。您的代碼可能會在事件處理程序之外關閉該端口,這會拋出您的代碼無法捕捉的異常。
  4. 這裏可能沒關係,但是你知道在.NET中字符串連接是一個相對昂貴的操作嗎?字符串是不可變的。 receivedStr += currentReceived不附加到receivedStr - 它創建一個新的字符串對象來保存這兩個部分。
2

串口不是線程安全的資源,也不是與它們一起工作的包裝器,也不是你用來保存結果的字符串。發生的事情是DataReceived事件處理程序多次觸發,並且最終會同時執行多個處理程序。試着增加一個鎖塊到您的事件處理程序:

... 

Object lockingObj = new Object(); 

... 

void commPort_DataReceived(object sender, SerialDataReceivedEventArgs e) 
{ 
    lock(lockingObj) 
    { 
     SerialPort currentPort = (SerialPort)sender; 
     currentReceived = currentPort.ReadExisting(); 
     receivedStr += currentReceived; 
    } 
} 

lock語句防止多個線程在同一時間在塊執行代碼,所以之前的另一寫入receivedStr會得到一個ReadExisting的全部內容它。

請注意,這並不能保證執行的順序;首先獲勝。因此,假設線程1先到達此處,然後線程2到達250毫秒後,線程3在250毫秒之後到達。線程1很可能首先進入,執行Read將需要一段時間,然後退出。那時,線程2和3進入函數並在鎖定語句中被阻塞,等待線程1釋放其鎖。一旦發生這種情況,首先由內核調度哪個線程,這可能是線程3,具體取決於許多操作系統和硬件因素。