从不同线程的串行端口读取数据

时间:2016-11-25 14:50:39

标签: c# asynchronous serial-port

使用this example我创建了两个用于在串行端口上读取设备的类。该设备是一个激光器,我可以使用SYST:STAT?等命令控制(打开/关闭系统/激光,读取统计数据等)。大多数命令会立即发回一个我可以使用_serialPort.ReadLine()读取的响应。

但是,打开系统的命令SYST:STAT ON是大约需要十秒钟的命令之一。这就是激光制造商决定发送一条消息说:系统就绪的好处。我可以使用NewSerialDataRecieved事件异步读取此响应。在这里,我读了这样的每一行:

private void OnDataReceived(object sender, SerialDataEventArgs e)
{
    _dataReceived += Encoding.ASCII.GetString(e.Data);

    // Loop as long as there's full lines available
    while (_dataReceived.Length > 0 && _dataReceived.Contains("\r\n"))
    {
        int delimiterIndex = _dataReceived.IndexOf("\r\n", StringComparison.Ordinal) + 2;
        string line = _dataReceived.Substring(0, delimiterIndex); // Get first line from data
        _dataReceived = _dataReceived.Substring(delimiterIndex, _dataReceived.Length - line.Length); // Remove first line from data
        line = line.Substring(0, line.Length - 2);

        TryParseSystemReadyResponse(line);
        TryParseEnteringStandbyModeResponse(line);
        TryParseTemperatureResponse(line);

        OnResponseReceived?.Invoke(line);
    }
}

该方法读取传入的数据,存储它,然后逐行解析数据。这非常适合我的异步,定义明确的响应。这里有一个问题:大多数回复都被格式化为' 0 0'或者' 0 1'。这意味着没有办法像那样解析它们。这就是我将命令发送到串行设备后直接读取它们的原因,如下所示:

public void UpdateLaserState()
{
    // Get the selected laser
    _serialPortManager.SendCommand("LSR:NSEL?");
    int selectedLaser = _serialPortManager.ReadIntegerResponse();
    // Do stuff with the laser number
    OnSystemStateChange?.Invoke();
}

ReadIntegerResponse是一种执行ReadLine的方法,它会删除前面的' 0'然后将剩余的字符串解析为int。到目前为止,沟通工作和我期望的一样。可以立即读取可以同步的消息,并且可以通过OnDataReceived方法解析异步消息。

这就是问题所在。我需要大约两秒读取激光系统的温度。我的解决方案是创建一个读取温度的线程,如下所示:

public Laser(){
    _temperatureThread = new Thread(TemperatureTask);
    _temperatureThread.Start();
    _temperatureThreadRunning = true;
}

public void Dispose()
{
    _temperatureThreadRunning = false;
    _temperatureThread.Join();
}

private void TemperatureTask()
{
    while (_temperatureThreadRunning)
    {
        this.UpdateTemperatures();
        Thread.Sleep(TemperatureTaskSleep);
    }
}

public void UpdateTemperatures()
{
    _serialPortManager.SendCommand("SYST:TEMP?");
    //string line = _serialPortManager.ReadResponse();
    //TryParseTemperatureResponse(line);
}

这会产生问题,因为UpdateTemperatures会在不同的线程上经常发送响应。我通过实现锁解决了很多问题。但是,我的回答经常变得混乱。我认为以下情况正在发生:

Main thread            Temperature thread
-----------------------------------------
Sends command       |
                    |  Sends command
Reads response      | 
                    |  Reads response

这意味着线程正在读取每个响应。没有什么好方法可以识别像' 0 0'属于某个命令。

有没有办法防止这种情况发生?

P.S。 _serialPortManager的ReadResponse方法看起来都像这样(解析响应除外):

public string ReadResponse()
{
    string response = SafeReadLine();
    return StripResponse(response);
}

private string SafeReadLine()
{
    lock (_serialPortLock)
    {
        string response = _serialPort.ReadLine();
        return StripResponse(response);
    }
}

0 个答案:

没有答案