0

かなり長文になりますのでお許しください。現在、C# で SerialPort クラスを使用して、Fluke 5500A と呼ばれるデバイスと通信するアプリケーションを作成しています。私は過去に、デバイスがコマンドを発行して出力するものを返すのにかかる時間がせいぜい予測不可能であるため、多くの問題を抱えていました。昨日、ここで質問しました: System.Timers.Timer の使用法質問への回答は素晴らしく、ほとんどの場合、完全に機能しているように見えます。例として、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>

クラス">"では、そのトークンが見つかるまで SerialPort.ReadLine() が終了しないように NewLine プロパティとして設定しました。クラス自体が例外をスローしたことは一度もありませんが、デバッグ中に、例外がスロー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

最後に、アプリケーションが閉じられると、さらに 2 つのコマンドが送信されます。

*RST
LOCAL

最初*RSTにマシンをリセットして、電源を入れたときと同じ状態であることを確認します (つまり、動作しておらず、パラメーターが設定されていません)。次にLOCAL、ユーザー操作用の手動インターフェイスを再度有効にし、REMOTEもう一度発行されるまでシリアル ポート経由のアクセスを無効にします。

ご覧のとおり、コマンドは、*IDN?送信された最初の手動コマンドの前後に発行されます (この場合、コマンドは であると仮定しますOUT 30MV,60HZ)。*IDN問題は、出力が何であるかを確認するたびにの出力を受け取ることがありOUT 30MV,60HZますが、コードやマシンの操作に使用している手順に問題が見られないことです。これが起こる理由はありますか?

私が言ったように、エラーを再現するのは非常に困難です (おそらく 40 回の実行で 2 回見たことがあります)。それでも、このタイプのエラーは本番環境では受け入れられず、アプリケーション全体のテストを開始する前にエラーを修正する必要があります。エラーの再現を試み続けるので、例を示し、問題の原因をさらに明確にすることができれば幸いです。

編集:

また、コードは本質的にやや単純化されているため、バグがアプリケーション自体のどこかにあるのではないと確信していることを明確にしたいと思います。

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 に答える