Skip to content

Commit

Permalink
Correct minor framer/pdu errors.
Browse files Browse the repository at this point in the history
  • Loading branch information
janiversen committed Oct 24, 2024
1 parent 9b97a18 commit 33c77b7
Show file tree
Hide file tree
Showing 12 changed files with 183 additions and 171 deletions.
15 changes: 5 additions & 10 deletions examples/client_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
sys.exit(-1)

import pymodbus.client as modbusClient
from pymodbus import ModbusException


_logger = logging.getLogger(__file__)
Expand Down Expand Up @@ -124,15 +123,11 @@ async def run_async_client(client, modbus_calls=None):

async def run_a_few_calls(client):
"""Test connection works."""
try:
rr = await client.read_coils(32, 1, slave=1)
assert len(rr.bits) == 8
rr = await client.read_holding_registers(4, 2, slave=1)
assert rr.registers[0] == 17
assert rr.registers[1] == 17
except ModbusException:
pass

rr = await client.read_coils(32, 1, slave=1)
assert len(rr.bits) == 8
rr = await client.read_holding_registers(4, 2, slave=1)
assert rr.registers[0] == 17
assert rr.registers[1] == 17

async def main(cmdline=None):
"""Combine setup and run."""
Expand Down
84 changes: 59 additions & 25 deletions examples/client_async_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
import logging
import sys

from pymodbus import ModbusException
from pymodbus.pdu import FileRecord


try:
import client_async
Expand All @@ -57,14 +60,14 @@ async def async_template_call(client):
"""Show complete modbus call, async version."""
try:
rr = await client.read_coils(1, 1, slave=SLAVE)
except client_async.ModbusException as exc:
except ModbusException as exc:
txt = f"ERROR: exception in pymodbus {exc}"
_logger.error(txt)
raise exc
if rr.isError():
txt = "ERROR: pymodbus returned an error!"
_logger.error(txt)
raise client_async.ModbusException(txt)
raise ModbusException(txt)

# Validate data
txt = f"### Template coils response: {rr.bits!s}"
Expand Down Expand Up @@ -163,6 +166,24 @@ async def async_handle_input_registers(client):
assert len(rr.registers) == 8


async def async_handle_file_records(client):
"""Read/write file records."""
_logger.info("### Read/write file records")
record = FileRecord(file_number=14, record_number=12, record_length=64)
rr = await client.read_file_record([record, record], slave=SLAVE)
assert not rr.isError()
assert len(rr.records) == 2
assert rr.records[0].record_data == b'SERVER DUMMY RECORD.'
assert rr.records[1].record_data == b'SERVER DUMMY RECORD.'
record.record_data = b'Pure test '
record.record_length = len(record.record_data) / 2
record = FileRecord(file_number=14, record_number=12, record_data=b'Pure test ')
rr = await client.write_file_record([record], slave=1)
assert not rr.isError()




async def async_execute_information_requests(client):
"""Execute extended information requests."""
_logger.info("### Running information requests.")
Expand All @@ -172,21 +193,21 @@ async def async_execute_information_requests(client):

rr = await client.report_slave_id(slave=SLAVE)
assert not rr.isError() # test that call was OK
# assert not rr.status
assert rr.status

rr = await client.read_exception_status(slave=SLAVE)
assert not rr.isError() # test that call was OK
# assert not rr.status
assert not rr.status

rr = await client.diag_get_comm_event_counter(slave=SLAVE)
assert not rr.isError() # test that call was OK
# assert rr.status
# assert not rr.count
assert rr.status
assert not rr.count

rr = await client.diag_get_comm_event_log(slave=SLAVE)
assert not rr.isError() # test that call was OK
# assert rr.status
# assert not (rr.event_count + rr.message_count + len(rr.events))
assert rr.status
assert not (rr.event_count + rr.message_count + len(rr.events))


async def async_execute_diagnostic_requests(client):
Expand All @@ -197,23 +218,35 @@ async def async_execute_diagnostic_requests(client):
assert not rr.isError() # test that call was OK
assert rr.message == message

await client.diag_restart_communication(True, slave=SLAVE)
await client.diag_read_diagnostic_register(slave=SLAVE)
await client.diag_change_ascii_input_delimeter(slave=SLAVE)

# NOT WORKING: await client.diag_force_listen_only(slave=SLAVE)

await client.diag_clear_counters()
await client.diag_read_bus_comm_error_count(slave=SLAVE)
await client.diag_read_bus_exception_error_count(slave=SLAVE)
await client.diag_read_slave_message_count(slave=SLAVE)
await client.diag_read_slave_no_response_count(slave=SLAVE)
await client.diag_read_slave_nak_count(slave=SLAVE)
await client.diag_read_slave_busy_count(slave=SLAVE)
await client.diag_read_bus_char_overrun_count(slave=SLAVE)
await client.diag_read_iop_overrun_count(slave=SLAVE)
await client.diag_clear_overrun_counter(slave=SLAVE)
# NOT WORKING await client.diag_getclear_modbus_response(slave=SLAVE)
rr = await client.diag_restart_communication(True, slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = await client.diag_read_diagnostic_register(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = await client.diag_change_ascii_input_delimeter(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = await client.diag_clear_counters()
assert not rr.isError() # test that call was OK
rr = await client.diag_read_bus_comm_error_count(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = await client.diag_read_bus_exception_error_count(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = await client.diag_read_slave_message_count(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = await client.diag_read_slave_no_response_count(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = await client.diag_read_slave_nak_count(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = await client.diag_read_slave_busy_count(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = await client.diag_read_bus_char_overrun_count(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = await client.diag_read_iop_overrun_count(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = await client.diag_clear_overrun_counter(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = await client.diag_getclear_modbus_response(slave=SLAVE)
assert not rr.isError() # test that call was OK
assert not await client.diag_force_listen_only(slave=SLAVE, no_response_expected=True)


# ------------------------
Expand All @@ -226,6 +259,7 @@ async def run_async_calls(client):
await async_handle_discrete_input(client)
await async_handle_holding_registers(client)
await async_handle_input_registers(client)
await async_handle_file_records(client)
await async_execute_information_requests(client)
await async_execute_diagnostic_requests(client)

Expand Down
86 changes: 55 additions & 31 deletions examples/client_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,57 +159,80 @@ def handle_input_registers(client):
assert len(rr.registers) == 8


def handle_file_records(_client):
"""Read/write file records."""
_logger.info("### Read/write file records")
# NOT WORKING:


def execute_information_requests(client):
"""Execute extended information requests."""
_logger.info("### Running information requests.")
rr = client.read_device_information(slave=SLAVE)
assert not rr.isError() # test that call was OK
assert rr.information[0] == b"Pymodbus"
# NOT WORKING: ONLY SYNC.
# FAILS WITH framer = RTU
# rr = client.read_device_information(slave=SLAVE)
# assert not rr.isError() # test that call was OK
# assert rr.information[0] == b"Pymodbus"

rr = client.report_slave_id(slave=SLAVE)
assert not rr.isError() # test that call was OK
# assert not rr.status
assert rr.status

rr = client.read_exception_status(slave=SLAVE)
assert not rr.isError() # test that call was OK
# assert not rr.status
assert not rr.status

rr = client.diag_get_comm_event_counter(slave=SLAVE)
assert not rr.isError() # test that call was OK
# assert rr.status
# assert not rr.count
assert rr.status
assert not rr.count

rr = client.diag_get_comm_event_log(slave=SLAVE)
assert not rr.isError() # test that call was OK
# assert rr.status
# assert not (rr.event_count + rr.message_count + len(rr.events))
assert rr.status
assert not (rr.event_count + rr.message_count + len(rr.events))


def execute_diagnostic_requests(client):
"""Execute extended diagnostic requests."""
_logger.info("### Running diagnostic requests.")
message = b"OK"
rr = client.diag_query_data(msg=message, slave=SLAVE)
assert not rr.isError() # test that call was OK
assert rr.message == message

client.diag_restart_communication(True, slave=SLAVE)
client.diag_read_diagnostic_register(slave=SLAVE)
client.diag_change_ascii_input_delimeter(slave=SLAVE)
# NOT WORKING: ONLY SYNC
# message = b"OK"
# rr = client.diag_query_data(msg=message, slave=SLAVE)
# assert not rr.isError() # test that call was OK
# assert rr.message == message

# NOT WORKING: await client.diag_force_listen_only(slave=SLAVE)
rr = client.diag_restart_communication(True, slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = client.diag_read_diagnostic_register(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = client.diag_change_ascii_input_delimeter(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = client.diag_clear_counters()
assert not rr.isError() # test that call was OK
rr = client.diag_read_bus_comm_error_count(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = client.diag_read_bus_exception_error_count(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = client.diag_read_slave_message_count(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = client.diag_read_slave_no_response_count(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = client.diag_read_slave_nak_count(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = client.diag_read_slave_busy_count(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = client.diag_read_bus_char_overrun_count(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = client.diag_read_iop_overrun_count(slave=SLAVE)
assert not rr.isError() # test that call was OK
rr = client.diag_clear_overrun_counter(slave=SLAVE)
assert not rr.isError() # test that call was OK
# NOT WORKING rr = client.diag_getclear_modbus_response(slave=SLAVE)
assert not rr.isError() # test that call was OK
# NOT WORKING: rr = client.diag_force_listen_only(slave=SLAVE)
assert not rr.isError() # test that call was OK

client.diag_clear_counters()
client.diag_read_bus_comm_error_count(slave=SLAVE)
client.diag_read_bus_exception_error_count(slave=SLAVE)
client.diag_read_slave_message_count(slave=SLAVE)
client.diag_read_slave_no_response_count(slave=SLAVE)
client.diag_read_slave_nak_count(slave=SLAVE)
client.diag_read_slave_busy_count(slave=SLAVE)
client.diag_read_bus_char_overrun_count(slave=SLAVE)
client.diag_read_iop_overrun_count(slave=SLAVE)
client.diag_clear_overrun_counter(slave=SLAVE)
# NOT WORKING client.diag_getclear_modbus_response(slave=SLAVE)


# ------------------------
Expand All @@ -222,8 +245,9 @@ def run_sync_calls(client):
handle_discrete_input(client)
handle_holding_registers(client)
handle_input_registers(client)
# awaiting fix: execute_information_requests(client)
# awaiting fix: execute_diagnostic_requests(client)
handle_file_records(client)
execute_information_requests(client)
execute_diagnostic_requests(client)


def main(cmdline=None):
Expand Down
9 changes: 4 additions & 5 deletions pymodbus/client/mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,23 +367,22 @@ def report_slave_id(self, slave: int = 1, no_response_expected: bool = False) ->
"""
return self.execute(no_response_expected, pdu_other_msg.ReportSlaveIdRequest(slave=slave))

def read_file_record(self, records: list[tuple], slave: int = 1, no_response_expected: bool = False) -> T:
def read_file_record(self, records: list[pdu_file_msg.FileRecord], slave: int = 1, no_response_expected: bool = False) -> T:
"""Read file record (code 0x14).
:param records: List of (Reference type, File number, Record Number, Record Length)
:param records: List of FileRecord (Reference type, File number, Record Number)
:param slave: device id
:param no_response_expected: (optional) The client will not expect a response to the request
:raises ModbusException:
"""
return self.execute(no_response_expected, pdu_file_msg.ReadFileRecordRequest(records, slave=slave))

def write_file_record(self, records: list[tuple], slave: int = 1, no_response_expected: bool = False) -> T:
def write_file_record(self, records: list[pdu_file_msg.FileRecord], slave: int = 1, no_response_expected: bool = False) -> T:
"""Write file record (code 0x15).
:param records: List of (Reference type, File number, Record Number, Record Length)
:param records: List of File_record (Reference type, File number, Record Number, Record Length, Record Data)
:param slave: (optional) Device id
:param no_response_expected: (optional) The client will not expect a response to the request
:param no_response_expected: (optional) The client will not expect a response to the request
:raises ModbusException:
"""
return self.execute(no_response_expected, pdu_file_msg.WriteFileRecordRequest(records,slave=slave))
Expand Down
1 change: 0 additions & 1 deletion pymodbus/framer/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ def __init__(
) -> None:
"""Initialize a ADU (framer) instance."""
self.decoder = decoder
self.databuffer = b""

def decode(self, _data: bytes) -> tuple[int, int, int, bytes]:
"""Decode ADU.
Expand Down
2 changes: 2 additions & 0 deletions pymodbus/pdu/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
"DecodePDU",
"ExceptionResponse",
"ExceptionResponse",
"FileRecord",
"ModbusExceptions",
"ModbusPDU",
]

from pymodbus.pdu.decoders import DecodePDU
from pymodbus.pdu.file_message import FileRecord
from pymodbus.pdu.pdu import ExceptionResponse, ModbusExceptions, ModbusPDU
2 changes: 1 addition & 1 deletion pymodbus/pdu/decoders.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ def decode(self, frame: bytes) -> base.ModbusPDU | None:
raise ModbusException(f"Unknown response {function_code}")
pdu = pdu_type()
pdu.setData(0, 0, False)
Log.debug("decode PDU for {}", function_code)
pdu.decode(frame[1:])
Log.debug("decoded PDU function_code({} sub {}) -> {} ", pdu.function_code, pdu.sub_function_code, str(pdu))

if pdu.sub_function_code >= 0:
lookup = self.sub_lookup.get(pdu.function_code, {})
Expand Down
Loading

0 comments on commit 33c77b7

Please sign in to comment.