[PYTHON] Nachdem ich die Watson IoT Platform-Anwendung mit Flask implementiert hatte, war ich süchtig nach MQTT-Verbindungen

Hier ist eine Zusammenfassung dessen, was ich süchtig danach war, Watson IoT Platform-Anwendungen mit Flask zu implementieren. Da ich Flask nicht sehr gut verstand, bedauerte ich, dass ich es richtig verstehen und verwenden musste.

Umgebung

Für Watson IoT Platform-Anwendungen stehen mehrere Programmierschnittstellen zur Verfügung. Diesmal habe ich jedoch eine Anwendung erstellt, die den Gerätestatus abonniert und den Verbindungsstatus von IoT-Geräten mit MQTT meldet.

Schließlich wollte ich den Code an IBM Cloud Foundry senden, also habe ich beschlossen, ihn in Flask zu implementieren, aber ich sollte hier ein solides Verständnis von Flask haben ...

Python 2.7 paho-mqtt 1.5.0 Flask 1.1.2

Vorbereitung

Erstellen einer Anwendung mit IBM Cloud Foundry

Erstellen Sie eine öffentliche Anwendung, indem Sie auf den folgenden Artikel verweisen. https://cloud.ibm.com/docs/cloud-foundry-public?topic=cloud-foundry-public-getting-started

Holen Sie sich den Beispielcode, der in den obigen Schritten ausgegeben wird, von git und ändern Sie ihn so, dass er vorerst auf HTTP-Anforderungen antworten kann.

hello.py


from flask import Flask
import os

app = Flask(__name__, static_url_path='')
port = int(os.getenv('PORT', 8000))

@app.route('/')
def root():
    return 'Hello, world'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=port, debug=True)

Versuchen Sie, dieses Programm lokal auszuführen.

# python hello.py
 * Serving Flask app "hello" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://0.0.0.0:8000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 210-659-291

Ich werde versuchen, zur Bestätigung mit Curl darauf zuzugreifen. Es scheint, dass Sie auf Port 8000 erfolgreich auf HTTP-Anfragen antworten können.

# curl localhost:8000
Hello, world

Sie haben jetzt eine Anwendung, die zumindest auf HTTP-Anforderungen antworten kann.

Erstellen von API-Schlüsseln und -Token für die Watson IoT-Plattform

Die Watson IoT Platform verfügt über einen MQTT-Client zum Veröffentlichen von Informationen, die von IoT-Geräten gesammelt wurden (es gibt zwei Methoden, die als Geräte bzw. Gateways bezeichnet werden). Er empfängt und verarbeitet die von IoT-Geräten gesammelten Daten und überprüft den Status von IoT-Geräten. Zwei Arten von Client-Verbindungen sind möglich: MQTT-Client (Anwendung) zu betreiben und.

Stellen Sie diesmal eine Verbindung als Anwendung her und abonnieren Sie das Thema, um eine Benachrichtigung über Änderungen des Gerätestatus zu erhalten, die von Watson IoT Platform verwaltet werden. Weitere Informationen zur Verbindung finden Sie unter dem folgenden Link. https://www.ibm.com/support/knowledgecenter/en/SSQP8H/iot/platform/applications/mqtt.html

Erstellen Sie zunächst einen API-Schlüssel und ein Token, um eine MQTT-Verbindung zur Watson IoT-Plattform als Anwendung herzustellen. Bitte klicken Sie auf den unten stehenden Link, um fortzufahren. https://www.ibm.com/support/knowledgecenter/en/SSQP8H/iot/platform/applications/app_dev_index.html

Wenn Sie mithilfe des erstellten API-Schlüssels und -Tokens eine MQTT-Verbindung herstellen, sollten Sie jedes Mal über Änderungen des Gerätestatus benachrichtigt werden können, wenn ein IoT-Gerät eine Verbindung zur Watson IoT-Plattform herstellt oder diese trennt.

Ich habe die Anwendung implementiert ...

Fügen Sie den Code für die Verbindung zur Watson IoT-Plattform mit dem erstellten API-Schlüssel und dem Token zu hello.py mithilfe von Flask hinzu.

Um eine MQTT-Verbindung zur Watson IoT Platform herzustellen, müssen Sie die Client-ID, die Benutzer-ID und das Kennwort angeben. Erstellen Sie diese, indem Sie die Organisations-ID, den API-Schlüssel und das Token verketten, die von Watson IoT Platform zugewiesen wurden, und zwar jeweils im angegebenen Format. Dieses Format wird in der MQTT-Authentifizierung unter dem folgenden Link ausführlich beschrieben. https://www.ibm.com/support/knowledgecenter/en/SSQP8H/iot/platform/applications/mqtt.html

Im folgenden Beispielcode lautet die Organisations-ID "oooooo ", der API-Schlüssel" kkkkkkkkkk "und das Token" ttttttttttttttttttt ". Sie müssen ihn jedoch auf den von Watson IoT Platform bereitgestellten Wert ändern, um den Code auszuführen.

hello.py


from flask import Flask
import os

import paho.mqtt.client as mqtt
from datetime import datetime

def on_connect(client, userdata, flags, respons_code):
    client.on_message = on_message
    client.subscribe('iot-2/type/+/id/+/mon')
    print(datetime.now().strftime("%Y/%m/%d %H:%M:%S") + ": mqtt connected")

def on_disconnect(client, userdata, rc):
    print(datetime.now().strftime("%Y/%m/%d %H:%M:%S") + ": mqtt disconnected")

def on_message(client, userdata, msg):
    print(datetime.now().strftime("%Y/%m/%d %H:%M:%S") + ": mqtt message arrived")

client = mqtt.Client(client_id='a:oooooo:appl1', protocol=mqtt.MQTTv311)
client.username_pw_set('a-oooooo-kkkkkkkkkk', password='tttttttttttttttttt')
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.connect('de.messaging.internetofthings.ibmcloud.com', 1883, 120)
client.loop_start()

app = Flask(__name__, static_url_path='')
port = int(os.getenv('PORT', 8000))

@app.route('/')
def root():
    return "Hello, world"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=port, debug=True)

Ich beschloss, einen solchen Code vorzubereiten und den Betrieb zu überprüfen. Es wird erwartet, dass Sie den Empfang der MQTT-Nachricht bestätigen können, während Sie auf HTTP-Anforderungen von Port 8000 antworten.

Aber wenn Sie den Code tatsächlich ausführen ...

# python hello.py
 * Serving Flask app "hello" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://0.0.0.0:8000/ (Press CTRL+C to quit)
 * Restarting with stat
2020/05/13 18:13:26: mqtt connected
 * Debugger is active!
 * Debugger PIN: 210-659-291
2020/05/13 18:13:26: mqtt message arrived
2020/05/13 18:13:27: mqtt disconnected
2020/05/13 18:13:27: mqtt connected
2020/05/13 18:13:27: mqtt message arrived
2020/05/13 18:13:28: mqtt connected
2020/05/13 18:13:28: mqtt disconnected
2020/05/13 18:13:29: mqtt message arrived
2020/05/13 18:13:30: mqtt disconnected
2020/05/13 18:13:30: mqtt connected
2020/05/13 18:13:30: mqtt message arrived
2020/05/13 18:13:31: mqtt disconnected
2020/05/13 18:13:31: mqtt connected

Auf diese Weise wurde die Verbindung mit MQTT zwar einmal hergestellt, aber sofort unterbrochen. Danach stellt paho-mqtt aufgrund einer unerwarteten Unterbrechung der Sitzung automatisch die Verbindung wieder her, trennt jedoch auch sofort die Verbindung und stellt dann die Verbindung wieder her und trennt sie wiederholt.

Wenn der API-Schlüssel oder das Token falsch ist, sollte die MQTT-Verbindung zunächst fehlschlagen, und ich habe versucht, das Nachrichtenintervall anzupassen, um festzustellen, ob das MQTT-Keepalive-Zeitlimit abgelaufen ist. Dies führte jedoch auch nicht zu einer Verbesserung. Es war.

Ich fragte mich, was mit dem Kommunikationspfad nicht stimmte und ließ ihn einige Tage lang stehen, aber die Symptome änderten sich nicht.

Lösung

Als ich ratlos war und eine Frage an stackoverflow stellte, rieten mir zwei Personen, dass die Client-ID möglicherweise dupliziert wird.

Natürlich können Sie keine Verbindung zu MQTT Broker mit derselben Client-ID herstellen. Ich dachte, es sei wahr, aber ich kann mir keinen Grund für die Vervielfältigung vorstellen. hello.py führt jeweils nur einen Prozess aus. Die Regel lautet auch, dass die Client-ID durch Verketten der Organisations-ID und des API-Schlüssels generiert wird, die Organisations-ID den API-Schlüssel jedoch nicht für andere Zwecke umleitet. Aus irgendeinem Grund dachte ich, dass alte Verbindungsinformationen möglicherweise auf der Watson IoT-Plattform verbleiben und ich keine Verbindung herstellen konnte. Deshalb habe ich versucht, einen neuen API-Schlüssel zu generieren, aber das Symptom ändert sich nicht, selbst wenn ich ihn verwende. tat.

Anscheinend gab ich es auf zu denken, dass die doppelte Client-ID nicht die Ursache war, aber als ich mir die Ausgabe von hello.py noch einmal ansah, sah ich die Zeichenfolge 'Neustart mit stat'.

Was ist das übrigens?

Ich war dem MQTT-Teil bisher misstrauisch, aber ich beschloss, diese mysteriöse Botschaft von Flask zu untersuchen.

Ergebnis,

Ich habe das verstanden.

Wenn ich es überprüfe,

# ps -f | grep hello.py
  501 20745  2576   0 10:39PM ttys005    0:00.35 /System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python hello.py
  501 20748 20745   0 10:39PM ttys005    0:00.36 /System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python /Users/(Kürzung)/hello.py

Es gibt sicherlich zwei hello.py-Prozesse, die in einer Eltern-Kind-Beziehung ausgeführt werden.

In dieser Situation überzeugt die doppelte Client-ID. Der übergeordnete Prozess (pid: 20745) und der untergeordnete Prozess (pid: 20748) verwenden ungefähr zur gleichen Zeit dieselbe Client-ID, um die MQTT-Verbindung herzustellen. Nun, ich verstehe.

Wie vermeiden Sie doppelte Client-IDs? Es ist einfach, den Debug-Modus zu deaktivieren, aber es ist eine Schande, dass Sie die nützlichen Funktionen, die bereitgestellt werden, nicht verwenden können. Weitere Untersuchungen ergaben, dass der übergeordnete Prozess "WEERKZUG_RUN_MAIN" war, bevor der untergeordnete Prozess aufgerufen wurde. Es scheint in Ordnung zu sein, wenn Sie diese Umgebungsvariable überprüfen und im übergeordneten Prozess keine MQTT-Verbindung herstellen.

Codekorrektur und Funktionsprüfung

Basierend auf dem obigen Verständnis des Debug-Modus

--Überprüfen Sie die Umgebungsvariable 'WEERKZUG_RUN_MAIN', bevor Sie MQTT verbinden, und stellen Sie nur eine Verbindung her, wenn diese Umgebungsvariable definiert ist.

Ich habe hello.py wieder so modifiziert. Die zweite Lösung besteht darin, dass der übergeordnete Prozess 1, wenn er eine Codeänderung erkennt und neu lädt, den untergeordneten Prozess 1 stoppt und einen neuen untergeordneten Prozess 2 startet, jedoch nicht darauf wartet, dass die MQTT-Verbindung des untergeordneten Prozesses 1 getrennt wird. Dasselbe passiert wahrscheinlich, wenn der untergeordnete Prozess 2 gestartet wird. Deshalb habe ich beschlossen, ihn für alle Fälle hinzuzufügen.

hello.py


from flask import Flask
import os
import threading
import json

import paho.mqtt.client as mqtt
from datetime import datetime

def on_connect(client, userdata, flags, respons_code):
    client.on_message = on_message
    client.subscribe('iot-2/type/+/id/+/mon')
    print(datetime.now().strftime("%Y/%m/%d %H:%M:%S") + ": mqtt connected")

cond = threading.Condition()
notified = False

def on_disconnect(client, userdata, rc):
    global notified
    print(datetime.now().strftime("%Y/%m/%d %H:%M:%S") + ": mqtt disconnected")
    with cond:
        notified = True
        cond.notify()

status = 'Unknown'

def on_message(client, userdata, msg):
    global status
    response_json = msg.payload.decode("utf-8")
    response_dict = json.loads(response_json)
    if(response_dict["ClientID"] == 'Geben Sie hier die Client-ID des Geräte-Gateways an, das Sie verfolgen möchten'):
        if( response_dict["Action"] == "Disconnect" ):
            status = "Disconnected"
        elif( response_dict["Action"] == "Connect" ):
            status = "Connected"

if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
    client = mqtt.Client(client_id='a:oooooo:appl1', protocol=mqtt.MQTTv311)
    client.username_pw_set('a-oooooo-kkkkkkkkkk', password='tttttttttttttttttt')
    client.on_connect = on_connect
    client.on_disconnect = on_disconnect
    client.connect('de.messaging.internetofthings.ibmcloud.com', 1883, 120)
    client.loop_start()

app = Flask(__name__, static_url_path='')
port = int(os.getenv('PORT', 8000))

@app.route('/')
def root():
    return "Status: " + status

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=port, debug=True)

    if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
        client.loop_stop()
        client.disconnect()
        with cond:
            if( not notified ):
                cond.wait()

Ich habe den Code wie oben geändert und erneut versucht, ihn auszuführen.

# python hello.py
 * Serving Flask app "hello" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://0.0.0.0:8000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 210-659-291
2020/05/13 23:04:28: mqtt connected
127.0.0.1 - - [13/May/2020 23:04:38] "GET / HTTP/1.1" 200 -

Wiederholtes Verbinden / Trennen hat aufgehört!

Wenn ich eine HTTP-Anfrage mit Curl mache,

# curl localhost:8000
Status: Disconnected

Es scheint, dass die Verarbeitung von MQTT-Nachrichten ebenfalls gut funktioniert.

Versuchen Sie, ein registriertes IoT-Gerät als Testversion anzuschließen.

Dieses Mal werden wir mosquitto_sub verwenden, um den Gerätebefehl zu abonnieren und das IoT-Gerät zu ersetzen. Ausführliche Informationen zum Einrichten und Herstellen einer Verbindung finden Sie in dem unten verlinkten Artikel. https://qiita.com/kuraoka/items/5380f6b5e97e8cd1ad98

# mosquitto_sub -h de.messaging.internetofthings.ibmcloud.com -u use-token-auth -P "tttttttttttttttttt" -i (hello.Client-ID von py überwacht) -t 'iot-2/type/(type)/id/(id)/cmd/control/fmt/json'

Versuchen Sie bei der oben beschriebenen MQTT-Verbindung vom IoT-Gerät erneut, eine HTTP-Anforderung mit curl zu stellen.

# curl localhost:8000
Status: Connected

Diesmal wird der Verbindungsstatus zurückgegeben. Durch Herstellen einer MQTT-Verbindung mit mosquitto_sub wurde anscheinend die Gerätestatusmeldung an hello.py gesendet und die Statusvariable in der Funktion on_message () geändert.

Das ist alles wie erwartet.

Zusammenfassung

Diesmal habe ich zum ersten Mal versucht, Flask zu verwenden. Als ich versuchte, den Mindestcode aus dem Beispielcode zu erstellen und den Teil hinzuzufügen, den ich von dort aus implementieren wollte, schien mein Fuß unerwartet hochgeschöpft zu sein. Es gibt auch die Idee, dass ich für diese Art von Versuch und Irrtum studiert habe, aber es scheint, dass ich es zuerst hätte studieren und dann verwenden sollen.

Die Watson IoT Platform ist ein leistungsstarker Cloud-Service zum Verwalten und Steuern von IoT-Geräten und zum Sammeln von Daten.

Der Teil, der die vom IoT-Gerät gesammelten Daten in die Cloud hochlädt, ist die Basis des Systems. Ich möchte jedoch eine Anwendung implementieren, die den Status des IoT-Geräts überwacht und bei Bedarf in der Cloud steuert. Ich dachte darüber nach und dachte an diese Studie. Nachdem wir den Mindestbetrieb des Statusüberwachungsteils bestätigt haben, möchten wir unsere Reichweite erweitern, um die Steuerung von IoT-Geräten durch Senden von Gerätebefehlen zu untersuchen.

Recommended Posts

Nachdem ich die Watson IoT Platform-Anwendung mit Flask implementiert hatte, war ich süchtig nach MQTT-Verbindungen
Ich war süchtig danach, logging.getLogger mit Flask 1.1.x zu versuchen
Ich war auf dotCloud süchtig nach Flask
Wovon ich beim Erstellen von Webanwendungen in einer Windows-Umgebung abhängig war
Ich war süchtig danach, 2020 mit Selen (+ Python) zu kratzen
Was ich mit json.dumps in Pythons base64-Codierung süchtig gemacht habe
Ich war süchtig nach falschen Klassenvariablen und Instanzvariablen in Python
Ich war süchtig nach Multiprocessing + Psycopg2
Der Dateiname war in Python schlecht und ich war süchtig nach Import
Was ich süchtig nach Python Autorun war
[Einführung in json] Nein, ich war süchtig danach. .. .. ♬
Python: Ich konnte in Lambda rekursieren
Ich war nüchtern süchtig danach, awscli von einem in crontab registrierten Python 2.7-Skript aus aufzurufen
Beachten Sie, dass ich süchtig nach dem npm-Skript war, das in der Überprüfungsumgebung nicht übergeben wurde
Wovon ich süchtig war, als ich Klassenvererbung und gemeinsame Tabellenvererbung in SQLAlchemy kombinierte
Ich möchte mit einem Knopf am Kolben übergehen
Eine Geschichte, von der ich bei np.where süchtig war
Python: Kann in Lambda wiederholt werden
Wovon ich süchtig war, als ich Python Tornado benutzte
Als ich versuchte, PIL und matplotlib in einer virtuellen Umgebung zu installieren, war ich süchtig danach.
Wovon ich süchtig war, als ich mit riesigen Dateien in einer Linux 32-Bit-Umgebung umging
Eine Geschichte, nach der ich süchtig war, als ich in Go nil als Funktionsargument angab