Frage

Ich bin ein Windows-Dienst für die Kommunikation Schreiben mit einem Serie Mag-Kartenleser und ein Relaiskarte (Zutrittskontrollsystem).

ich auf Probleme stoßen, wo der Code nicht mehr funktioniert (i IOExceptions bekommen) nach dem anderen Programm den Prozess „unterbrochen“ wurde durch die gleiche serielle Schnittstelle als meinen Dienst zu öffnen.

Ein Teil des Codes ist wie folgt:

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

Mein Beispielprogramm startet erfolgreich den Arbeit-Thread, und das Öffnen / Schließen und Anheben von DTR verursacht meinen Mag-Kartenleser an der Macht (1 s warten), heruntergefahren (warten 1 Sekunde) und so weiter.

Wenn ich Hyper-Terminal starten und eine Verbindung mit dem gleich COM-Port, Hyper-Terminal sagt mir, der Hafen ist derzeit im Einsatz. Wenn ich in Hyper-Terminal wiederholt ENTER drücken, um zu versuchen, wieder zu öffnen der Hafen schon nach wenigen Wiederholungen erfolgreich sein wird.

Dies hat den Effekt von IOExceptions in meiner Arbeit-Thread verursacht, was zu erwarten ist. Aber selbst wenn ich Hyper-Terminal schließen, bekomme ich immer noch die gleiche IOException in meiner Arbeit-Thread. Das einzige Heilmittel ist eigentlich den Computer neu zu starten.

Andere Programme (die .NET-Bibliotheken nicht Port-Zugriff verwendet werden kann) scheinen normalerweise an dieser Stelle zu arbeiten.

Alle Ideen, was verursacht das?

War es hilfreich?

Lösung

@thomask

Ja, ist Hyper in der Tat ermöglichen fAbortOnError in SetCommState des DCB, die durch das Objekt geworfen Serialport für die meisten der IOExceptions erklärt. Einige PCs / Handhelds haben auch UARTs, dass der Abbruch auf Fehler-Flag haben standardmäßig aktiviert - also ist es zwingend notwendig, dass eine serielle init-Routine des Port löscht es (was Microsoft zu tun vernachlässigt). Ich schrieb einen langen Artikel vor kurzem dies näher zu erläutern (siehe this , wenn Sie interessiert sind).

Andere Tipps

Sie können nicht jemand andere Verbindung zu einem Port schließen, der folgende Code wird nie funktionieren:

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

Da Ihr Objekt, das Sie nicht den Port öffnen können sie nicht schließen.

Auch sollten Sie die serielle Schnittstelle schließen und entsorgen auch nach Ausnahmen auftreten

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

Wenn Sie den Prozess wollen unterbrechbare sein, dann sollten Sie prüfen, ob der Port offen ist, und dann für einen Zeitraum wieder aus und versuchen Sie dann erneut, so etwas wie.

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

Haben Sie versucht, den Port in der Anwendung öffnen zu verlassen, und Drehen nur DTREnable ein / aus, und dann Schließen der Öffnung, wenn die Anwendung geschlossen wird? das heißt:

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

Ich bin mit DTR Semantik nicht vertraut, so dass ich weiß nicht, ob das funktionieren würde.

Wie zuverlässig async Comms tun

Sie die blockierenden Methoden nicht verwenden, die interne Hilfsklasse hat einige subtile Fehler.

Mit APM mit einem Sitzungszustandsklasse, Instanzen, von denen einen Puffer zu verwalten und puffern Cursor über Anrufe geteilt, und eine Callback-Implementierung, die EndRead in einem try...catch wickelt. Im Normalbetrieb ist das letzte, was der try Block tun soll die nächsten überlappte I / O-Rückruf mit einem Rufaufbau bis BeginRead().

Wenn die Dinge schief gehen, catch sollte einen Delegierten zu einem Neustart Methode asynchron aufrufen. Die Callback-Implementierung sollte unmittelbar nach dem catch Block verlassen, so dass die Neustart-Logik der aktuelle Sitzung zerstören kann (Sitzungsstatus an Sicherheit grenzender Wahrscheinlichkeit beschädigt ist) und eine neue Sitzung erstellen. Der Neustart-Methode muss nicht auf der Sitzungsstatus-Klasse implementiert werden, da diese es von der Zerstörung verhindern würde und Neuerstellung der Sitzung.

Wenn das Serialport-Objekt geschlossen wird (was passieren wird, wenn die Anwendung beendet) dort auch eine anstehende E / A-Operation sein kann. Wenn dies so ist, wird den Serialport schließt den Rückruf auslösen, und unter diesen Bedingungen wird EndRead eine Ausnahme aus, die von einer allgemeinen Comms shitfit nicht zu unterscheiden ist. Sie sollen eine Flagge in der Sitzung Zustand versetzen den Neustart Verhalten im catch Block zu hemmen. Dies wird Ihre Anlaufart stören natürliche Abschaltung stoppen.

Diese Architektur kann, nicht als Grundlage auf das Serialport-Objekt zu halten unerwartet.

Die Neustart-Methode verwaltet die Schließung und Wiedereröffnung des seriellen Port-Objekts. Nachdem Sie Close() auf dem SerialPort Objekt aufrufen, rufen Thread.Sleep(5) es eine Chance zu geben, loszulassen. Es ist möglich, etwas anderes, den Hafen zu greifen, so bereit sein, mit dieser während der Wiedereröffnung sie zu behandeln.

Ich glaube, ich bin zu dem Schluss gekommen, dass Hyper-Terminal nicht gut spielen. Ich habe den folgenden Test durchgeführt:

  1. meinen Dienst startet in "Konsolenmodus", beginnt er das Gerät Ein- / Ausschalten (i durch seine LED sagen kann).

  2. Starten Sie Hyper-Terminal und dem Hafen verbinden. Das Gerät bleibt auf (Hyper-Terminal erhöht DTR) Mein Service schreibt in das Ereignisprotokoll, dass es nicht den Port öffnen kann

  3. Stop Hyper-Terminal, ich es überprüfen richtig Task-Manager geschlossen wird mit

  4. Das Gerät bleibt ausgeschaltet (Hyper-Terminal hat DTR gesenkt), meine app hält in das Ereignisprotokoll zu schreiben, sagen, es nicht den Port öffnen kann.

  5. Ich beginne eine dritte Anwendung (die, die ich mit koexistieren müssen), und melden Sie es mit dem Anschluss zu verbinden. I tut dies. Keine Fehler hier.

  6. Ich halte die oben genannten Anwendungs.

  7. VOILA, meinen Dienst Tritte wieder öffnet sich der Port erfolgreich, und die LED geht AN / AUS.

Ich habe versucht, den Arbeit-Thread wie dies zu ändern, mit dem exakt gleichen Ergebnis. Sobald einmal Hyper-Terminal in „Einfangen des Hafens“ erfolgreich ist (während mein Thread schläft), wird mein Service nicht in der Lage sein, wieder den Anschluss zu öffnen.

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

Dieser Code scheint richtig zu funktionieren. Ich habe es auf meinem lokalen Rechner in einer Konsolenanwendung getestet, Procomm Plus-mit dem Port zu öffnen / schließen, und das Programm hält auf tickt.

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

Diese Antwort bekam zu lange um einen Kommentar zu sein ...

Ich glaube, dass, wenn Ihr Programm in einem Thread.Sleep (1000) ist und Sie Ihre Hyper-Terminal-Verbindung öffnen, der Hyper-Terminal Steuerung über die serielle Schnittstelle erfolgt. Wenn Ihr Programm dann aufwacht und zu versuchen, den seriell Port zu öffnen, eine IOException geworfen wird.

Redesign Ihre Methode und versucht, die Öffnung des Hafens in einer anderen Art und Weise zu handhaben.

EDIT: Über, dass Sie Ihren Computer neu starten, wenn Ihr Programm nicht ...

Das wahrscheinlich, weil Ihr Programm wirklich geschlossen Nichtstellung öffnen Sie Ihre Taskmanager und sehen, ob Sie Ihren Programm-Service finden. Achten Sie darauf, alle Threads zu stoppen, bevor die Anwendung beendet wird.

Sie haben einen guten Grund, Ihren Dienst zu halten von „Besitz“ Hafen? Schauen Sie sich die Einbau-USV-Dienst - wenn Sie sagen, es gibt eine UPS ist an, sagen wir, COM1, können Sie diesen Port zum Abschied küssen. Ich würde vorschlagen, dass Sie das gleiche tun, wenn es eine starke betriebliche Anforderung ist, den Hafen zu teilen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top