0

请原谅我,因为这将是一个相当长的帖子。我目前正在使用 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;
        }
    }
}

如您所见,当我打开一个连接时,在这种情况下,我通过向 SerialPortCom1写入命令来测试连接。*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. STBYOPER在 app.config 中设置,因为它们只会在应用程序的使用中添加不必要的步骤。出于安全原因,该STBY命令使机器处于待机状态。该OPER命令将其置于操作模式,并且设备开始在设置的参数下运行。

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

STBY

最后,当应用程序关闭时,又提交了两条命令:

*RST
LOCAL

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

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

正如我所说,该错误极难重现(我在大约 40 次运行中见过两次)。即便如此,这种类型的任何错误在生产环境中都是不可接受的,并且需要先修复错误,然后我才能开始完整地测试我的应用程序。我将继续尝试重现该错误,以便我可以提供一个示例,并希望进一步说明问题可能是什么。

编辑:

我还想澄清一下,我相当确定该错误不在我的应用程序本身的某个位置,因为代码本质上有些简单:

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;
    }

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

4

1 回答 1

1

你让这很难诊断,你不喜欢的反应看起来和你喜欢的完全一样。

一般来说,您需要确保您的程序与设备同步。一种可能的故障模式是驱动程序在接收缓冲区中仍有来自先前连接的未读取数据。设备的传输缓冲区中也可能存在陈旧数据。当您开始备份时,您将读取该陈旧数据并假设它是对您的命令的响应。不是。您现在将永久不同步,始终读取作为对上一个命令的响应的陈旧数据。

这也很奇怪,这在不考虑握手的情况下工作,设备通常注意这一点。

为避免意外,请像这样初始化您的程序:

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

你现在有一个合理的保证,你会同步。

于 2013-07-16T16:07:17.383 回答