Seite 1 von 1

Array dynamisch erstellen und in Tuple packen

Verfasst: Mittwoch 22. Mai 2024, 20:09
von kiaralle
Hallo,
ich möchte ein Arry "wert[x]" dynamisch erstellen, erweitern und in ein Tuple packen.
Hoffe ich liege jetzt richtig mit der Bezeichnung Array. In PHP ist es ja eins :roll:
Über den Sinn, lässt sich streiten. Hatten wir ja schon mal. Ich möchte aber etwas lernen.
Gebrauchen kann man es ja immer.

Ich benutze wieder growatt_registers[] und möchte mir das viele schreiben im statement ersparen.
Das statement funktioniert und ich kann so in einem anderen Script meine Tabelle erstellen.
Wie lang die jetzt ist, interessiert erst einmal nicht. mir geht es ums lernen und verstehen, lösen einer Aufgabe.

data sollte in data() sein.

Mit tuple erhalte ich eins, es schaut aber etwas seltsam aus.
...'register[89]', 'register[90]')
Ausschauen sollte es ja so.
..., register[89], register[90])

Code: Alles auswählen

growatt_registers = [
    [0, "Status", 1],
    [1, "Vpv1", 0.1],
    ....
    [88, "Eop_dischrTotal_L", 1],
    [89, "leer89", 0],
    ["90", "ParaChgCurr", 0.1]  # 90
    ]

register_name = []
register_id = []
register_factor = []

data = ("")
platz = ['%s'] * len(growatt_registers)
platz = ", ".join(platz)

for i in range(0, len(growatt_registers)):
    register_id.append("register[" + str(growatt_registers[i][0]) + "]")
    register_name.append(growatt_registers[i][1])
    register_factor.append(growatt_registers[i][2])

print(tuple(register_id))

register_name_lower = ", ".join(register_name)
register_name_lower = register_name_lower.lower()


cursor = connection.cursor()
statement = "INSERT INTO master(" + register_name_lower + " ) VALUES (" + platz + " )"
data = tuple(register_id)
cursor.execute(statement, data)
connection.commit()

Fehlermeldung:
OperationalError: Incorrect integer value: 'register[0]' for column `solaranlage`.`master`.`status` at row 1
Ich verstehe das jetzt so, 'register[0]' gibt es in der Tabelle nicht. Es gibt nur ein register[0]

Wo liegt mein Fehler? Danke :wink:

Re: Array dynamisch erstellen und in Tuple packen

Verfasst: Mittwoch 22. Mai 2024, 20:57
von Dennis89
Hallo,

das sieht nicht komisch aus, du schreibst einen String in die Liste. Man würde aber eher einen `f`-String verwenden, anstatt das zusammen puzzeln mit "+".

Code: Alles auswählen

f"register[{growatt_registers[i][0]}]"
"register[89]" ist ein String, ohne die Anführungszeichen wird das zurück gegeben was sich in der Liste `register` an Position 89 befindet. Also wenn du den String nicht drin haben willst dann `append(register[89])` dann hast du den Wert drin.

Grüße
Dennis

Re: Array dynamisch erstellen und in Tuple packen

Verfasst: Mittwoch 22. Mai 2024, 20:59
von Sirius3
`growatt_registers` scheint ein Konstante zu sein: `GROWATT_REGISTERS`.
Die Namen in den Registern scheinen ja Tabellenfeldnamen zu sein. Diese sollten genausowenig kryptische Abkürzungen enthalten, wie bei allen anderen Namen ja auch. Was soll ein Eop_dischrTotal_L sein? Und ParaChgCurr sollte wohl besser paraguay_change_currency sein.
Warum ist das erste Element des 91. Eintrag plötzlich ein String? Und wenn da wirklich was von 0 bis 90 durchnummieriert wird, kann man den ersten Eintrag auch weglassen.
Man belegt keine Variablen mit Dummy-Werten, wie das bei `data` der Fall scheint.
Über einen Index iteriert man nicht. Strings stückelt man nicht mit + zusammen.
Anscheinend brauchst Du `data` gar nicht, weil Du `register` direkt verwenden könntest.

Code: Alles auswählen

GROWATT_REGISTERS = [
    ["Status", 1],
    ["Vpv1", 0.1],
    ...
    ["Eop_dischrTotal_L", 1],
    ["leer89", 0],
    ["ParaChgCurr", 0.1]  # 90
]

def insert_registers_in_database(connection, registers):
    register_names = [
        name.lower()
        for name, _ in GROWATT_REGISTERS
    ]
    statement = f"INSERT INTO master({','.join(register_names)}) VALUES ({','.join(['%s' * len(register_names)])})"
    with closing(connection.cursor()) as cursor:
        cursor.execute(statement, tuples(registers))
    connection.commit()

Re: Array dynamisch erstellen und in Tuple packen

Verfasst: Mittwoch 22. Mai 2024, 21:17
von __blackjack__
@kiaralle: Python hat da Listen und Wörterbücher wo PHP (assoziative) Arrays und für beides benutzt.

Das ist alles etwas wirr in dem Code. Der sollte mal aufgeräumt werden was die Namen und die Werte und Typen angeht.

Bei `growatt_registers` ist das erste Element der Listen immer eine ganze Zahl, ausser beim letzten, da ist es eine Zeichenkette mit der Darstellung einer ganzen Zahl. Das sollte wohl auch eine Zahl sein.

Wenn die Position eines Elements die Bedeutung des Elements festlegt, also nicht alle Elemente die gleiche Bedeutung haben, dann verwendet man eher Tupel als Listen.

Wie viele Elemente enthält `growatt_registers` denn? Wenn das 91 sind, und das erste Element lückelos aufsteigend einfach nur durchnummeriert ist, dann macht das keinen Sinn, denn dann ist dieser Wert ja immer gleich dem Index. Den hat man schon wenn man per Index darauf zugreift, und den kann man sich mit `enumerate()` einfach erzeugen wenn man direkt über die Elemente der Liste iteriert.

`register_name`, `register_id` und `register_factor` haben Namen die auf *einen* Wert schliessen lassen, stehen aber für Liste mit *vielen* Werten.

`register_factor` wird zwar befüllt, aber nirgends verwendet.

Man muss nicht jedes kleine Zwischnergebnis an einen Namen binden. Schon gar nicht wenn dann Werte unterschiedlichen Typs an einen Namen gebunden werden. Das ist verwirrend.

`data` wird an eine leere Zeichenkette gebunden die nie irgendwo verwendet wird, und später wird der Name dann an ein Tupel gebunden.

Namen sollten erst an Werte gebunden werden wenn man sie braucht. Und beispielsweise nicht zwischen dem binden an leere Listen und dem befüllen in einer Schleife noch andere Sachen machen.

``for i in range(len(sequence)):`` nur um dann `i` für den Zugriff in die Sequenz zu verwenden ist in Python ein „anti pattern“. Man kann direkt über die Elemente von Sequenzen iterieren, ohne den unnötigen Umweg über einen Laufindex.

Und auch feste ”magische” Indexwerte sind nicht gut, weil die nicht zum Verständnis der Bedeutung der Werte beitragen. Man kann die Werte an Namen binden.

Das zusammenstückeln von Zeichenketten und Werten mittels ``+`` und `str()` ist eher BASIC als Python. Dafür gibt es die `format()`-Methode auf Zeichenketten und f-Zeichenkettenliterale.

Das zweite Argument von `execute()` muss kein Tupel sein, eine Liste geht da auch.

Der Code würde dann so aussehen:

Code: Alles auswählen

from contextlib import closing


def some_function(connection):
    growatt_registers = [
        (0, "Status", 1),
        (1, "Vpv1", 0.1),
        # ....
        (88, "Eop_dischrTotal_L", 1),
        (89, "leer89", 0),
        (90, "ParaChgCurr", 0.1),
    ]

    register_ids = []
    column_names = []
    for register_id, name, _factor in growatt_registers:
        register_ids.append(f"register[{register_id}]")
        column_names.append(name.lower())

    with closing(connection.cursor()) as cursor:
        cursor.execute(
            "INSERT INTO master ({}) VALUES ({})".format(
                ", ".join(column_names), ", ".join(["%s"] * len(column_names))
            ),
            register_ids,
        )
        connection.commit()
Das Problem sollte eigentlich offensichtlich sein. Du generierst da komische ”register IDs” die wie Pythoncode aussehen als Zeichenketten und versuchst diese Zeichenketten dann als Werte für die Datenbankspalten zu übergeben. Was soll die Datenbank denn machen wenn sie "register[0]" als *Wert* bekommt?

Und so ``..., register[89], register[90])`` kann das Tupel bei der Ausgabe nicht aussehen. Das muss ja *Werte* enthalten. ``register[90]`` ist aber kein Wert sondern ein *Ausdruck*. Das ist in PHP auch nicht anders, da geht das auch nicht ein Array mit Ausdrücken zu erstellen.

Die Lösung ist supersimpel wenn man verstanden hat was Code ist, was Zeichenketten sind, was Werte sind, und die Trennung von Python und SQL/Datenbank. Man kann der SQL-Datenbank keine Python-Ausdrücke als Zeichenketten übergeben und erwarten, dass die damit etwas anfangen kann oder auch nur Zugriff auf die Daten im Python-Prozess hat.

Letztlich solltest Du *dieses* Problem aber gar nicht lösen, sondern die Tabelle so strukturieren das die nur vier Spalten hat: id, timestamp, register_number, value. Sofern die Werte alle den gleichen Typ haben.

Der Code sähe dann ungefähr so aus:

Code: Alles auswählen

from contextlib import closing
from datetime import datetime as DateTime


def some_function(connection, register_values):
    growatt_registers = [
        (0, "Status", 1),
        (1, "Vpv1", 0.1),
        # ....
        (88, "Eop_dischrTotal_L", 1),
        (89, "leer89", 0),
        (90, "ParaChgCurr", 0.1),
    ]

    with closing(connection.cursor()) as cursor:
        now = DateTime.now()
        cursor.executemany(
            "INSERT INTO master (`timestamp`, register_id, value) VALUES (%s, %s, %s)",
            (
                (now, register_id, register_values[register_id])
                for register_id, _, _ in growatt_registers
            ),
        )
        connection.commit()

Re: Array dynamisch erstellen und in Tuple packen

Verfasst: Donnerstag 23. Mai 2024, 20:29
von kiaralle
Hi,
Und so ``..., register[89], register[90])`` kann das Tupel bei der Ausgabe nicht aussehen. Das muss ja *Werte* enthalten. ``register[90]`` ist aber kein Wert sondern ein *Ausdruck*. Das ist in PHP auch nicht anders, da geht das auch nicht ein Array mit Ausdrücken zu erstellen.
Das alles ist nur ein Bastel-Code.

Ich weiß das die Register...[x] bestückt sein müssen.
Das funktioniert auch, also die Abfrage der Wechselrichter über RS485. Und ich speichere ja bereits in eine Datenbank ab.
Nur teste ich jetzt verschiedene Wege der Speicherung.

Letztlich solltest Du *dieses* Problem aber gar nicht lösen, sondern die Tabelle so strukturieren das die nur vier Spalten hat: id, timestamp, register_number, value. Sofern die Werte alle den gleichen Typ haben.
[/quote

Das wäre der einfacherer Aufbau der DB und sicher auch der einfachere Weg.
Diese sollten genausowenig kryptische Abkürzungen enthalten, wie bei allen anderen Namen ja auch. Was soll ein Eop_dischrTotal_L sein? Und ParaChgCurr sollte wohl besser paraguay_change_currency sein.
Diese Bezeichnungen sind in der Modbus-Liste von Growatt so vorgegeben. Es macht aus meiner Sicht Sinn, dabei zu bleiben.
So kann ich später zuordnen was ich im Modbus abrufe. Warum Sachen neu erfinden.
Eop_dischrTotal_L , Eop_dischrTotal_H Das L ist Low und ein H wäre High.
Wenn Low überläuft, wird High mit eingerechnet. 16bit.
Para system charge current = ParaChgCurr

Danke für die vielen Hinweise, ich werde sicher einige testen und umsetzten. Macht wie immer Spass :-)

Re: Array dynamisch erstellen und in Tuple packen

Verfasst: Freitag 24. Mai 2024, 09:33
von DeaD_EyE
Diese Bezeichnungen sind in der Modbus-Liste von Growatt so vorgegeben. Es macht aus meiner Sicht Sinn, dabei zu bleiben.
Das ist in der Welt der Automatisierung völlig normal und das sollten auch mal die alten Python-Häschen lernen.
Man könnte die Namen ändern, was zusätzliche Arbeit ist und dann steht noch die Frage im Raum: für wen überhaupt?

Die Namen würde ich auch nicht ändern, aber der Code muss gut lesbar sein.
Schwierige Aufgabe, wenn man mit kryptischen Namen arbeitet :-D

Re: Array dynamisch erstellen und in Tuple packen

Verfasst: Freitag 24. Mai 2024, 09:59
von grubenfox
Wobei man hier
kiaralle hat geschrieben: Donnerstag 23. Mai 2024, 20:29 Para system charge current = ParaChgCurr
anstelle von ParaChgCurr als Namen auch Para_system_charge_current hätte nehmen können. Wenn das Para_system_charge_current erst einmal im Listing steht, dann sollte es bei einem ordentlichen Editor beim späteren verwenden des Namens nur 2-4 Tastendrücke brauchen bis einem der vollständige Namen in einer eher kurzen Liste vom Editor vorgeschlagen wird.

Re: Array dynamisch erstellen und in Tuple packen

Verfasst: Freitag 24. Mai 2024, 11:29
von Sirius3
@DeaD_EyE: bei Micro-Controllern hat man wenig Speicher, hier sogar nur eine Handvoll Register. Zudem wurde früher solche Micro-Controller auch noch mit obskuren Programmiersprachen programmiert, die Variablennamen noch stark einschränkten.
Alles aber kein Grund, auf einem großen System und einer entsprechenden Datenbank kryptische Namen zu verwenden.

@kiaralle: Du hast ja noch zusätzlich das Problem, das sich die Werte aus den Registern gar nicht direkt verwenden lassen. Wenn Du einen High- und einen Low-Wert hast, dann ist der erste Schritt, den in den eigentlichen Wert umzurechnen. Zusätzlich mußt Du die Zahlen ja noch entsprechend skalieren.

Re: Array dynamisch erstellen und in Tuple packen

Verfasst: Freitag 24. Mai 2024, 11:31
von __blackjack__
Diese Namen müssten halt überhaupt gar nicht im Code stehen als *Namen*, also weder in Python noch in SQL. Problem aus meiner Programmierersicht ist nicht das ein Hersteller oder eine Problemdomäne diese Namen benutzt, sondern das so etwas keine Namen in Programmen sein sollten. Also im konkreten Fall keine Spaltennamen in der Datenbank. Falls das Werte sind, in Programm oder Datenbank, wenn man die Registernummern dem vom Hersteller vergebenen Namen zuordnen möchte, zum Beispiel zur Ausgabe, ist das ja okay.

Man könnte beispielsweise in der Datenbank eine Tabelle mit Registernummer (id), Name, Beschreibung, und Faktor haben, wo dann ``90, 'ParaChgCurr', 'Para system charge current', 0.1`` als Datensatz drin steht. Registernummer als Primärschlüssel, es sei denn es gibt einen Grund dafür noch extra eine künstliche ID einzuführen.

Re: Array dynamisch erstellen und in Tuple packen

Verfasst: Freitag 24. Mai 2024, 11:52
von DeaD_EyE
@Sirius ich meinte aktuelle Mikrocontroller, auf denen z.B. Micropython lauffähig ist. Davon gibt es viele und die haben mehr Speicher als damalige PCs. Wie viel RAM möchtest du haben? Es gibt Controller mit 8 MiB SPIRAM. Damalige PCs hatten weniger als 1 MiB Arbeitsspeicher.

Theoretisch sollten lange Namen keinen zusätzlichen Speicher verbrauchen, da im Maschinencode die Namen nicht verwendet werden, sondern Adressen, die immer gleich lang sind.

Die Ausnahme bilden Interpreter, die zwangsläufig die Namen speichern müssen.

Re: Array dynamisch erstellen und in Tuple packen

Verfasst: Freitag 24. Mai 2024, 12:12
von Sirius3
@DeaD_EyE: ich habe nur versucht, in zu Erklären, warum man das "in der Welt der Automatisierung" für "normal" hält. Du bestätigst ja nur, dass das heutzutage alles überholt ist, und niemand mehr sich mit kryptischen Abkürzungen herumschlagen müsste.
Aber schlechte Angewohnheiten halten sich länger als gedacht.

Re: Array dynamisch erstellen und in Tuple packen

Verfasst: Freitag 24. Mai 2024, 12:46
von kiaralle
@kiaralle: Du hast ja noch zusätzlich das Problem, das sich die Werte aus den Registern gar nicht direkt verwenden lassen. Wenn Du einen High- und einen Low-Wert hast, dann ist der erste Schritt, den in den eigentlichen Wert umzurechnen. Zusätzlich mußt Du die Zahlen ja noch entsprechend skalieren.
Bis auf zwei Werte welche im Wechselrichter auflaufen, werde ich High höchstwahrscheinlich nie brauchen.
Die Leistung, und andere Werte werden bei LOW die 5000 nie knacken. Deshalb verzichte ich darauf mich mit High abzugeben.
Falls doch, kann ich die im PHP umrechnen. Steuerungstechnisch haben die bei mir keine Relevanz.
Diese Namen müssten halt überhaupt gar nicht im Code stehen als *Namen*, also weder in Python noch in SQL.
Aktuell steige ich schrittweise auf die Maria-DB um und du hast auch hier wieder Recht.
Nur mit Registeradressen arbeiten ist aber auch nicht toll. Wenn Growatt am Modbus etwas ändert, muss ich lange zuordnen und suchen.
Allerdings habe ich dann ein Problem wenn die Maria-Db mal nicht läuft, eventuell, müsste ich halt vorsorgen.
Den Status meiner Steuerung frage ich schon über Maria-DB ab.

Meine Steuerung läuft ohne Aussetzer schon seit 3 Monaten.
Eure Anregungen finde ich klasse und ich schreibe weiter an meiner Solarsteuerung mit Lastabwurf und Überschussladung vom E-Auto.
Man muss ein Projekt haben um mehr zu lernen.

Re: Array dynamisch erstellen und in Tuple packen

Verfasst: Freitag 24. Mai 2024, 13:07
von __blackjack__
@kiaralle: Das ist eine gruselige Vorstellung. Wenn es einen High-Wert gibt dann sollte man den immer mit verwenden auch wenn der ”immer” 0 ist. Das sind so Annahmen die halt gerne mal *nicht* stimmen, und sei es nur im Fehlerfall wo dann viel zu grosse Werte nur deshalb nicht auffallen weil man da einfach einen Teil der Daten ignoriert hat und einfach mit falschen Werten weiterarbeitet/-rechnet.

Wer sagt denn es sollte nur mit Registeradressen gearbeitet werden? Man kann und sollte die vernünftigen Namen im Programm zuordnen.

Falls Veränderungen an den Registern/Zuordnungen durch die Firma möglich sind, muss man das halt im Datenbankentwurf berücksichtigen, das man solche Änderungen auch darüber nachverfolgen kann. Da muss man als zusätzliches Datum noch eine Version der Zuordnung speichern. Denn man will ja auch weiterhin die alten Daten korrekt lesen und interpretieren können.

Re: Array dynamisch erstellen und in Tuple packen

Verfasst: Montag 27. Mai 2024, 09:03
von DeaD_EyE
Sirius3 hat geschrieben: Freitag 24. Mai 2024, 12:12 @DeaD_EyE: ich habe nur versucht, in zu Erklären, warum man das "in der Welt der Automatisierung" für "normal" hält. Du bestätigst ja nur, dass das heutzutage alles überholt ist, und niemand mehr sich mit kryptischen Abkürzungen herumschlagen müsste.
Aber schlechte Angewohnheiten halten sich länger als gedacht.
@Sirius3
Das wissen wir Automatisierer, aber manche Hersteller interessiert es nicht. Mit kryptischen Namen von anderen Herstellern werde ich öfters genervt. Bevor Industrieanlagen überhaupt gebaut werden, wird alles berechnet. Wenn man z.B. Adernummern verwenden soll, wird extra abgerechnet. Wenn die Namensgebung von Drittherstellern geändert werden soll, wird das extra abgerechnet. Jede zusätzliche Tätigkeit kostet Geld.

Re: Array dynamisch erstellen und in Tuple packen

Verfasst: Montag 27. Mai 2024, 10:01
von Sirius3
Natürlich kostet alles Geld. Und am meisten Geld kostet es, Bugs in fertigen Produkten zu reparieren, weil man am Anfang an guten Namen gespart hat.
Meine Strategie ist es, einen möglichst dünnen Layer auf die externe Schnittstelle zu legen, wo alle Implementierungsdetails in klar verständliche Namen, Methoden, Klasse, etc. umgewandelt werden. Das was der dünne Layer kostet, spart man an anderer Stelle hundertfach.