SAP Hana Datenbank anbindung

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
oggie93
User
Beiträge: 2
Registriert: Freitag 8. Juni 2018, 14:35

Freitag 8. Juni 2018, 14:49

Hallo zusammen!

Da ich ein Anfänger mit Python bin, ich jedoch ein für mich schwieriges Projekt von der FH bekommen habe und nicht weiter komme, hoffe ich hier Hilfe zu bekommen.
Die Aufgabe des Projektes ist es, dass eine Gruppe Daten an den MQTT-Broker schickt, ich diese Daten von dem MQTT-Broker empfange und diese in die SAP Hana db einfüge. Hinzu kommt, dass ich mit einem RaspberryPI verbunden bin, bzw. dass alles sich auf dem RaspberryPI abspielt.

Mein bisheriger Code:

Code: Alles auswählen

import pyhdb
import paho.mqtt.client as mqtt
import xmltodict
import logging
import signal
import sys


MQTT_SERVER = "143.93.197.43"

# Methode zur Ausgabe des Verbindungsstatus zum MQTT-Broker
def on_mqtt_connect(client, userdata, flags, rc):
    print("Connected with MQTT-Broker, result code " + str(rc))
    client.subscribe("Factory/ColorSorter/Test")

# Methode zum Einfügen der Werte in die Datenbank
def on_mqtt_message_colorsorter(mosq, obj, msg):
    payload = xmltodict.parse(msg.payload.decode("utf-8"))
    print("Topic: " + msg.topic + ", QoS: " + str(msg.qos) + ", Payload: " + str(msg.payload))

activityId      = payload['Activity']['ID']
startDateTime   = datetime.datetime.strptime(payload['Activity']['StartDateTime'],"%Y-%m-%dT%H:%M:%SZ")
endDateTime     = datetime.datetime.strptime(payload['Activity']['EndDateTime'],"%Y-%m-%dT%H:%M:%SZ")
deviceId        = payload['Activity']['DeviceID']
totalAmount     = payload['Activity']['CountedBricks']['TotalAmount']
brickId         = payload['Activity']['CountedBricks']['Color']['BrickId']
brickDateTime   = datetime.datetime.strptime(payload['Activity']['CountedBricks']['Color']['DateTime'],"%Y-%m-%dT%H:%M:%SZ")
name            = payload['Activity']['CountedBricks']['Color']['Name']
rgb             = payload['Activity']['CountedBricks']['Color']['RGB_Value']

cursor=connection.cursor() 

# Hinzufügen der Daten zur Tabelle "Activity"
add_activity = "INSERT INTO `Activity` (`activityId`, `startDateTime`, `endDateTime`, `deviceId`, `totalAmount`) VALUES (%s, %s, %s, %s, %s)"
cursor.execute(add_activity, (activityId, startDateTime, endDateTime, deviceId, totalAmount))


# Hinzufügen der Daten zur Tabelle "Colors_of_Counted_Bricks"
add_colors_of_counted_bricks = "INSERT INTO `Colors_of_Counted_Bricks` (`brickId`, `brickDateTime`, `activityId`) VALUES (%s, %s, %s)"
cursor.execute(add_colors_of_counted_bricks, (brickId, brickDateTime, activityId))

# Hinzufügen der Daten zur Tabelle "Color"
add_color = "INSERT INTO `Color` (`name`, `brickId`, `rgb`) VALUES (%s, %s, %s)"
cursor.execute(add_color, (name, brickId, rgb))
connection.commit()

if __name__== "__main__":

# Herstellen der Verbindung zur Datenbank
    try:
        connection = pyhdb.connect(host="143.93.203.123",
                                   port=39041,
                                   user="MW220_01",
                                   password="MWW220_01")
    except pyhdb.Error as e:
        print("Fehler aufgetreten")

#cursor=connection.cursor()

# Herstellen der Verbindung zum MQTT-Broker
    client = mqtt.Client()
    client.on_connect = on_mqtt_connect
    client.message_callback_add("Factory/ColorSorter/Test", on_mqtt_message_colorsorter)
    client.username_pw_set("pi", "raspberry")
    client.connect("143.93.197.43", 1883, 60)
    client.loop_forever()
wenn ich diesen Code ausführen will, kommt folgende Fehlermeldung:
"File "gruppe_5.py", line 24, in <module>
activityId = payload['Activity']['ID']
NameError: name 'payload' is not defined"

ich hoffe jemand kann mir helfen.
Danke im voraus und liebe Grüße

oggie
Sirius3
User
Beiträge: 7921
Registriert: Sonntag 21. Oktober 2012, 17:20

Freitag 8. Juni 2018, 15:40

Einrückungen sind wichtig für die Zuordnung von Codezeilen zu Blöcken. Die Zeilen ab Zeile 24 sollten eingerückt sein, damit sie zur Funktion on_mqtt_message_colorsorter. Die Funktion on_mqtt_message_colorsorter erhält connection aus dem Nichts. Das ist eine potentielle Fehlerquelle. Alles was eine Funktion braucht, sollte sie über ihre Parameter bekommen, Will man bei einer Callback-Funktion zusätzliche Parameter übergeben, nutzt man functools.partial.
Benutzeravatar
__blackjack__
User
Beiträge: 538
Registriert: Samstag 2. Juni 2018, 10:21

Freitag 8. Juni 2018, 16:48

@oggie93: Die Behandlung der Ausnahme bei der Verbindung zur Datenbank ist fehlerhaft. Wenn die Verbindung fehl schlägt, wird einfach nur der Text 'Fehler aufgetreten' ausgegeben und `connection` bleibt undefiniert, was dann in der Folge unweigerlich zu einem `NameError` führt. Das macht keinen Sinn. Und 'Fehler aufgetreten' ist auch nichts was einem bei der Fehlersuche helfen wird, wenn das tatsächlich passieren sollte. Wenn man eine Ausnahme nicht sinnvoll behandeln kann, das heisst ohne das Programm in einen kaputten Zustand zu bringen und ohne die Fehlersuche zu erschweren, dann sollte man die Ausnahme gar nicht behandeln. In diesem Fall bricht das Programm so oder so mit einer Ausnahme ab, aber wenn man die Ausnahme so nicht ”behandeln” würde, wüsste man wenigstens mehr über die Ausnahme die beim Verbinden aufgetreten ist.

Du importierst drei Module aus der Standardbibliothek, von denen dann aber kein einiges im Code verwendet wird. Andererseits wird im Code das `datetime`-Modul verwendet ohne es zu importieren‽

Es wird die Konstante `MQTT_SERVER` definiert, aber nicht verwendet. Stattdessen steht die IP noch mal im Quelltext. Die IP der Datenbank würde man vielleicht auch besser als Konstante heraus ziehen.

Damit das Problem mit der ”magisch” einfach so existierenden `connection` gar nicht erst entsteht, sollte der Code in dem `if __name__ …`-Zweig in einer Funktion verschwinden.

Die Kommmentare über den Funktionen wären besser DocStrings. Und man sollte Funktionen vielleicht nicht als Methoden bezeichnen. Wobei man eigentlich weder bei einer Funktions- noch einer Methodendefinition kommentieren oder dokumentieren muss das es sich um eine Funktion oder Methode handelt. Wer das nicht sieht, dem ist auch mit einem Kommentar nicht zu helfen. :-)

Ansonsten wird da auch viel Offensichtliches kommentiert. Kommentare sollten nicht erklären was gemacht wird, denn das steht da ja bereits im Code, sondern warum das so gemacht wird — sofern das nicht offensichtlich ist.
“The optimist proclaims that we live in the best of all possible worlds;
and the pessimist fears this is true.” — James Branch Cabell
oggie93
User
Beiträge: 2
Registriert: Freitag 8. Juni 2018, 14:35

Freitag 8. Juni 2018, 20:08

@Sirius3 danke das waren tatsächlich "Einrückungsfehler", jetzt funktioniert der Code und die Verbindung zur DB funktioniert und zum Broker auch.
@__blackjack__ , da es eine Gruppenarbeit ist, sind die Kommentare auch dementsprechend für die Erklärung der anderen Kommilitonen und auch für den Professor gedacht:D.
Import datetime hat gefehlt das ist richtig. Wenn ich jetzt versuche eine Nachricht vom Broker aus zuschicken kommt folgende Fehlermeldung:brickId = payload['Activity']['CountedBricks']['Color']['BrickId']
TypeError: list indices must be integers or slices, not str

ist das ein Fehler in der XML?
Die XML Nachricht lautet:
<?xml version="1.0" encoding="UTF-8"?>
<Activity>
<ID>1</ID>
<DeviceID>LU-BOT</DeviceID>
<StartDateTime>2018-04-13T15:02:00Z</StartDateTime>
<EndDateTime>2018-04-13T15:02:25Z</EndDateTime>
<CountedBricks>
<TotalAmount>2</TotalAmount>
<Color>
<BrickId>1</BrickId>
<Name>red</Name>
<RGB_Value>255,0,0</RGB_Value>
</Color>
<Color>
<BrickId>1</BrickId>
<Name>green</Name>
<RGB_Value>255,0,0</RGB_Value>
</Color>
</CountedBricks>
</Activity>
Die Datenbank in der SAP DB ist auch so aufgebaut wie es in der XML steht.
Weiß jemand vielleicht was ich da falsch mache?
Sirius3
User
Beiträge: 7921
Registriert: Sonntag 21. Oktober 2012, 17:20

Freitag 8. Juni 2018, 20:37

Da es mehr als ein <Color>-Tag gibt, scheint xmltodict das in eine Liste zu konvertieren.

Auch wenn ein Kommilitone den Code liest, hat er mit "INSERT INTO `Activity `" exakt die gleiche Information, wie mit dem Kommentar eine Zeile davor.

Strings sollte man nicht mit + zusammenstückeln, sondern .format benutzen. Wenn man payload['Activity'] einer Variable zuweist, wird das deutlich lesbarer; durch eine Funktion zum Umwandeln der Zeiten auch. `totalAmount` ist redundant mit der Länge der Color-Liste und damit überflüssig und für die Farben brauchst Du dann eine Schleife.
Benutzeravatar
__blackjack__
User
Beiträge: 538
Registriert: Samstag 2. Juni 2018, 10:21

Freitag 8. Juni 2018, 20:54

@oggie93: Schau Dir das XML und die Zeile die den TypeError auslöst mal an und überlege mal was Du da als Ergebnis erwartest? Welche brickId soll da als Ergebnis heraus kommen? Und warum *die* und nicht die andere? (Ich meine jetzt nicht den Wert, sondern welches der beiden Tags im der XML-Dokument)

Ansonsten kann man so etwas auch sehr gut einfach mal in einer Python-Shell ausprobieren:

Code: Alles auswählen

In [13]: payload['Activity']#['CountedBricks']['Color']['BrickId']
Out[13]: 
OrderedDict([(u'ID', u'1'),
             (u'DeviceID', u'LU-BOT'),
             (u'StartDateTime', u'2018-04-13T15:02:00Z'),
             (u'EndDateTime', u'2018-04-13T15:02:25Z'),
             (u'CountedBricks',
              OrderedDict([(u'TotalAmount', u'2'),
                           (u'Color',
                            [OrderedDict([(u'BrickId', u'1'),
                                          (u'Name', u'red'),
                                          (u'RGB_Value', u'255,0,0')]),
                             OrderedDict([(u'BrickId', u'1'),
                                          (u'Name', u'green'),
                                          (u'RGB_Value', u'255,0,0')])])]))])

In [14]: payload['Activity']['CountedBricks']#['Color']['BrickId']
Out[14]: 
OrderedDict([(u'TotalAmount', u'2'),
             (u'Color',
              [OrderedDict([(u'BrickId', u'1'),
                            (u'Name', u'red'),
                            (u'RGB_Value', u'255,0,0')]),
               OrderedDict([(u'BrickId', u'1'),
                            (u'Name', u'green'),
                            (u'RGB_Value', u'255,0,0')])])])

In [15]: payload['Activity']['CountedBricks']['Color']#['BrickId']
Out[15]: 
[OrderedDict([(u'BrickId', u'1'),
              (u'Name', u'red'),
              (u'RGB_Value', u'255,0,0')]),
 OrderedDict([(u'BrickId', u'1'),
              (u'Name', u'green'),
              (u'RGB_Value', u'255,0,0')])]
Ob es natürlich Sinn macht das ein Brick mit der gleichen ID zwei Unterschiedliche Farben hat, musst Du wissen. Für mich sieht's ein wenig komisch aus.

Auch für andere Kommilitonen oder Professoren muss man Offensichtliches nicht kommentieren. Dokumentieren was eine Funktion macht, im Docstring, ja, aber keine Kommentare die vor einem INSERT in Tabelle xyz erklären, dass da Werte in Tabelle xyz eingefügt werden.

Namen werden in Python übrigens in der Regel klein_mit_unterstrichen geschrieben. Also beispielsweise `activity_id` statt `activityId`. Das gilt für alles ausser Klassen (MixedCase) und Konstanten (KOMPLETT_GROSS).

Zum Ausrichten von mehreren Zeilen an Gleichheitszeichen sagt der Style Guide auch Nö.
“The optimist proclaims that we live in the best of all possible worlds;
and the pessimist fears this is true.” — James Branch Cabell
Antworten