Skip to content

Serial forwarder demo doesnt work with write single register #2660

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
sthome-cz opened this issue May 3, 2025 · 3 comments
Open

Serial forwarder demo doesnt work with write single register #2660

sthome-cz opened this issue May 3, 2025 · 3 comments

Comments

@sthome-cz
Copy link

Steps to reproduce

Evidences

  • Communication on RTU side is as expected, enable (on) the switch
    • Request: 0xf7 0x6 0xb9 0xec 0x0 0x1 0xb8 0x35
    • Response: 0xf7 0x6 0xb9 0xec 0x0 0x1 0xb8 0x35
    • Critical section is tuple 0x0 0x1 starting on the 5th byte
  • Communication on TCP side is corrupted, as the packet is encoded like
    • Request: 0x82 0xcc 0x0 0x0 0x0 0x6 0xf7 0x6 0xb9 0xec 0x0 0x1
    • Response: 0x82 0xcc 0x0 0x0 0x0 0x6 0xf7 0x6 0xb9 0xec 0x0 0x0
    • Critical section is also 0x0 0x1 in request, two bytes from the end of the packet
    • And the corrupted section is 0x0 0x0 in the response, the last two bytes

The reason (in the code), why pymodbus works like this is in the code, where the condition makes to always return [0].

def getValues(self, fc_as_hex, _address, _count=1):
"""Get values from remote device."""
if fc_as_hex in self._write_fc:
return [0]
group_fx = self.decode(fc_as_hex)
func_fc = self.__get_callbacks[group_fx]
self.result = func_fc(_address, _count)
return self.__extract_result(self.decode(fc_as_hex), self.result)

Datastore is called during the execution of the execute in WriteSingleRegisterRequest object.

class WriteSingleRegisterRequest(WriteSingleRegisterResponse):
"""WriteSingleRegisterRequest."""
async def update_datastore(self, context: ModbusDeviceContext) -> ModbusPDU:
"""Run a write single register request against a datastore."""
if not 0 <= self.registers[0] <= 0xFFFF:
return ExceptionResponse(self.function_code, ExceptionResponse.ILLEGAL_VALUE)
rc = await context.async_setValues(
self.function_code, self.address, self.registers
)
if rc:
return ExceptionResponse(self.function_code, rc)
values = await context.async_getValues(self.function_code, self.address, 1)
return WriteSingleRegisterResponse(address=self.address, registers=cast(list[int], values))

Due to the condition above, the returned value after the WriteSingleRegisterRequest is always 0 and this will fail the command in the management application.

What is the correct way to fix this issue ? I have removed the condition for the test and the command is now executed successfully. Unfortunately I dont think, this is correct, because the program now executes the command to write the register value, then read its state back. AFAIK this shouldnt be necessary and the current state of the register is contained in the response on the RTU side.

Am I missing something, or is it a bug ?

@janiversen
Copy link
Collaborator

please add a debug log (using the pymodbus call) so we can see what happens.

The serial forwarder should forward the requests and pass the responses, however this is a very limited forwarder a real forwarder works at frame level and passes frames back and forth. We have for a long time wanted to make a real forwarder but never had the time to do so.

@janiversen
Copy link
Collaborator

calling the datastore during execute is correct and will not always return zero but the current value.

@janiversen
Copy link
Collaborator

janiversen commented May 4, 2025

pull requests are always welcome.

it do looks as if the "if" in get values are wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants