Hallo Python Community,
ich habe ein Problem, der ModbusClient fragt spezielle Addressbereiche ab und auf diesen Adressbereich möchte ich im ModbusTCP Server reagieren.
Beispiel aus einem Wireshark log
Functionscode 3
Reference Number 1050
Word Count 100
das heißt ab "adresse" 1050 lese 100 Wörter, ich würde gerne im Pymodbus server die Zahl 1050 abgreifen, finde aber im Moment keine Möglichkeit diese Zahl zu bekommen.
Ich benötige die 1050 um darauf zu reagieren und ein zusätzliches Ereignis auszulösen.
Vielen Dank und viele Grüße,
McAce_2
Pymodbus Reference Number "address" abfrage im Server
Sorry vergessen, leider habe ich auf den Client keinen Einfluss, die Kommunikation zwischen Client und Server ist stabil und funktioniert auch.
Mir geht es halt um das reagieren auf einen bestimmten Adressbereich
Mir geht es halt um das reagieren auf einen bestimmten Adressbereich
Code: Alles auswählen
class ProtocolData:
def get_parameter_list(self):
parameter_list = [e for e in range(0,1550)]
return parameter_list
class ModbusServerTCP:
"""
Service to receive data from ModbusClient, will run on feeder
:author: Patrick Litzbarski
"""
def __init__(self):
self.identity = ModbusDeviceIdentification()
self.identity.VendorName = 'Pymodbus'
self.identity.ProductCode = 'PM'
self.identity.VendorUrl = 'http://github.com/riptideio/pymodbus/'
self.identity.ProductName = 'Pymodbus Server'
self.identity.ModelName = 'Pymodbus Server'
self.identity.MajorMinorRevision = '1.0'
# self.address = '10.13.0.130'
self.address = 'localhost'
self.port = 5020
self.interval = 1
def set_parameter_object(self, parameter_wrapper):
self.parameter_wrapper = parameter_wrapper
def set_parameter_object_2(self, parameter_wrapper):
parameter_wrapper.live_data = self.parameter_wrapper.live_data
self.parameter_wrapper = parameter_wrapper
def run_server(self):
# Holding Register (ir) FC4 (Address, [content]*number)
# codes
# 1 = c # 2 = d # 3 = h # 4 = i # 5 = c # 6 = h # 15 = c # 16 = h
# h = 16 ,6, 3 # i = 4 # c = 5, # d = 2
# parameter_list = ProtocolData().get_parameter_list(self.parameter_wrapper_null)
parameter_list = ProtocolData().get_parameter_list()
store = ModbusSlaveContext(
di=ModbusSequentialDataBlock(0, [0]*1000),
co=ModbusSequentialDataBlock(0, [0]*1000),
ir=ModbusSequentialDataBlock(0, [0]*1000),
hr=ModbusSequentialDataBlock(0, parameter_list))
context = ModbusServerContext(slaves=store, single=True)
server = ModbusTcpServer(context, identity=self.identity,
address=(self.address, self.port))
# print(f'store Values {store.getValues(4,0,100)}')
print(f'framer {server.framer}')
t = threading.Thread(target=server.serve_forever, daemon=True)
t.start()
loop = LoopingCall(f=self.updatevalues, a=server)
loop.start(self.interval, now=True)
reactor.run()
def updatevalues(self, a):
start = time.time()
print("------------START----------")
server_context = a.context[0x01]
print("-------------END-------------")
end = time.time()
print("Zeit %s" % str(end - start))
Okay ich dachte die sind nicht so wichtig
Code: Alles auswählen
from pymodbus.server.sync import StartTcpServer, ModbusTcpServer
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
Ich gehe noch etwas mehr auf dem Problem ein.
Mit der Adresse 16 wird ein Wert in die Variable x geschrieben aber mit der Adresse 1060 wird auch der Wert in Adresse x geschrieben. Wenn jetzt nur die Adresse 16 geschrieben wird z.B 50
dann wird x durch
überschrieben
Meine Idee war jetzt die 1060 abzufangen
und sowas wie
zu verwenden.
Fragt nicht nach Sinn das ist so gegeben und da kann ich nichts dran ändern
Ich vermute mal das das mit den updatevalues() nicht so optimal ist, nur wüsste ich nicht wie ich sonst aus dem Modbus die Werte in die Variable x bekomme.
Mit der Adresse 16 wird ein Wert in die Variable x geschrieben aber mit der Adresse 1060 wird auch der Wert in Adresse x geschrieben. Wenn jetzt nur die Adresse 16 geschrieben wird z.B 50
Code: Alles auswählen
x= server_context.getValues(3, 16, 2)
Code: Alles auswählen
x = server_context.getValues(3, 1060, 2)
Meine Idee war jetzt die 1060 abzufangen
und sowas wie
Code: Alles auswählen
if addresse == 1060:
x = server_context.getValues(3, 1060, 2)
Fragt nicht nach Sinn das ist so gegeben und da kann ich nichts dran ändern

Ich vermute mal das das mit den updatevalues() nicht so optimal ist, nur wüsste ich nicht wie ich sonst aus dem Modbus die Werte in die Variable x bekomme.
- __blackjack__
- User
- Beiträge: 13919
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@McAce_2: Ich habe so gut wie nix verstanden. Was ist `x`? Im Text klingt das so als wäre das etwas in einem Gerät an einer Adresse, im Code ist das aber ein Name in Python.
Und sorry, aber bei:
Da muss ich einfach nach dem Sinn fragen beziehungsweise feststellen: das macht keinen Sinn. Das ist wenn man nur lesen zugreifen muss einfach ``parameters = range(1550)`` oder wenn darauf auch geschrieben wird ``parameters = list(range(1550))``. Die ”Klasse” ist einfach nur hochgradig unsinnig.
Und sorry, aber bei:
Code: Alles auswählen
class ProtocolData:
def get_parameter_list(self):
parameter_list = [e for e in range(0,1550)]
return parameter_list
# ...
# An anderer Stelle im Modul
parameter_list = ProtocolData().get_parameter_list()
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
— Scott Bellware
Natürlich ist die Klasse in dem Beispiel unsinnig, dort stehen die 1550 Parameter drin die verwendet werden, das macht aber kein Sinn die hier zu posten. Es wird eine Liste erzeugt die dem entspricht was ich hier verwende. nur auf das wesentliche reduziert. Nämlich eine Liste mit 1550 Elementen.__blackjack__ hat geschrieben: ↑Donnerstag 12. März 2020, 13:21 @McAce_2: Ich habe so gut wie nix verstanden. Was ist `x`? Im Text klingt das so als wäre das etwas in einem Gerät an einer Adresse, im Code ist das aber ein Name in Python.
Und sorry, aber bei:Da muss ich einfach nach dem Sinn fragen beziehungsweise feststellen: das macht keinen Sinn. Das ist wenn man nur lesen zugreifen muss einfach ``parameters = range(1550)`` oder wenn darauf auch geschrieben wird ``parameters = list(range(1550))``. Die ”Klasse” ist einfach nur hochgradig unsinnig.Code: Alles auswählen
class ProtocolData: def get_parameter_list(self): parameter_list = [e for e in range(0,1550)] return parameter_list # ... # An anderer Stelle im Modul parameter_list = ProtocolData().get_parameter_list()
Ja ich weiß das wenn man nicht möchte das etwas überschrieben werden soll eine andere Variable verwendet werden soll, aber an der Sache kann ich hier nichts ändern, wenn dem so wäre hätte ich das Problem nicht.
Und genau das Problem würde ich gerne über die Abfrage der Adresse umgehen.
- __blackjack__
- User
- Beiträge: 13919
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@McAce_2: Auch wenn da 1550 Parameter erzeugt werden ist das als Klasse so unsinnig und wäre einfach nur eine Funktion. Klassen von denen ein Objekt erzeugt wird nur um dann eine Methode darauf aufzurufen und danach das Objekt wegzuwerfen sind komisch. Eine Klasse *mit* `__init__()` und nur einer Methode ist ja schon ein „code smell“, aber eine Klasse *ohne* `__init__()` und einer einzigen “Methode“ ist halt einfach nur eine Funktion. Das ist Python, da muss man nicht zwanghaft alles in Klassen stopfen. Und falls die tatsächliche Klasse noch wieder anders aussieht und die Methode auch tatsächlich eine Methode ist: wir können halt nur von dem ausgehen was Du hier zeigst.
Ich verstehe immer noch nicht welches Problem Du da eigentlich zu lösen versuchst. Schreib doch mal ein kleines Beispiel das genau das Problem illustriert, lauffägig ist, und ohne `pymodbus` & Co auskommt. Also etwas das jeder hier problemlos einfach mal ausführen kann.
Ich verstehe immer noch nicht welches Problem Du da eigentlich zu lösen versuchst. Schreib doch mal ein kleines Beispiel das genau das Problem illustriert, lauffägig ist, und ohne `pymodbus` & Co auskommt. Also etwas das jeder hier problemlos einfach mal ausführen kann.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
— Scott Bellware
Ich würde sogar sagen eine Funktion, die nur den Sinn hat eine konstante Liste von Elementen zurückzugeben und sonst nichts tut ist überflüssig.
Also bleibt:
Das mag ich. Das ist übersichtlich.
Also bleibt:
Code: Alles auswählen
PARAMETERS = [e for e in range(0,1550)]
Ja ich weiß das die Klasse komisch aussieht, sie hat ihren Zweck hier erfüllt. 
Das ganze Konstrukt ist wesentlich komplizierter, und ändert nichts an dem Problem außer das die Sache unübersichtlich gewesen wäre und es hätte nichts an dem Problem geändert, weshalb ich darauf verzichtet habe das hier zu posten.
Aber gut das du darauf hinweist das das kein guter Python Code ist, das ist er so in der Tat wirklich nicht.
Ich habe das Problem gelöst, falls es jemand interessiert.
so setze ich den Bereich in 1060 mit dem Wert der in 16 steht.
Jetzt wird x immer noch zweimal beschrieben aber mit demselben Wert, und mehr wollte ich nicht, bzw. auf mehr habe ich keinen Einfluss, da der Rest halt gegeben ist und nicht abgeändert werden kann, leider.

Das ganze Konstrukt ist wesentlich komplizierter, und ändert nichts an dem Problem außer das die Sache unübersichtlich gewesen wäre und es hätte nichts an dem Problem geändert, weshalb ich darauf verzichtet habe das hier zu posten.
Aber gut das du darauf hinweist das das kein guter Python Code ist, das ist er so in der Tat wirklich nicht.

Ich habe das Problem gelöst, falls es jemand interessiert.
Code: Alles auswählen
x = server_context.getValues(3, 16, 2)
server_context.setValues(3, 1060, server_context.getValues(3, 16, 2))
Jetzt wird x immer noch zweimal beschrieben aber mit demselben Wert, und mehr wollte ich nicht, bzw. auf mehr habe ich keinen Einfluss, da der Rest halt gegeben ist und nicht abgeändert werden kann, leider.
Ich kann beim besten Willen nicht Deine „Lösung” und mit Deiner Problembeschreibung zusammen bekommen.
Was hat denn die ganze Klasse mit dem simplen übergeben eines Wertes in eine Funktion zu tun?
Was hat denn die ganze Klasse mit dem simplen übergeben eines Wertes in eine Funktion zu tun?
Code: Alles auswählen
x = server_context.getValues(3, 16, 2)
server_context.setValues(3, 1060, x)
Ich denke hier herrscht Verwirrung weil Modbus als eher spezielles Protokoll im Spiel ist. Da scheinen die Clients als Register-Raum dargestellt zu sein, aus dem man eben ab einer bestimmten Adresse eine Menge an Werten liest oder schreibt. Und ich vermute mal stark unser TE moechte an der Stelle einhaken, und die Daten erzeugn, statt das einfach auf eine Liste oder so abzubilden. Darum habe ich auch nach der Server-Version gefragt. Damit man sehen kann, wie ein solches Request konkret reinkommt, und dann verarbeitet wird.
Ich denke der Ansatz ist das Erstellen eines eigenen Block Storages: https://github.com/riptideio/pymodbus/b ... ore.py#L62
Den wuerde ich ableiten und einhaengen, mit prints/debugger statements versehen, um die callbacks zu verstehen.
Ich denke der Ansatz ist das Erstellen eines eigenen Block Storages: https://github.com/riptideio/pymodbus/b ... ore.py#L62
Den wuerde ich ableiten und einhaengen, mit prints/debugger statements versehen, um die callbacks zu verstehen.
Die Klasse hat nichts mit dem Problem zu tun, ich weiß nicht warum das thematisiert worden ist, wahrscheinlich weil das eine schlecht gekürzte Version ist, die nicht dem Python Standard entspricht.
Mein Problem war das eine Variable x = server_context.getValues(3, 16, 2) gesetzt wurde und die Variable x wurde auch durch x = server_context.getValues(3, 1060, 2) gesetzt.
Nur das unter bestimmten Bedingen x = server_context.getValues(3, 16, 2) mit einem Validen Wert gesetzt wird und danach mit einem nicht validen Wert durch x = server_context.getValues(3, 1060, 2) gesetzt wird.
Weil x =server_context.getValues(3, 16, 2) zuerst gesetzt wird und im Anschluß x=server_context.getValues(3, 106,0 2) gesetzt wird ist in x ein nicht valider Wert gespeichert worden.
Das Problem wollte ich durch eine Abfrage der Adresse 1060 umgehen, was aber nicht zu funktionieren scheint. Jetzt habe ich das Problem so gelöst, dass wenn x=server_context.getValues(3, 16, 2) gesetzt wird, setze ich server_context.setValues(3, 1060, x) .
So wird x immer noch überschrieben, aber jetzt mit einem validen Wert.
Mein Problem war das eine Variable x = server_context.getValues(3, 16, 2) gesetzt wurde und die Variable x wurde auch durch x = server_context.getValues(3, 1060, 2) gesetzt.
Nur das unter bestimmten Bedingen x = server_context.getValues(3, 16, 2) mit einem Validen Wert gesetzt wird und danach mit einem nicht validen Wert durch x = server_context.getValues(3, 1060, 2) gesetzt wird.
Weil x =server_context.getValues(3, 16, 2) zuerst gesetzt wird und im Anschluß x=server_context.getValues(3, 106,0 2) gesetzt wird ist in x ein nicht valider Wert gespeichert worden.
Das Problem wollte ich durch eine Abfrage der Adresse 1060 umgehen, was aber nicht zu funktionieren scheint. Jetzt habe ich das Problem so gelöst, dass wenn x=server_context.getValues(3, 16, 2) gesetzt wird, setze ich server_context.setValues(3, 1060, x) .
So wird x immer noch überschrieben, aber jetzt mit einem validen Wert.
Genau, das ist das Problem gewesen und deswegen nur die Liste in einer Klasse. Das mit dem Ableiten hatte ich mir für den absoluten Worst Case aufgehoben.__deets__ hat geschrieben: ↑Donnerstag 12. März 2020, 14:17 Ich denke hier herrscht Verwirrung weil Modbus als eher spezielles Protokoll im Spiel ist. Da scheinen die Clients als Register-Raum dargestellt zu sein, aus dem man eben ab einer bestimmten Adresse eine Menge an Werten liest oder schreibt. Und ich vermute mal stark unser TE moechte an der Stelle einhaken, und die Daten erzeugn, statt das einfach auf eine Liste oder so abzubilden. Darum habe ich auch nach der Server-Version gefragt. Damit man sehen kann, wie ein solches Request konkret reinkommt, und dann verarbeitet wird.
Ich denke der Ansatz ist das Erstellen eines eigenen Block Storages: https://github.com/riptideio/pymodbus/b ... ore.py#L62
Den wuerde ich ableiten und einhaengen, mit prints/debugger statements versehen, um die callbacks zu verstehen.
Ich habe es jetzt anders gelöst, und das passt so.
x = server_context.getValues(3, 16, 2)
server_context.setValues(3, 1060, x)
Vielen Dank für die Hilfe.

Ich weiß wirklich nicht, wie ModBus hier hereinspielt, aber ein
sollte das Problem doch auch lösen.
Code: Alles auswählen
x = server_context.getValues(3, 16, 2)
irgendein_anderer_name_so_dass_x_nicht_ueberschrieben_wird = server_context.getValues(3, 1060, 2)
Ja das stimmt aber nicht bei meinem Problem, weil x unter zwei Bedingungen geschrieben wird wenn ich dann ein x_1 udn x_2 verwende weiß die nachgeschaltete Software nicht welchen Wert diese zu nehmen hat. Das Problem ist komplexer als ich nehme einfach eine zweite Variable.