[PYTHON] Verwalten Sie Statusübergänge und kommunizieren Sie mit intelligenten Zählern

Einführung

Im vorherigen Artikel Smart Meter Measurement Logger mit Worker Thread Design Pattern wird der Langzeitbetrieb durch Neustart des für die Messung verantwortlichen Objekts (Threads) ausgeführt. Es war ein Ansatz, der es möglich machte. Dies ist jedoch ein letzter Ausweg, und es wird angenommen, dass der Thread stabil arbeitet. Auch wenn der Thread ausgeführt wird, ist dies keine Gegenmaßnahme, wenn er durch eine bestimmte Verarbeitung blockiert wird.

Insbesondere bei der Kommunikation mit einem Smart Meter über Route B müssen Sie mehrere Schritte ausführen, z. B. Scannen und Authentifizieren. Einige Schritte können fehlschlagen oder eine Zeitüberschreitung verursachen. Sie müssen über Verarbeitungen wie erneutes Versuchen oder Zurücksetzen und Neustarten nachdenken, aber die zu erledigenden Aufgaben ändern sich je nach Situation. Wenn Sie sie also nicht gut organisieren und implementieren, werden Sie ewig warten. Es ist wahrscheinlicher, dass Sie fortfahren oder unerwartete Daten erhalten und abstürzen.

Dieses Mal werde ich die Kommunikation mit dem Smart Meter von Route B vorstellen, da ich den Zustandsübergang verwaltet und implementiert habe.

Aus Quellcode keilog / broute.py

Schreiben Sie ein Zustandsübergangsdiagramm

Zunächst ist es notwendig, richtig zu organisieren, was in welcher Situation zu tun ist, und es dann auf leicht verständliche Weise zu programmieren. In der run () - Thread-Schleife der BrouteReader-Klasse wird die Verarbeitung grob mit den folgenden Statusübergängen ausgeführt. Das blaue Quadrat stellt den Status dar. Abhängig vom Ausführungsergebnis der in diesem Status aufgerufenen Funktion werden Wiederholungsversuche oder Statusübergänge durchgeführt. Dies ist nicht immer die richtige Antwort, und andere Methoden sind möglich, aber es funktioniert gut, um Momentanleistung und integrierte Leistung zu erhalten.

Broute状態遷移図.001.jpeg

Implementierungsmethode

Der Code, der dies implementiert, lautet wie folgt. (Eigentlich etwas komplizierter)

class BrouteReader ( Worker ):
    
    # <Kürzung>

    def run( self ):
        while not self.stopEvent.is_set():
            if self.state == self._STATE_INIT:
                self._open()

            elif self.state == self._STATE_OPEN:
                self._setup()

            elif self.state == self._STATE_SETUP:
                self._scan()

            elif self.state == self._STATE_SCAN:
                self._join()

            elif self.state == self._STATE_JOIN:
                if time_has_come(): #Vergleichen Sie tatsächlich die letzte Aktualisierungszeit mit der aktuellen Zeit
                    self._sendto('Eigenschaftswert-Anforderungsnachricht')
                dataframe = self._receive() #Empfangen Sie die Nachricht erhalten()Zeitüberschreitung in 1 Sekunde
                if dataframe:
                    self._accept(dataframe) #Verarbeiten Sie die empfangene Nachricht

                if long_time_has_gone():
                    logger.error('Ich habe lange keine Nachricht mehr erhalten')
                    self._term()
                    self._close()
                    self.state = self._STATE_INIT
                    time.sleep(5)

        self._term() #Smart Meter abklemmen
        self._close() #WiSun-Geräte öffnen
        logger.info('[STOP]')

Wie im vorherigen Artikel beschrieben, wird die Schleife innerhalb von run () wiederholt, sofern nicht stopEvent festgelegt ist.

Innerhalb der Schleife wird der Code, der zu einem der Zustände gehört, abhängig vom aktuellen Zustand ausgeführt, der durch self.state dargestellt wird. Hier wird nur die Funktion aufgerufen, aber die erforderliche Verarbeitung wird in der Funktion ausgeführt und self.state wird neu geschrieben, um den Status zu aktualisieren. In der nächsten Schleife wird dann der Code im nächsten Übergangszustand ausgeführt. Dies ist der Grundablauf. Die Wiederholungsverarbeitung wird auch in scan () und join () definiert. Die Fälle sind je nach Zustand in der Regel lang, daher halte ich es für wichtig, sie so einfach und klar wie möglich zu halten.

Ich werde den Ablauf der Operation ein wenig erklären. Es scheint, dass es ungefähr 1 Sekunde dauert, bis das Smart Meter auf die von sendto () gesendete Nachricht reagiert. Wenn time_has_come () in einer Schleife festgelegt ist und eine Nachricht gesendet wird, die den Eigenschaftswert des Smart Meters anfordert, kommt die Antwort auf die Anforderungsnachricht nicht sofort an, sodass sie in der nächsten oder nächsten Schleife anstatt in dieser Schleife verarbeitet wird. Wird besorgt. Daher stimmt die vom Empfang empfangene Antwort- oder Benachrichtigungsnachricht nicht immer mit der unmittelbar zuvor gesendeten Anforderung überein, und Sie können nicht erkennen, was darin enthalten ist, ohne nach innen zu schauen. accept () muss jede Nachricht verarbeiten, damit sie ordnungsgemäß verarbeitet werden kann.

Beachten Sie außerdem, dass für jede Zeitüberschreitung jede Funktion definiert werden muss. Andernfalls wird die Schleife dort möglicherweise blockiert und Sie können möglicherweise nicht fortfahren. Eine der Wurzeln des Blocks ist serial.readline (). Für diese Funktion muss ein Timeout (ca. 1 Sekunde) festgelegt und die nachfolgende Verarbeitung unter dieser Annahme programmiert werden. Stellen Sie dann eine Zeitüberschreitung ein, damit der Prozess nicht ewig wartet, selbst wenn er nur auf eine Wiederholungsschleife oder "OK" wartet.

Unten finden Sie Beispiele für open () und setup ().

    def _open( self ):
        if self.wisundev.open():
            self.state = self._STATE_OPEN
        else:
            #Im Fehlerfall 5 Sekunden anhalten. Schnelle Endlosschleifen belegen die CPU nicht mehr. (Gleich unten)
            time.sleep(5)

    def _setup( self ):
        #zurücksetzen
        if self.wisundev.reset():
            pass
        else:
            time.sleep(5)
            return False
 
        #B Legen Sie die Root-ID und das Kennwort auf dem Gerät fest (im Register registriert).
        if self.wisundev.setup( self.broute_id, self.broute_pwd ):
            self.state = self._STATE_SETUP
        else:
            time.sleep(5)

Unter den Funktionen wird die Funktion mit demselben Namen wie der WiSun-Gerätetreiber aufgerufen. Wenn die Ausführung jedoch erfolgreich ist, wird gleichzeitig der Statusübergang ausgeführt. Wenn ein Fehler auftritt, schlafen Sie, bevor Sie die Funktion beenden. Andernfalls wird es weiterhin mit hoher Geschwindigkeit wiederholt, wenn das Gerät getrennt wird, und es werden weiterhin Fehler protokolliert und die CPU belegt. Wenn Sie das oben erwähnte Timeout von serial.readline () zu kurz einstellen, läuft die Schleife möglicherweise mit hoher Geschwindigkeit. Seien Sie also vorsichtig.

Zur Verbesserung der Stabilität

Durch die Verwaltung von Zustandsübergängen und die Steuerung der Ausführung wird meiner Meinung nach klarer, was zu tun ist, und es ist weniger wahrscheinlich, dass Fehler in der Logik gemacht werden. Und selbst wenn ein Problem auftritt, lässt sich der Code leicht beheben. Es ist immer noch notwendig, die Genauigkeit jeder Funktion für einen stabilen Betrieb zu verbessern, aber da es einfach ist, das Problem zu isolieren, denke ich, dass wir die Gegenmaßnahmen genau bestimmen können.

In Bezug auf die Genauigkeit ist es erforderlich, ausreichende Maßnahmen gegen unbeabsichtigte Ereignisse und die Verarbeitung zu ergreifen, wenn eine Nachricht empfangen wird. Wenn Sie den Index des Arrays angeben, ohne die Möglichkeit anderer Ereignisse oder Meldungen zu berücksichtigen, steigt das Fehlerrisiko. Da es sich bei den meisten zu verarbeitenden Daten um eine hexadezimale HEX-Zeichenfolge handelt, prüfen wir, ob sie als hexadezimale Zeichenfolge korrekt ist. Abhängig von der Umgebung scheint diese Art von Fehler jedoch nicht sehr häufig aufzutreten.

Andererseits ist es relativ häufig, dass die Kommunikation verloren geht. Wenn am Ende des obigen Codelaufs () die Nachricht für einen bestimmten Zeitraum nicht empfangen wird, wird der Vorgang der Rückkehr zu INIT ausgeführt, um dieses Problem zu beheben. Die PANA-Sitzung hat ein Ablaufdatum und erfordert eine regelmäßige erneute Authentifizierung, um den Schlüssel zu aktualisieren. Der hier verwendete RL7023 kann sich jedoch automatisch erneut authentifizieren. Beim Betrachten des Protokolls gab es Hinweise darauf, und die erneute Authentifizierung war erfolgreich, aber es gab Fälle, in denen die Kommunikation danach unterbrochen wurde. Es ist sehr wahrscheinlich, dass die Kommunikation aus irgendeinem Grund unterbrochen wird. Daher ist diese Maßnahme von wesentlicher Bedeutung. Ich weiß nicht, was mit anderen WiSun-Geräten ist, aber wenn Sie sich erneut authentifizieren müssen, können Sie regelmäßig rejoin () durchführen.

abschließend

Als ich dieses Mal ein Programm erstellte, das Zustandsübergänge verwaltet, stellte ich fest, dass es verschiedene Punkte gibt, wie die Notwendigkeit einer angemessenen Zeitüberschreitung und die Beachtung von Endlosschleifen für schnelle Wiederholungsversuche. Wenn ein Programmfehler oder -fehler dazu führt, dass Funkwellenstöße übertragen werden, verursacht dies Probleme für umgebende Geräte, die dasselbe Band verwenden, und im schlimmsten Fall kann dies gegen das Funkgesetz verstoßen. Es gibt. Ich dachte, ich müsste aufpassen, dass ich nicht unachtsam Radiowellen aussende. Ich würde es begrüßen, wenn Sie auf andere Punkte hinweisen könnten, die Sie beachten sollten.

Dann machte ich mir unerwartet Sorgen, den Staat zu benennen. Immerhin ist es der gleiche wie der Funktionsname, aber bitte lassen Sie mich wissen, ob es eine gute Benennungsmethode gibt.

Referenz

Recommended Posts

Verwalten Sie Statusübergänge und kommunizieren Sie mit intelligenten Zählern
Kommunizieren Sie mit FX-5204PS mit Python und PyUSB
Kommunizieren Sie mit gRPC zwischen Elixir und Python
Implementieren Sie ein Modell mit Status und Verhalten
Verwalten Sie die Umgebung deklarativ mit Nix und Home-Manager
Django: User Agent aufzeichnen und mit Admin verwalten
Verwalten Sie Python-Laufzeitpakete und Entwicklungsumgebungspakete mit Poetry