Hallo in die Runde,
ich bin neu hier, und eigentlich auch neu im Thema Python, aber ein paar Snipsel laufen doch.
Mein Problem:
Ich habe Code vom Notebook auf eine Ordid C4 / Ubuntu portiert und alles ist sehr seltsam.
Auf einem anderen C4 laufen die Progs schon, aber nicht auf dem "Neuen"
C4-Neu:
Python 3.10.12
pymodbus 3.11.3
Auf meinem Notebook:
Python 3.8.12
pymodbus 3.0.2
Auf dem Notebook , unter eclipse , läuft es wie erwartet.
Was ich beim Port auf den C4 machen muste: in den pymodbuscall: unit= durch device_id= ersetzen, ansonsten bekam ich immer eine Exception.
Aber das wird scheinbar nicht richtig verarbeitet.
Daher habe ich "versucht" das Logging der RTU Connection einzuschalten.
Obwohl der Code auf dem Nodebook incl. einen verständlichem Logging läuft, bekomme ich ich auf dem C4 für read_holding_registers gar kein Logging. Wieso ?
Und für client.write_register:
2025-11-23 20:16:22,301 MainThread DEBUG base :72 Processing: 0xc9 0x6 0x0 0xc 0x0 0x0 0x59 0x81
2025-11-23 20:16:22,302 MainThread DEBUG decoders :79 decoded PDU function_code(6 sub -1) -> WriteSingleRegisterResponse(dev_id=0, transaction_id=0, address=12, count=0, bits
Wobei in der ersten Zeile die Adresse noch richtig geloggt wird. In der zweiten Zeile ist die Adresse "0" -- Verstehe ich nicht!
Hier der Code: des write:
def qxdo24_setDuty ( client, io, wert ):
lunit = int(config_tempctl.ADDR_QXDO24)
val = int(wert * 10)
reg = 0x0
reg += io -1
print( "Set Unit= " + str(lunit) + " Reg: " + str(reg) + " Val: " + str(val) )
client.write_register(reg, val, device_id=lunit)
return()
Das Main sieht aus wie folgt:
#!/usr/bin/env python3
# coding=utf-8
from pymodbus.client import ModbusSerialClient as ModbusClient
from qxdo24 import qxdo24_readConfig1, qxdo24_setAddr, qxdo24_setDuty, qxdo24_setFreq, qxdo24_setCtrl
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s '
'%(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
print ( "Vorr RTU Connection" )
baudrate = 9600
client = ModbusClient(
# method = 'rtu'
#,
port=config_tempctl.port
,baudrate=baudrate
,parity = 'N'
,bytesize = 8
,stopbits = 1
,timeout=2
# war 2 Sek
)
connection = client.connect()
if connection is None:
print ( "Fehler RTU Connection" )
log_msg( "E","0", "0", "Fehler RTU Connect" )
exit ()
if connection == False:
print ( "Fehler RTU Connection" )
log_msg( "E","0", "0", "Fehler RTU Connect" )
exit ()
print ( "Nach RTU Connection" )
config_tempctl.client = client
#QX-DO24 PWM Outout
qxdo24_readConfig1 ( config_tempctl.client )
# qxdo24_setAddr ( config_tempctl.client )
qxdo24_setDuty ( config_tempctl.client , 1, 0 )
Soweit die Fragmente
Meine Fragen:
Wieso verhält sich das Logging so anders?
Und wie übergebe ich unter 3.11.3 die Adresse den nun richtig ?
Ich weiss, das der Write keine Fehlerbearbeitung hat........
Für mich sieht es aus, als wenn hier Versionen massiv NICHT zusammen passen.
Aber wie erkennen ...und dann beheben.
Vielen Dank für jeden Hinweis
JR
pymodbus 3.11.3 Logging der RTU Frames / Änderungen zu 3.02
- __blackjack__
- User
- Beiträge: 14238
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
Das Modul hat eine Dokumentation die unter anderem einen „API changes“-Abschnitt enthält. Wo man auch sieht wie verdammt weit die beiden Versionen bei Dir auseinander sind.
Anmerkungen zum Quelltext: Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).
Man schreibt nicht den Modulnamen als Prefix für alle Funktionen. Das Modul ist doch schon der Namensraum. Es macht keinen Sinn diese Information noch mal zu wiederholen.
Namen sollten keine kryptischen Pre- oder Postfixe enthalten. Und auch keine angehängten nichtssagenden Nummern.
Es wird in dem bisschen Code `logging`, `print()`, *und* dann auch noch eine `log_msg()`-Funktion verwendet.
Auf Modulebene sollte nur Code stehen, der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.
`ModbusClient`-Objekte sind Kontextmanager, die sollte man mit der ``with``-Anweisung verwenden.
`connection` wäre ein Name für eine Verbindung, nicht für einen Wahrheitswert. Der auch nicht `None` werden kann. Selbst wenn er es könnte machen die *zwei* Prüfungen die dann jeweils den gleichen Code im ``if``-Zweig stehen haben keinen Sinn. Das ist einfach ``if not client.connect():``.
Wurde `exit()` explizit aus dem `sys`-Modul importiert? Denn sonst verlässt man sich auf eine andere Funktion die so heisst und die es nicht zwingend ”einfach so” geben muss. Zudem sollte man da auch einen Rückgabecode angeben, denn *das* ist der Zweck dieser Funktion. Sonst hätte man nämlich die Hauptfunktion an der Stelle auch einfach per ``return`` verlassen können.
Der Client sollte nicht als globale Variable im Konfigurationsmodul gesetzt werden!
Bei der `set_duty()`-Funktion herrscht ziemliches Namenschaos. Im Konfigurationsmodul heisst das was an `lunit` gebunden wird `ADDR_*`, ist aber gar keine Adresse, das ist `reg`, und wird als `device_id` übergeben. Und `value` auszuschreiben bringt einen auch nicht um. Was auch immer das `l` in `lunit` bedeuten soll. `reg` wird auch komisch gebildet. Klar kann man was auf 0 addieren. Man kann aber auch einfach gleich den Wert zuweisen den man da addiert.
Eine Funktion die Grundsätzlich ein leeres Tupel zurück gibt, mit dem der Aufrufer dann auch überhaupt gar nichts anfängt, macht keinen Sinn.
Die `set_duty()`-Funktion würde eher so aussehen:
Und das Hauptprogramm so:
Anmerkungen zum Quelltext: Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).
Man schreibt nicht den Modulnamen als Prefix für alle Funktionen. Das Modul ist doch schon der Namensraum. Es macht keinen Sinn diese Information noch mal zu wiederholen.
Namen sollten keine kryptischen Pre- oder Postfixe enthalten. Und auch keine angehängten nichtssagenden Nummern.
Es wird in dem bisschen Code `logging`, `print()`, *und* dann auch noch eine `log_msg()`-Funktion verwendet.
Auf Modulebene sollte nur Code stehen, der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.
`ModbusClient`-Objekte sind Kontextmanager, die sollte man mit der ``with``-Anweisung verwenden.
`connection` wäre ein Name für eine Verbindung, nicht für einen Wahrheitswert. Der auch nicht `None` werden kann. Selbst wenn er es könnte machen die *zwei* Prüfungen die dann jeweils den gleichen Code im ``if``-Zweig stehen haben keinen Sinn. Das ist einfach ``if not client.connect():``.
Wurde `exit()` explizit aus dem `sys`-Modul importiert? Denn sonst verlässt man sich auf eine andere Funktion die so heisst und die es nicht zwingend ”einfach so” geben muss. Zudem sollte man da auch einen Rückgabecode angeben, denn *das* ist der Zweck dieser Funktion. Sonst hätte man nämlich die Hauptfunktion an der Stelle auch einfach per ``return`` verlassen können.
Der Client sollte nicht als globale Variable im Konfigurationsmodul gesetzt werden!
Bei der `set_duty()`-Funktion herrscht ziemliches Namenschaos. Im Konfigurationsmodul heisst das was an `lunit` gebunden wird `ADDR_*`, ist aber gar keine Adresse, das ist `reg`, und wird als `device_id` übergeben. Und `value` auszuschreiben bringt einen auch nicht um. Was auch immer das `l` in `lunit` bedeuten soll. `reg` wird auch komisch gebildet. Klar kann man was auf 0 addieren. Man kann aber auch einfach gleich den Wert zuweisen den man da addiert.
Eine Funktion die Grundsätzlich ein leeres Tupel zurück gibt, mit dem der Aufrufer dann auch überhaupt gar nichts anfängt, macht keinen Sinn.
Die `set_duty()`-Funktion würde eher so aussehen:
Code: Alles auswählen
def set_duty(client, io, value):
address = io - 1
value = int(value * 10)
device_id = config.ADDR_QXDO24
log.debug("Set Unit=%r Reg: %r Val: %r", device_id, address, value)
client.write_register(address, value, device_id=device_id)Code: Alles auswählen
#!/usr/bin/env python3
import logging
import sys
import config
import qxdo24
from pymodbus.client import ModbusSerialClient as ModbusClient
BAUDRATE = 9600
logging.basicConfig(
format=(
"%(asctime)-15s %(threadName)-15s "
"%(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s"
)
)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
def main():
log.debug("Vor RTU Connection")
with ModbusClient(
port=config.PORT,
baudrate=BAUDRATE,
parity="N",
bytesize=8,
stopbits=1,
timeout=2,
) as client:
if not client.connect():
log.fatal("Fehler RTU Connection")
sys.exit(1)
log.debug("Nach RTU Connection")
qxdo24.read_config(client)
# qxdo24.set_addr(client)
qxdo24.set_duty(client, 1, 0)
if __name__ == "__main__":
main()“Ich bin für die Todesstrafe. Wer schreckliche Dinge getan hat, muss eine angemessene Strafe bekommen. So lernt er seine Lektion für das nächste Mal.” — Britney Spears, Interview in der französischen Zeitung Libération, 2. April 2002
Hallo _blackjock_
Danke für die Hinweise.
Werde das Prog. überarbeiten, bzw Deine Korrekturen übernehmen.
Das Prog. ist nunmehr schon ziemlich verbastelt, weil ich auch versuchte, den Fehler einzugrenzen.
Zum Thema Modbus:
Ok, die Vers. liegen auseinder.
Die API Docu habe ich gelesen .
Da habe ich ja auch die "devide_id " Änderung her
Was mich so ersaunt: Das der Code mit dem 3.1 nicht mehr läuft und das ohne , für mich, erkennbaren Grund.
Ich habe doch ...nur .. das OS geupdated.
Da hätte ich mehr erwartet, auch in den Change.logs
der Lesebefehl wird gar nicht mehr geloggt
Der Schreibbefehl erzeugt nur noch 2 Zeilen, statt den gewohnten 10-15 Zeilen.
Und dann in den Zeilen ist die Ausgabe mehr als verwirrend:
2025-11-23 20:16:22,301 MainThread DEBUG base :72 Processing: 0xc9 0x6 0x0 0xc 0x0 0x0 0x59 0x81
2025-11-23 20:16:22,302 MainThread DEBUG decoders :79 decoded PDU function_code(6 sub -1) -> WriteSingleRegisterResponse(dev_id=0, transaction_id=0, address=12, count=0, bits
In der ersten Zeile ist die Client Adresse mit 0xc9 erkennbar
in der zweiten Zeile ist dann "(dev_id=0, " Das widerspricht sich enorm. Aber so strubbelig kann doch die pymodbus nicht sein! Das kann ich mir nicht vorstellen !!!!
Ich bin ziemlich "verwirrt" ganz vorsichtig ausgedrückt
Hat schon jemand Erfahrung mit der pymodbus 3.11.3
Für mich sieht es aus, als wenn die Debug Lines etwas anderes als RTU verarbeiten wollen ...?
Danke für jeden Hinweis
JR
Danke für die Hinweise.
Werde das Prog. überarbeiten, bzw Deine Korrekturen übernehmen.
Das Prog. ist nunmehr schon ziemlich verbastelt, weil ich auch versuchte, den Fehler einzugrenzen.
Zum Thema Modbus:
Ok, die Vers. liegen auseinder.
Die API Docu habe ich gelesen .
Da habe ich ja auch die "devide_id " Änderung her
Was mich so ersaunt: Das der Code mit dem 3.1 nicht mehr läuft und das ohne , für mich, erkennbaren Grund.
Ich habe doch ...nur .. das OS geupdated.
Da hätte ich mehr erwartet, auch in den Change.logs
der Lesebefehl wird gar nicht mehr geloggt
Der Schreibbefehl erzeugt nur noch 2 Zeilen, statt den gewohnten 10-15 Zeilen.
Und dann in den Zeilen ist die Ausgabe mehr als verwirrend:
2025-11-23 20:16:22,301 MainThread DEBUG base :72 Processing: 0xc9 0x6 0x0 0xc 0x0 0x0 0x59 0x81
2025-11-23 20:16:22,302 MainThread DEBUG decoders :79 decoded PDU function_code(6 sub -1) -> WriteSingleRegisterResponse(dev_id=0, transaction_id=0, address=12, count=0, bits
In der ersten Zeile ist die Client Adresse mit 0xc9 erkennbar
in der zweiten Zeile ist dann "(dev_id=0, " Das widerspricht sich enorm. Aber so strubbelig kann doch die pymodbus nicht sein! Das kann ich mir nicht vorstellen !!!!
Ich bin ziemlich "verwirrt" ganz vorsichtig ausgedrückt
Hat schon jemand Erfahrung mit der pymodbus 3.11.3
Für mich sieht es aus, als wenn die Debug Lines etwas anderes als RTU verarbeiten wollen ...?
Danke für jeden Hinweis
JR
- __blackjack__
- User
- Beiträge: 14238
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@jr1: Bei der langen Liste an Änderungen zwischen den Bibliotheksversionen wundert mich nicht dass man da was umschreiben muss. Die haben da einiges geändert in den grob 11 Versionen mit API-Änderungen, die dazwischen liegen. Die haben da ja streckenweise in den letzten 3 Jahren monatlich eine Version mit API-Änderungen raus gehauen.
Hast Du das Logging für `pymodbus` denn konfiguriert? Vielleicht waren denen oder den Nutzern das früher auch einfach zu viele Logausgaben.
Hast Du das Logging für `pymodbus` denn konfiguriert? Vielleicht waren denen oder den Nutzern das früher auch einfach zu viele Logausgaben.
“Ich bin für die Todesstrafe. Wer schreckliche Dinge getan hat, muss eine angemessene Strafe bekommen. So lernt er seine Lektion für das nächste Mal.” — Britney Spears, Interview in der französischen Zeitung Libération, 2. April 2002
- DeaD_EyE
- User
- Beiträge: 1297
- Registriert: Sonntag 19. September 2010, 13:45
- Wohnort: Hagen
- Kontaktdaten:
Ja, schon krass die Anzahl der Änderungen: https://pymodbus.readthedocs.io/en/late ... ges-3-12-0
Aber das ergibt sich halt, wenn man alte Sünden beseitigen will oder neue Features anbieten möchte, die dann Änderungen erfordern.
Aber das ergibt sich halt, wenn man alte Sünden beseitigen will oder neue Features anbieten möchte, die dann Änderungen erfordern.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
@jr1: Wir müssen kurz über den Elefanten lm Raum sprechen: Python 3.8 ist schon ein Jahr EOL. 3.9 jetzt gerade. pymodbus scheint die Unterstützung für abgelaufene Python Versionen auch zeitnah zu droppen.
Zur Softwareentwicklung gehört auch, so etwas im Blick zu haben und eigene Software an die Zyklen anzupassen.
Zur Softwareentwicklung gehört auch, so etwas im Blick zu haben und eigene Software an die Zyklen anzupassen.
- __blackjack__
- User
- Beiträge: 14238
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
Ergänzend, man kann natürlich jetzt im Nachhinein trotzdem die kleinen Schritte machen. Also venv anlegen und dort drin dann pymodbus 3.0.x installieren, und dass dann in mehreren Schritten auf 3.11.x aktualisieren, und immer den eigenen Code nachbessern. Dann startet man mit einer funktionierenden Software und hat nicht die ganzen Änderungen auf einen Schlag zu bewältigen.
“Ich bin für die Todesstrafe. Wer schreckliche Dinge getan hat, muss eine angemessene Strafe bekommen. So lernt er seine Lektion für das nächste Mal.” — Britney Spears, Interview in der französischen Zeitung Libération, 2. April 2002
Danke für die Tips.
Und vielen Dank fürs Aufräumen des Codes @blackjack
Es war mein Fehler, bzw. ich habe die Änderung nicht gesehen, aber auch nicht beachtet, weil ich es als unwichtig erachtet hatte:
V1: rr = client.read_holding_registers(1, 2, device_id=201)
V2: rr = client.read_holding_registers(1, count=2, device_id=201)
Der Code in V2 funktioniert.
Daher die Frage: Wie heisst diese Thematik, bzw, wo/wie kann ich lesen, was der Unterschied ist, bzw wie erkenne ich die Anforderung in Libs ?
OK, mir war schon aufgefallen, das einmal Parameter als Wert übergeben werden, und das andere Mal mit "Identifier"........
Aber das dies diese Auswirkungen hat......
Mein "alter Code" lief doch ....grübel
Vielen Dank für die Hinweise
JR
Und vielen Dank fürs Aufräumen des Codes @blackjack
Es war mein Fehler, bzw. ich habe die Änderung nicht gesehen, aber auch nicht beachtet, weil ich es als unwichtig erachtet hatte:
V1: rr = client.read_holding_registers(1, 2, device_id=201)
V2: rr = client.read_holding_registers(1, count=2, device_id=201)
Der Code in V2 funktioniert.
Daher die Frage: Wie heisst diese Thematik, bzw, wo/wie kann ich lesen, was der Unterschied ist, bzw wie erkenne ich die Anforderung in Libs ?
OK, mir war schon aufgefallen, das einmal Parameter als Wert übergeben werden, und das andere Mal mit "Identifier"........
Aber das dies diese Auswirkungen hat......
Mein "alter Code" lief doch ....grübel
Vielen Dank für die Hinweise
JR
- noisefloor
- User
- Beiträge: 4251
- Registriert: Mittwoch 17. Oktober 2007, 21:40
- Wohnort: WW
- Kontaktdaten:
Hallo,
Wie heisst diese Thematik, bzw, wo/wie kann ich lesen, was der Unterschied ist, .../quote]
Änderungen jeglicher Art sind im Changelog dokumentiert - den jedes ordentliche Projekt auch hat. Der Changelog ist immer von Version zu Version, d.h. wenn du viele Versionen überspringst, muss du viele Changelogs lesen. Kann ein wenig mühsam sein.
Doku lesen. Jedes vernünftige Modul sollte eine Doku inkl. API Doku haben, wo drin steht, welche Parameter eine Funktion / Methode erwartet, welche optional sind etc.... bzw wie erkenne ich die Anforderung in Libs ?[
Gruß, noisefloor
Hm,
meinte den Unterschied in Py. Code
client.read_holding_registers(1, count=2, device_id=201)
der zweite Parameter muss nun mit "count=" übergeben werden ......
Die Doku zu pymodbus hatte ich gelesen, aber auf die "Kleinigkeit" nicht geachtet.
Die Funkionalität kenne ich aus anderen Prog.Sprachen nicht.
Kleiner Unterschied, aber gr. Auswirkung
Vielen Dank
JR
meinte den Unterschied in Py. Code
client.read_holding_registers(1, count=2, device_id=201)
der zweite Parameter muss nun mit "count=" übergeben werden ......
Die Doku zu pymodbus hatte ich gelesen, aber auf die "Kleinigkeit" nicht geachtet.
Die Funkionalität kenne ich aus anderen Prog.Sprachen nicht.
Kleiner Unterschied, aber gr. Auswirkung
Vielen Dank
JR
Aber du solltest eine ausagefähige Fehlermeldung bekommen.
TypeError: read_holding_registers() takes 1 positional argument but 2 were given
"Keyword-Only Arguments" werden im offiziellen Tutorial beschrieben und fußen auf PEP 3102.
TypeError: read_holding_registers() takes 1 positional argument but 2 were given
"Keyword-Only Arguments" werden im offiziellen Tutorial beschrieben und fußen auf PEP 3102.
- noisefloor
- User
- Beiträge: 4251
- Registriert: Mittwoch 17. Oktober 2007, 21:40
- Wohnort: WW
- Kontaktdaten:
Hallo,
Bzgl den Argumentarten: Python kennt positional, named und optional arguments. Das sind eigentlich Python Grundlagen, die man lernt, wenn man sich mit Funktionen und Methoden beschäftigt.
Gruß, noisefloor
Na ja, es ist halt keine "Kleinigkeit". Das Programm macht halt genau das - und nur genau das - was der Programmierer vorgibt.Die Doku zu pymodbus hatte ich gelesen, aber auf die "Kleinigkeit" nicht geachtet.
Bzgl den Argumentarten: Python kennt positional, named und optional arguments. Das sind eigentlich Python Grundlagen, die man lernt, wenn man sich mit Funktionen und Methoden beschäftigt.
Keyword Arguments gibt es auch in etlichen anderen Programmiersprachen inkl. solchen, die Mainstream sind. Siehe z.B. https://en.wikipedia.org/wiki/Named_parameter.Die Funkionalität kenne ich aus anderen Prog.Sprachen nicht.
Gruß, noisefloor
- __blackjack__
- User
- Beiträge: 14238
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@noisefloor: Der Knackpunkt hier war ja „keyword _only_“. Das ist schon etwas spezieller und in Python ja auch nicht von Anfang an vorhanden gewesen was die Syntax angeht. (Seit Python 3.8 glaube ich.)
Andererseits ist das sehr einfach erkennbar / lösbar weil die Fehlermeldung einen da recht deutlich drauf stösst.
Andererseits ist das sehr einfach erkennbar / lösbar weil die Fehlermeldung einen da recht deutlich drauf stösst.
“Ich bin für die Todesstrafe. Wer schreckliche Dinge getan hat, muss eine angemessene Strafe bekommen. So lernt er seine Lektion für das nächste Mal.” — Britney Spears, Interview in der französischen Zeitung Libération, 2. April 2002
