使用timercallback

时间:2018-02-16 22:29:05

标签: c# serial-port

我正在使用.NET 4.0开发一个项目。我有一个通过串口连接的设备,我发送命令,然后等待响应。一旦收到响应,我将检查响应以确保它是有效的,然后发送下一个命令。

主要问题是我发送的命令响应时间差异很大。一个命令的响应可能需要30ms或30秒。因此我实施了一个TimerCallback来检查串口的每500ms的响应。我已经发布了下面的代码以及我得到的输出。

// From different class
private string SendCommandAwaitResponse(string command)
    {
        // Set timeout to 30 seconds.
        this.port.ReadTimeout = 30000;
        // Clears existing
        this.port.ReadExisting();
        this.port.Write(command);

        var autoEvent = new AutoResetEvent(false);
        var responseTimer = new ResponseTimer(port, command);
        var timer = new Timer(responseTimer.GetResponse, autoEvent, 0, 500);

        autoEvent.WaitOne();
        Trace.WriteLine(string.Format("AutoEvent Set: {0}", command));
        timer.Dispose();

        return responseTimer.Response;
    }

public class ResponseTimer
{
    private ISerialPort serialPort;
    private string command;
    private bool isAttemptingRead;

    public string Response
    {
        get { return this.response; }
        set
        {
            if (this.response == value)
                return;
            this.response = value;
        }
    }
    private string response;

    public ResponseTimer(ISerialPort port, string command)
    {
        Trace.WriteLine("Constructor Created");
        this.isAttemptingRead = false;
        this.serialPort = port;
        this.command = command;
        Response= string.Empty;
    }

    public void GetResponse(Object stateInfo)
    {
        AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;

        try
        {
            if (!isAttemptingRead)
            {
                Trace.WriteLine("Initiating ReadLine");
                Response= serialPort.ReadLine();
                isAttemptingRead = true;
            }
            Trace.WriteLine("Try: " + Response);
        }
        catch (TimeoutException ex)
        {
            Trace.WriteLine(String.Format("EX: TIMED OUT: Response {0}", Response));
            autoEvent.Set();
        }

        Trace.WriteLine(String.Format("CURRENT Response {0}", Response));

        if (Response == command)
        {
            Trace.WriteLine(string.Format("COMMAND: {0}", command));
            Trace.WriteLine(string.Format("Clearing Buffer: {0}", Response));
            Response= string.Empty;
            isAttemptingRead = false;
        }
        else if (!string.IsNullOrEmpty(Response))
        {
            Trace.WriteLine(String.Format("Got Response - {0}", Response));
            autoEvent.Set();
        }
        else if (string.IsNullOrEmpty(Response))
            Trace.WriteLine("Empty or Null read");
    }
}

代码适用于较小的响应时间(30ms),但当它到达响应时间为30秒的命令时,我得到以下输出(主要是)。

的WriteLine(#XSFCD)

  1. 构造函数已创建
  2. 启动ReadLine
  3. 尝试:#XSFCD
  4. CURRENT Response #XSFCD
  5. 命令:#XSFCD
  6. 清除缓冲区:#XSFCD
  7. 启动阅读线
  8. 启动阅读线
  9. 启动Readline等......
  10. 然后我通常只是结束程序。我是否错过了使用TimerCallback方法的东西?

1 个答案:

答案 0 :(得分:1)

从MSDN页面:

serialport.readline

  

将等到它在串口上看到行尾字符或者直到ReadTimeout已经过去。

     

...

     

默认情况下,ReadLine方法将阻塞,直到收到一行。如果不希望出现这种情况,请将ReadTimeout属性设置为任何非零值,以强制ReadLine方法在端口上没有行时抛出TimeoutException。

对于System.Threading.Timer

  

提供以指定的时间间隔在线程池线程上执行方法的机制。

因此每个计时器回调可能在一个单独的线程上,每个readline()调用暂停当前线程,直到它看到行尾字符。无论发生了什么,您的计时器每500毫秒触发一次。所以第一个调用readline()并在那里等待行尾字符。它没有看到它,所以它一直在等待。 500ms后,另一个线程完全相同。 500毫秒后,同样的事情发生,等等。所以你堆叠所有这些readline()调用等待readline()完成。最终你厌倦了等待并杀死程序。您可以使用readExisting()来读取当前缓冲区,而不是readLine(),但您必须找到行尾字符的位置。或者你可以完全摆脱计时器并使用serialport事件(例如DataReceived)告诉你何时获得了一些数据。