かなり長文になりますのでお許しください。現在、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 で設定されますSTBY
。OPER
この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;
}
必要に応じてさらにコードを提供できますが、現在、当面の質問に関連する可能性のある他のコードを確認できません。