質問

シリアルマグストライプリーダーおよびリレーボード(アクセス制御システム)と通信するためのWindowsサービスを作成しています。

別のプログラムが「中断」した後、コードが機能しなくなる(IOExceptionが表示される)問題が発生します。サービスと同じシリアルポートを開いてプロセスを実行します。

コードの一部は次のとおりです。

public partial class Service : ServiceBase
{
    Thread threadDoorOpener;
    public Service()
    {
        threadDoorOpener = new Thread(DoorOpener);
    }
    public void DoorOpener()
    {
        while (true)
        {
            SerialPort serialPort = new SerialPort();
            Thread.Sleep(1000);
            string[] ports = SerialPort.GetPortNames();
            serialPort.PortName = "COM1";
            serialPort.BaudRate = 9600;
            serialPort.DataBits = 8;
            serialPort.StopBits = StopBits.One;
            serialPort.Parity = Parity.None;
            if (serialPort.IsOpen) serialPort.Close();
            serialPort.Open();
            serialPort.DtrEnable = true;
            Thread.Sleep(1000);
            serialPort.Close();
        }
    }
    public void DoStart()
    {
        threadDoorOpener.Start();
    }
    public void DoStop()
    {
        threadDoorOpener.Abort();
    }
    protected override void OnStart(string[] args)
    {
        DoStart();
    }
    protected override void OnStop()
    {
        DoStop();
    }
}

私のサンプルプログラムはワークスレッドを正常に開始し、DTRを開いたり閉じたりして上げたりすると、Magストライプリーダーの電源が入り(1秒待機)、シャットダウン(1秒待機)などが発生します。

HyperTerminalを起動して同じCOMポートに接続すると、HyperTerminalはそのポートが現在使用中であることを通知します。ハイパーターミナルでEnterキーを繰り返し押して、再度開く 数回の再試行後に成功するポート。

これには、ワークスレッドでIOExceptionが発生するという効果がありますが、これは予想どおりです。ただし、ハイパーターミナルを閉じても、ワークスレッドで同じIOExceptionが発生します。唯一の解決策は、実際にコンピューターを再起動することです。

他のプログラム(ポートアクセスに.NETライブラリを使用していない)は、この時点では正常に動作しているようです。

これを引き起こしているものについて何か考えはありますか?

役に立ちましたか?

解決

@thomask

はい、ハイパーターミナルは実際にSetCommStateのDCBでfAbortOnErrorを有効にします。これは、SerialPortオブジェクトによってスローされるほとんどのIOExceptionについて説明します。一部のPC /ハンドヘルドには、デフォルトでオンになっているエラーフラグを無効にするUARTがあるため、シリアルポートの初期化ルーチンでクリアすることが必須です(Microsoftはこれを怠っています)。これをより詳細に説明するために、最近長い記事を書きました( this 興味がある場合)。

他のヒント

他の誰かのポートへの接続を閉じることはできません。次のコードは機能しません:

if (serialPort.IsOpen) serialPort.Close();

オブジェクトがポートを開かなかったため、ポートを閉じることはできません。

また、例外が発生した後でもシリアルポートを閉じて破棄する必要があります

try
{
   //do serial port stuff
}
finally
{
   if(serialPort != null)
   {
      if(serialPort.IsOpen)
      {
         serialPort.Close();
      }
      serialPort.Dispose();
   }
}

プロセスを中断できるようにする場合は、ポートが開いているかどうかを確認し、一定期間戻ってからもう一度試してください。

while(serialPort.IsOpen)
{
   Thread.Sleep(200);
}

アプリケーションでポートを開いたままにして、DtrEnableのオン/オフを切り替えてから、アプリケーションが閉じたらポートを閉じてみましたか?すなわち:

using (SerialPort serialPort = new SerialPort("COM1", 9600))
{
    serialPort.Open();
    while (true)
    {
        Thread.Sleep(1000);
        serialPort.DtrEnable = true;
        Thread.Sleep(1000);
        serialPort.DtrEnable = false;
    }
    serialPort.Close();
}

DTRのセマンティクスに精通していないため、これが機能するかどうかはわかりません。

信頼できる非同期通信を行う方法

ブロッキングメソッドを使用しないでください。内部ヘルパークラスにはいくつかの微妙なバグがあります。

セッション状態クラス、呼び出し間で共有されるバッファとバッファカーソルを管理するインスタンス、および try ... catch EndRead をラップするコールバック実装でAPMを使用しますコード>。通常の操作では、 try ブロックが最後に行うべきことは、 BeginRead()を呼び出して、次にオーバーラップするI / Oコールバックを設定することです。

問題が発生した場合、 catch はrestartメソッドへのデリゲートを非同期に呼び出す必要があります。コールバックの実装は、 catch ブロックの直後に終了する必要があります。これにより、再起動ロジックが現在のセッションを破棄し(セッション状態はほぼ確実に破損)、新しいセッションを作成できます。セッションを破棄して再作成することを防ぐため、再起動メソッドはセッション状態クラスに実装しないでください。

SerialPortオブジェクトが閉じられると(アプリケーションの終了時に発生します)、保留中のI / O操作が発生する可能性があります。その場合、SerialPortを閉じるとコールバックがトリガーされ、これらの条件下で EndRead は一般的な通信shitfitと見分けがつかない例外をスローします。セッション状態にフラグを設定して、 catch ブロックの再起動動作を禁止する必要があります。これにより、再起動方法が自然なシャットダウンを妨げなくなります。

このアーキテクチャは、SerialPortオブジェクトを予期せず保持しないように信頼できます。

restartメソッドは、シリアルポートオブジェクトのクローズと再オープンを管理します。 SerialPort オブジェクトで Close()を呼び出した後、 Thread.Sleep(5)を呼び出して手放す機会を与えます。他の何かがポートを取得する可能性があるため、ポートを再度開くときにこれに対処する準備をしてください。

HyperTerminalはうまく機能しないという結論に達したと思います。次のテストを実行しました:

  1. 「コンソールモード」でサービスを開始すると、デバイスのオン/オフの切り替えが開始されます(LEDで確認できます)。

  2. ハイパーターミナルを起動し、ポートに接続します。 デバイスはオンのままです(ハイパーターミナルはDTRを発生させます) サービスがイベントログに書き込み、ポートを開くことができない

  3. ハイパーターミナルを停止し、タスクマネージャーを使用して適切に閉じられていることを確認します

  4. デバイスはオフのままです(HyperTerminalはDTRを下げました)、アプリはポートを開けないというイベントログへの書き込みを続けます。

  5. 3番目のアプリケーション(共存する必要があるアプリケーション)を起動し、ポートに接続するように指示します。私はそうします。ここにエラーはありません。

  6. 上記のアプリケーションを停止します。

  7. VOILA、私のサービスが再び作動し、ポートが正常に開き、LEDがオン/オフになります。

ワークスレッドをこのように変更してみましたが、まったく同じ結果になりました。ハイパーターミナルが「ポートのキャプチャ」に成功すると、 (スレッドがスリープしている間)、サービスはポートを再度開くことができません。

public void DoorOpener()
{
    while (true)
    {
        SerialPort serialPort = new SerialPort();
        Thread.Sleep(1000);
        serialPort.PortName = "COM1";
        serialPort.BaudRate = 9600;
        serialPort.DataBits = 8;
        serialPort.StopBits = StopBits.One;
        serialPort.Parity = Parity.None;
        try
        {
            serialPort.Open();
        }
        catch
        {
        }
        if (serialPort.IsOpen)
        {
            serialPort.DtrEnable = true;
            Thread.Sleep(1000);
            serialPort.Close();
        }
        serialPort.Dispose();
    }
}

このコードは正常に機能しているようです。 Procomm Plusを使用してポートを開いたり閉じたりするコンソールアプリケーションでローカルマシンでテストしましたが、プログラムは刻々と動作し続けます。

    using (SerialPort port = new SerialPort("COM1", 9600))
    {
        while (true)
        {
            Thread.Sleep(1000);
            try
            {
                Console.Write("Open...");
                port.Open();
                port.DtrEnable = true;
                Thread.Sleep(1000);
                port.Close();
                Console.WriteLine("Close");
            }
            catch
            {
                Console.WriteLine("Error opening serial port");
            }
            finally
            {
                if (port.IsOpen)
                    port.Close();
            }
        }
    }

この回答はコメントになるまでに長くなりました...

プログラムがThread.Sleep(1000)にあり、ハイパーターミナル接続を開くと、ハイパーターミナルがシリアルポートを制御すると信じています。その後、プログラムが起動してシリアルポートを開こうとすると、IOExceptionがスローされます。

メソッドを再設計し、別の方法でポートのオープンを処理しようとします。

編集: それについては、プログラムが失敗したときにコンピューターを再起動する必要があります...

おそらく、プログラムが実際に閉じられていないために、タスクマネージャーを開いて、プログラムサービスが見つかるかどうかを確認してください。アプリケーションを終了する前に、必ずすべてのスレッドを停止してください。

サービスを「所有」しないようにする正当な理由はありますか?港?組み込みのUPSサービスを見てください。たとえば、COM1にUPSが接続されていると通知すると、そのポートに別れを告げることができます。ポートを共有するための強力な運用要件がない限り、同じことを行うことをお勧めします。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top