C#中可见的IO.Ports.SerialPort缺陷或可能的硬件缺陷

时间:2013-07-16 14:35:03

标签: c# serial-port visual-studio-debugging

请原谅我,因为这将是一个相当长的帖子。我目前正在使用C#中的SerialPort类编写一个应用程序来与一个名为Fluke 5500A的设备进行通信。在过去,我遇到了许多问题,因为设备发出命令所花费的时间并且最多只能输出无法预测的输出。我昨天在这里问了一个问题:System.Timers.Timer Usage这个问题的答案很精彩,大部分时间似乎都很完美。作为一个例子,我用来连接到SerialPort的类现在看起来像这样:

public class SerialPortConnection
{
    private SerialPort serialPort;
    private string ping;
    double failOut;
    bool isReceiving;

    public SerialPortConnection(string comPort = "Com1", int baud = 9600, System.IO.Ports.Parity parity = System.IO.Ports.Parity.None, int dataBits = 8, System.IO.Ports.StopBits stopBits = System.IO.Ports.StopBits.One, string ping = "*IDN?", double failOut = 2)
    {
        this.ping = ping;
        this.failOut = failOut * 1000;

        try
        {
            serialPort = new SerialPort(comPort, baud, parity, dataBits, stopBits);
            serialPort.NewLine = ">";
            serialPort.ReadTimeout = 1000;
        }
        catch (Exception e)
        {
            serialPort = null;
        }
    }

    //Open Serial Connection. Returns False If Unable To Open.
    public bool OpenSerialConnection()
    {
        //Opens Initial Connection:
        try
        {
            serialPort.Open();
            serialPort.Write("REMOTE\r");
        }
        catch (Exception e)
        {
            return false;
        }

        serialPort.Write(ping + "\r");
        var testReceived = "";

        try
        {
            testReceived += serialPort.ReadLine();
            return true;
        }
        catch
        {
            return false;
        }
    }

    public string WriteSerialConnection(string SerialCommand)
    {
        serialPort.Write(String.Format(SerialCommand + "\r"));
        var received = "";

        try
        {
            received += serialPort.ReadLine();
            return received;
        }
        catch
        {
            received = "Error: No Data Received From Device";
            return received;
        }
    }

    public bool CloseSerialConnection()
    {
        try
        {
            serialPort.Write("LOCAL\r");
            serialPort.Close();
            return true;
        }
        catch (Exception e)
        {
            return false;
        }
    }
}

正如您所看到的,当我打开一个连接时,在这种情况下,Com1我通过向SerialPort写一个命令*IDN?来测试连接。此命令的返回如下所示:

FLUKE,5500A,8030005,2.61+1.3+2.0+*
66>

在类中我将">"设置为NewLine属性,以便SerialPort.ReadLine()在找到该标记之前不会完成。我从来没有曾经让类本身抛出异常,但我注意到在调试时有时testReceived无法正确捕获返回的数据,尽管事实上没有抛出异常且代码继续正常执行,而received将捕获返回的字符串:

FLUKE,5500A,8030005,2.61+1.3+2.0+*
66>

每当我通过SerialPort.Write();传递第一个命令时,重要的是要知道命令可以在没有完全返回数据的情况下执行。我担心的是,最初的ReadLine()似乎偶尔会跳过,而不会抓住整个回归。我的想法是,我正在与之通信的设备存在固有的缺陷导致这种情况,但我希望在继续之前完全确定。

我的命令顺序如下:

首先我在启动时提交命令:

REMOTE

这会禁用与设备手动界面的交互,并允许我通过串行端口提交命令。

然后我发出*IDN?,在这种情况下,检查设备是否已连接:

*IDN?

如果没有返回,则应用程序设置为在消息框中显示错误,然后FailFast。如果一切顺利,可以像这样提交命令:

STBY
OUT 30MV,60HZ
OPER

此处手动提交的唯一命令是OUT 30,MV,60HZ。 app.config中设置了STBYOPER,因为它们只为应用程序的使用添加了不必要的步骤。出于安全原因,STBY命令将机器置于待机状态。 OPER命令将其置于操作模式,设备开始在设置的参数下运行。

然后,应用程序等待技术人员将结果输入文本框并提交。这些结果的内容并不是特别相关,但在按下结果按钮后,机器将重新进入待机状态:

STBY

最后,当应用程序关闭时,还会提交另外两个命令:

*RST
LOCAL

首先*RST重置机器以确保它处于与其启动时相同的状态(I.E.它没有运行且没有设置参数)。然后LOCAL设置重新启用用户交互的手动界面,并禁用通过串行端口的访问,直到REMOTE再次发出。

如您所见,在*IDN?之后和发送的第一个手动命令之前发出命令(在这种情况下,我们假设命令为OUT 30MV,60HZ)。问题是,每当我检查*IDN的输出是什么时,我有时会收到OUT 30MV,60HZ的输出。我可以看到我的代码或我用来操作机器的程序没有问题。有什么理由可以发生这种情况吗?

正如我所说,这个错误极难重现(我已经看过两次,也许四十次)。即便如此,在生产环境中任何类型的错误都是不可接受的,并且在我开始完全测试我的应用程序之前需要修复错误。我将继续尝试重现错误,以便提供一个示例,并希望进一步澄清问题所在。

编辑:

我还想澄清一点,我相当肯定这个bug不在我的应用程序本身的某个地方,因为代码本质上有些过于简单:

public string SubmitCommand()
    {
        if (_command_Input != "No further commands to input.")
        {
            string received;
            serialPort.WriteSerialConnection("STBY");
            received = serialPort.WriteSerialConnection(_command_Input);
            serialPort.WriteSerialConnection("OPER");

            //Controls Enabled:
            _input_IsEnabled = false;
            _user_Input_IsEnabled = true;
            _results_Input_IsEnabled = false;
            RaisePropertyChanged("Input_IsEnabled");
            RaisePropertyChanged("User_Input_IsEnabled");
            RaisePropertyChanged("Results_Input_IsEnabled");

            return received;
        }
        else
            return "";
    }

收到然后被操纵如下:

public bool SetOutput()
    {
        string inter1 = SubmitCommand();

        try
        {

            string[] lines = inter1.Split(Environment.NewLine.ToCharArray()).ToArray();
            _results_Changed = lines[2];
            RaisePropertyChanged("Results_Changed");
        }
        catch
        {
            _results_Changed = inter1;
            RaisePropertyChanged("Results_Changed");
        }
        return true;
    }

如果需要,我可以提供更多代码,但我目前看不到任何可能与手头问题相关的其他代码。

1 个答案:

答案 0 :(得分:1)

你很难诊断,你不喜欢的回答看起来像你想要的完全

通常,您需要确保程序与设备同步。可能的故障模式是驱动程序在先前连接的接收缓冲区中仍有未读数据。过时的数据也可能存在于设备的发送缓冲区中。当您开始备份时,您将读取该陈旧数据并假设它是对您的命令的响应。事实并非如此。您现在将永久不同步,始终读取作为对上一个命令的响应的陈旧数据。

如果没有照顾握手,这也很奇怪,设备通常注意这一点。

为避免发生意外,请按以下步骤初始化您的程序:

  • 调用Open()方法打开端口
  • 将RtsEnable和DtrEnable属性设置为true,以便设备始终看到一个允许其传输数据的良好信号
  • 睡眠约100毫秒,以允许设备发送仍然从之前的连接缓冲但由于握手已关闭而无法发送的任何数据
  • 调用DiscardInBuffer()以丢弃任何陈旧的响应字节。

您现在有合理的保证可以保持同步。