From 06dda1d6b38e3237cee09c838047ee86635cba53 Mon Sep 17 00:00:00 2001 From: Sly Gryphon Date: Sat, 7 Jun 2025 20:29:42 +1000 Subject: [PATCH 1/3] refactor: update terminology to unit ID Modernizes the Modbus terminology by using "unit ID" throughout the codebase. This addresses issue #105 by: - Renaming parameters and variables to unit_id - Updating method signatures while maintaining backward compatibility - Revising documentation and comments to reflect current industry standards - Ensuring consistent terminology across the entire Modbus implementation This change improves code readability and aligns with current Modbus specification terminology while maintaining full backward compatibility. --- .gitignore | 4 +- scripts/suns.py | 8 ++-- sunspec2/modbus/client.py | 83 ++++++++++++++++++++++++++++++------ sunspec2/modbus/modbus.py | 90 +++++++++++++++++++-------------------- 4 files changed, 121 insertions(+), 64 deletions(-) diff --git a/.gitignore b/.gitignore index 176b3b1..ae30bb6 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,6 @@ __pycache__/ *.egg-info/ build/ -dist/ \ No newline at end of file +dist/ + +*.code-workspace diff --git a/scripts/suns.py b/scripts/suns.py index 6c3bb86..d916261 100644 --- a/scripts/suns.py +++ b/scripts/suns.py @@ -18,7 +18,7 @@ -o: output mode for data (text, xml) -x: export model description (slang, xml) -t: transport type: tcp or rtu (default: tcp) - -a: modbus slave address (default: 1) + -a: modbus unit identifier (default: 1) -i: ip address to use for modbus tcp (default: localhost) -P: port number for modbus tcp (default: 502) -p: serial port for modbus rtu (default: /dev/ttyUSB0) @@ -46,7 +46,7 @@ help='transport type: rtu, tcp, file [default: tcp]') parser.add_option('-a', metavar=' ', type='int', default=1, - help='modbus slave address [default: 1]') + help='modbus unit identifier [default: 1]') parser.add_option('-i', metavar=' ', default='localhost', help='ip address to use for modbus tcp [default: localhost]') @@ -72,10 +72,10 @@ try: if options.t == 'tcp': - sd = client.SunSpecModbusClientDeviceTCP(slave_id=options.a, ipaddr=options.i, ipport=options.P, + sd = client.SunSpecModbusClientDeviceTCP(unit_id=options.a, ipaddr=options.i, ipport=options.P, timeout=options.T) elif options.t == 'rtu': - sd = client.SunSpecModbusClientDeviceRTU(slave_id=options.a, name=options.p, baudrate=options.b, + sd = client.SunSpecModbusClientDeviceRTU(unit_id=options.a, name=options.p, baudrate=options.b, parity=options.R, timeout=options.T) elif options.t == 'file': sd = file_client.FileClientDevice(filename=options.m) diff --git a/sunspec2/modbus/client.py b/sunspec2/modbus/client.py index 00eef90..26aac09 100644 --- a/sunspec2/modbus/client.py +++ b/sunspec2/modbus/client.py @@ -22,6 +22,7 @@ import time import uuid +import warnings from sunspec2 import mdef, device, mb import sunspec2.modbus.modbus as modbus_client @@ -308,13 +309,52 @@ def scan(self, progress=None, delay=None, connect=True, full_model_read=True): if connected: self.disconnect() - class SunSpecModbusClientDeviceTCP(SunSpecModbusClientDevice): - def __init__(self, slave_id=1, ipaddr='127.0.0.1', ipport=502, timeout=None, ctx=None, trace_func=None, + """Provides access to a Modbus RTU device. + Parameters: + unit_id : + Modbus Unit Identifier. + ipaddr : + IP address of the Modbus TCP device. + ipport : + Port number for Modbus TCP. Default is 502 if not specified. + timeout : + Modbus request timeout in seconds. Fractional seconds are permitted + such as .5. + ctx : + Context variable to be used by the object creator. Not used by the + modbus module. + trace_func : + Trace function to use for detailed logging. No detailed logging is + perform is a trace function is not supplied. + max_count : + Maximum register count for a single Modbus request. + max_write_count : + Maximum register count for a single Modbus write request. + model_class : + Model class to use for creating models in the device. Default is + :class:`sunspec2.modbus.client.SunSpecModbusClientModel`. + slave_id : [DEPRECATED] Use unit_id instead. + Raises: + SunSpecModbusClientError: Raised for any general modbus client error. + SunSpecModbusClientTimeoutError: Raised for a modbus client request timeout. + SunSpecModbusClientException: Raised for an exception response to a modbus + client request. + """ + + def __init__(self, unit_id=1, ipaddr='127.0.0.1', ipport=502, timeout=None, ctx=None, trace_func=None, max_count=modbus_client.REQ_COUNT_MAX, max_write_count=modbus_client.REQ_WRITE_COUNT_MAX, - model_class=SunSpecModbusClientModel): + model_class=SunSpecModbusClientModel, slave_id=None): SunSpecModbusClientDevice.__init__(self, model_class=model_class) - self.slave_id = slave_id + if unit_id == 1 and slave_id is not None: + unit_id = slave_id + if slave_id is not None: + warnings.warn( + "The 'slave_id' parameter is deprecated and will be removed in a future version. Use 'unit_id' instead.", + DeprecationWarning, + stacklevel=2 + ) + self.unit_id = unit_id self.ipaddr = ipaddr self.ipport = ipport self.timeout = timeout @@ -324,7 +364,7 @@ def __init__(self, slave_id=1, ipaddr='127.0.0.1', ipport=502, timeout=None, ctx self.max_count = max_count self.max_write_count = max_write_count - self.client = modbus_client.ModbusClientTCP(slave_id=slave_id, ipaddr=ipaddr, ipport=ipport, timeout=timeout, + self.client = modbus_client.ModbusClientTCP(unit_id=unit_id, ipaddr=ipaddr, ipport=ipport, timeout=timeout, ctx=ctx, trace_func=trace_func, max_count=modbus_client.REQ_COUNT_MAX, max_write_count=modbus_client.REQ_WRITE_COUNT_MAX) @@ -351,8 +391,8 @@ def write(self, addr, data): class SunSpecModbusClientDeviceRTU(SunSpecModbusClientDevice): """Provides access to a Modbus RTU device. Parameters: - slave_id : - Modbus slave id. + unit_id : + Modbus Unit Identifier. name : Name of the serial port such as 'com4' or '/dev/ttyUSB0'. baudrate : @@ -373,6 +413,7 @@ class SunSpecModbusClientDeviceRTU(SunSpecModbusClientDevice): perform is a trace function is not supplied. max_count : Maximum register count for a single Modbus request. + slave_id : [DEPRECATED] Use unit_id instead. Raises: SunSpecModbusClientError: Raised for any general modbus client error. SunSpecModbusClientTimeoutError: Raised for a modbus client request timeout. @@ -380,12 +421,26 @@ class SunSpecModbusClientDeviceRTU(SunSpecModbusClientDevice): client request. """ - def __init__(self, slave_id, name, baudrate=None, parity=None, timeout=None, ctx=None, trace_func=None, + def __init__(self, unit_id=None, name=None, baudrate=None, parity=None, timeout=None, ctx=None, trace_func=None, max_count=modbus_client.REQ_COUNT_MAX, max_write_count=modbus_client.REQ_WRITE_COUNT_MAX, - model_class=SunSpecModbusClientModel): + model_class=SunSpecModbusClientModel, slave_id=None): # test if this super class init is needed SunSpecModbusClientDevice.__init__(self, model_class=model_class) - self.slave_id = slave_id + # Backward compatibility for slave_id + if unit_id is not None: + self.unit_id = unit_id + elif slave_id is not None: + self.unit_id = slave_id + else: + raise ValueError("unit_id must be provided") + if name is None: + raise ValueError("name must be provided") + if slave_id is not None: + warnings.warn( + "The 'slave_id' parameter is deprecated and will be removed in a future version. Use 'unit_id' instead.", + DeprecationWarning, + stacklevel=2 + ) self.name = name self.client = None self.ctx = ctx @@ -396,7 +451,7 @@ def __init__(self, slave_id, name, baudrate=None, parity=None, timeout=None, ctx self.client = modbus_client.modbus_rtu_client(name, baudrate, parity, timeout) if self.client is None: raise SunSpecModbusClientError('No modbus rtu client set for device') - self.client.add_device(self.slave_id, self) + self.client.add_device(self.unit_id, self) def open(self): self.client.open() @@ -406,7 +461,7 @@ def close(self): """ if self.client: - self.client.remove_device(self.slave_id) + self.client.remove_device(self.unit_id) def read(self, addr, count, op=modbus_client.FUNC_READ_HOLDING): """Read Modbus device registers. @@ -421,7 +476,7 @@ def read(self, addr, count, op=modbus_client.FUNC_READ_HOLDING): Byte string containing register contents. """ - return self.client.read(self.slave_id, addr, count, op=op, max_count=self.max_count) + return self.client.read(self.unit_id, addr, count, op=op, max_count=self.max_count) def write(self, addr, data): """Write Modbus device registers. @@ -432,4 +487,4 @@ def write(self, addr, data): Byte string containing register contents. """ - return self.client.write(self.slave_id, addr, data, max_write_count=self.max_write_count) + return self.client.write(self.unit_id, addr, data, max_write_count=self.max_write_count) diff --git a/sunspec2/modbus/modbus.py b/sunspec2/modbus/modbus.py index 200dad8..dc81c50 100644 --- a/sunspec2/modbus/modbus.py +++ b/sunspec2/modbus/modbus.py @@ -223,43 +223,43 @@ def close(self): except Exception as e: raise ModbusClientError('Serial close error: %s' % str(e)) - def add_device(self, slave_id, device): + def add_device(self, unit_id, device): """Add a device to the RTU client. Parameters: - slave_id : - Modbus slave id. + unit_id : + Modbus Unit Identifier. device : Device to add to the client. """ - self.devices[slave_id] = device + self.devices[unit_id] = device - def remove_device(self, slave_id): + def remove_device(self, unit_id): """Remove a device from the RTU client. Parameters: - slave_id : - Modbus slave id. + unit_id : + Modbus Unit Identifier. """ - if self.devices.get(slave_id): - del self.devices[slave_id] + if self.devices.get(unit_id): + del self.devices[unit_id] # if no more devices using the client interface, close and remove the client if len(self.devices) == 0: self.close() modbus_rtu_client_remove(self.name) - def _read(self, slave_id, addr, count, op=FUNC_READ_HOLDING): + def _read(self, unit_id, addr, count, op=FUNC_READ_HOLDING): resp = bytearray() len_remaining = 5 len_found = False except_code = None - req = struct.pack('>BBHH', int(slave_id), op, int(addr), int(count)) + req = struct.pack('>BBHH', int(unit_id), op, int(addr), int(count)) req += struct.pack('>H', computeCRC(req)) if self.trace_func: - # s = '{}:{}[addr={}] ->'.format(self.name, str(slave_id), addr) + # s = '{}:{}[addr={}] ->'.format(self.name, str(unit_id), addr) s = '> ' for c in req: s += '%02X' % c @@ -289,7 +289,7 @@ def _read(self, slave_id, addr, count, op=FUNC_READ_HOLDING): raise ModbusClientTimeout('Response timeout') if self.trace_func: - # s = '{}:{}[addr={}] <--'.format(self.name, str(slave_id), addr) + # s = '{}:{}[addr={}] <--'.format(self.name, str(unit_id), addr) s = '< ' for c in resp: s += '%02X' % c @@ -304,11 +304,11 @@ def _read(self, slave_id, addr, count, op=FUNC_READ_HOLDING): return resp[3:-2] - def read(self, slave_id, addr, count, op=FUNC_READ_HOLDING, max_count=REQ_COUNT_MAX): + def read(self, unit_id, addr, count, op=FUNC_READ_HOLDING, max_count=REQ_COUNT_MAX): """ Parameters: - slave_id : - Modbus slave id. + unit_id : + Modbus Unit Identifier. addr : Starting Modbus address. count : @@ -330,7 +330,7 @@ def read(self, slave_id, addr, count, op=FUNC_READ_HOLDING, max_count=REQ_COUNT_ read_count = max_count else: read_count = count - data = self._read(slave_id, addr + read_offset, read_count, op=op) + data = self._read(unit_id, addr + read_offset, read_count, op=op) if data: resp += data count -= read_count @@ -342,7 +342,7 @@ def read(self, slave_id, addr, count, op=FUNC_READ_HOLDING, max_count=REQ_COUNT_ return bytes(resp) - def _write(self, slave_id, addr, data): + def _write(self, unit_id, addr, data): resp = bytearray() len_remaining = 5 len_found = False @@ -351,13 +351,13 @@ def _write(self, slave_id, addr, data): len_data = len(data) count = int(len_data/2) - req = struct.pack('>BBHHB', int(slave_id), func, int(addr), count, len_data) + req = struct.pack('>BBHHB', int(unit_id), func, int(addr), count, len_data) req += data req += struct.pack('>H', computeCRC(req)) if self.trace_func: - # s = '{}:{}[addr={}] ->'.format(self.name, str(slave_id), addr) + # s = '{}:{}[addr={}] ->'.format(self.name, str(unit_id), addr) s = '> ' for c in req: s += '%02X' % c @@ -388,7 +388,7 @@ def _write(self, slave_id, addr, data): raise ModbusClientTimeout('Response timeout') if self.trace_func: - # s = '{}:{}[addr={}] <--'.format(self.name, str(slave_id), addr) + # s = '{}:{}[addr={}] <--'.format(self.name, str(unit_id), addr) s = '< ' for c in resp: s += '%02X' % c @@ -401,23 +401,23 @@ def _write(self, slave_id, addr, data): if except_code: raise ModbusClientException('Modbus exception: %d' % except_code) else: - resp_slave_id, resp_func, resp_addr, resp_count, resp_crc = struct.unpack('>BBHHH', bytes(resp)) - if resp_slave_id != slave_id or resp_func != func or resp_addr != addr or resp_count != count: + resp_unit_id, resp_func, resp_addr, resp_count, resp_crc = struct.unpack('>BBHHH', bytes(resp)) + if resp_unit_id != unit_id or resp_func != func or resp_addr != addr or resp_count != count: raise ModbusClientError('Modbus response format error') - def _write_single(self, slave_id, addr, data): + def _write_single(self, unit_id, addr, data): resp = bytearray() len_remaining = 5 len_found = False except_code = None func = FUNC_WRITE_SINGLE - req = struct.pack('>BBH', int(slave_id), func, int(addr)) + req = struct.pack('>BBH', int(unit_id), func, int(addr)) req += data req += struct.pack('>H', computeCRC(req)) if self.trace_func: - # s = '{}:{}[addr={}] ->'.format(self.name, str(slave_id), addr) + # s = '{}:{}[addr={}] ->'.format(self.name, str(unit_id), addr) s = '> ' for c in req: s += '%02X' % c @@ -448,7 +448,7 @@ def _write_single(self, slave_id, addr, data): raise ModbusClientTimeout('Response timeout') if self.trace_func: - # s = '{}:{}[addr={}] <--'.format(self.name, str(slave_id), addr) + # s = '{}:{}[addr={}] <--'.format(self.name, str(unit_id), addr) s = '< ' for c in resp: s += '%02X' % c @@ -461,16 +461,16 @@ def _write_single(self, slave_id, addr, data): if except_code: raise ModbusClientException('Modbus exception: %d' % except_code) else: - resp_slave_id, resp_func, resp_addr, resp_data, _ = struct.unpack('>BBHHH', bytes(resp)) - if (resp_slave_id != slave_id or resp_func != func or resp_addr != addr or + resp_unit_id, resp_func, resp_addr, resp_data, _ = struct.unpack('>BBHHH', bytes(resp)) + if (resp_unit_id != unit_id or resp_func != func or resp_addr != addr or resp_data != int.from_bytes(data, 'big')): raise ModbusClientError('Modbus response error') - def write(self, slave_id, addr, data, max_write_count=REQ_WRITE_COUNT_MAX): + def write(self, unit_id, addr, data, max_write_count=REQ_WRITE_COUNT_MAX): """ Parameters: - slave_id : - Modbus slave id. + unit_id : + Modbus Unit Identifier. addr : Starting Modbus address. data : @@ -484,7 +484,7 @@ def write(self, slave_id, addr, data, max_write_count=REQ_WRITE_COUNT_MAX): if self.serial is not None: if count == 1: # If only one register, use Func Code 0x06 - self._write_single(slave_id, addr, data) + self._write_single(unit_id, addr, data) else: while count > 0: if count > max_write_count: @@ -493,7 +493,7 @@ def write(self, slave_id, addr, data, max_write_count=REQ_WRITE_COUNT_MAX): write_count = count start = int(write_offset * 2) end = int((write_offset + write_count) * 2) - self._write(slave_id, addr + write_offset, data[start:end]) + self._write(unit_id, addr + write_offset, data[start:end]) count -= write_count write_offset += write_count else: @@ -501,9 +501,9 @@ def write(self, slave_id, addr, data, max_write_count=REQ_WRITE_COUNT_MAX): class ModbusClientTCP: - def __init__(self, slave_id=1, ipaddr='127.0.0.1', ipport=502, timeout=None, ctx=None, trace_func=None, + def __init__(self, unit_id=1, ipaddr='127.0.0.1', ipport=502, timeout=None, ctx=None, trace_func=None, max_count=REQ_COUNT_MAX, max_write_count=REQ_WRITE_COUNT_MAX): - self.slave_id = slave_id + self.unit_id = unit_id self.ipaddr = ipaddr self.ipport = ipport self.timeout = timeout @@ -563,10 +563,10 @@ def _read(self, addr, count, op=FUNC_READ_HOLDING): len_found = False except_code = None - req = struct.pack('>HHHBBHH', 0, 0, TCP_READ_REQ_LEN, int(self.slave_id), op, int(addr), int(count)) + req = struct.pack('>HHHBBHH', 0, 0, TCP_READ_REQ_LEN, int(self.unit_id), op, int(addr), int(count)) if self.trace_func: - # s = '%s:%s:%s[addr=%s] ->' % (self.ipaddr, str(self.ipport), str(self.slave_id), addr) + # s = '%s:%s:%s[addr=%s] ->' % (self.ipaddr, str(self.ipport), str(self.unit_id), addr) s = '> ' for c in req: s += '%02X' % c @@ -593,7 +593,7 @@ def _read(self, addr, count, op=FUNC_READ_HOLDING): except_code = resp[TCP_HDR_LEN + 2] if self.trace_func: - # s = '%s:%s:%s[addr=%s] <--' % (self.ipaddr, str(self.ipport), str(self.slave_id), addr) + # s = '%s:%s:%s[addr=%s] <--' % (self.ipaddr, str(self.ipport), str(self.unit_id), addr) s ='< ' for c in resp: s += '%02X' % c @@ -663,12 +663,12 @@ def _write(self, addr, data): write_len = len(data) write_count = int(write_len/2) - req = struct.pack('>HHHBBHHB', 0, 0, TCP_WRITE_MULT_REQ_LEN + write_len, int(self.slave_id), + req = struct.pack('>HHHBBHHB', 0, 0, TCP_WRITE_MULT_REQ_LEN + write_len, int(self.unit_id), func, int(addr), write_count, write_len) req += data if self.trace_func: - # s = '%s:%s:%s[addr=%s] ->' % (self.ipaddr, str(self.ipport), str(self.slave_id), addr) + # s = '%s:%s:%s[addr=%s] ->' % (self.ipaddr, str(self.ipport), str(self.unit_id), addr) s = '> ' for c in req: s += '%02X' % c @@ -695,7 +695,7 @@ def _write(self, addr, data): except_code = resp[TCP_HDR_LEN + 2] if self.trace_func: - # s = '%s:%s:%s[addr=%s] <--' % (self.ipaddr, str(self.ipport), str(self.slave_id), addr) + # s = '%s:%s:%s[addr=%s] <--' % (self.ipaddr, str(self.ipport), str(self.unit_id), addr) s = '< ' for c in resp: s += '%02X' % c @@ -716,12 +716,12 @@ def _write_single(self, addr, data): func = FUNC_WRITE_SINGLE write_len = len(data) - req = struct.pack('>HHHBBH', 0, 0, TCP_WRITE_SINGLE_REQ_LEN + write_len, int(self.slave_id), + req = struct.pack('>HHHBBH', 0, 0, TCP_WRITE_SINGLE_REQ_LEN + write_len, int(self.unit_id), func, int(addr)) req += data if self.trace_func: - # s = '%s:%s:%s[addr=%s] ->' % (self.ipaddr, str(self.ipport), str(self.slave_id), addr) + # s = '%s:%s:%s[addr=%s] ->' % (self.ipaddr, str(self.ipport), str(self.unit_id), addr) s = '> ' for c in req: s += '%02X' % c @@ -748,7 +748,7 @@ def _write_single(self, addr, data): except_code = resp[TCP_HDR_LEN + 2] if self.trace_func: - # s = '%s:%s:%s[addr=%s] <--' % (self.ipaddr, str(self.ipport), str(self.slave_id), addr) + # s = '%s:%s:%s[addr=%s] <--' % (self.ipaddr, str(self.ipport), str(self.unit_id), addr) s = '< ' for c in resp: s += '%02X' % c From 815d60cca317ca292f80afb18cabaf1c65bf9e14 Mon Sep 17 00:00:00 2001 From: Sly Gryphon Date: Sat, 7 Jun 2025 20:31:35 +1000 Subject: [PATCH 2/3] test: update tests to use unit ID terminology Updates test cases to align with the modernized unit ID terminology. Ensures all tests properly exercise the updated API while maintaining complete test coverage include backward compatibility tests. --- sunspec2/tests/test_modbus_client.py | 34 ++++++++++++++-------------- sunspec2/tests/test_modbus_modbus.py | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/sunspec2/tests/test_modbus_client.py b/sunspec2/tests/test_modbus_client.py index c0fdf4c..2623cb2 100644 --- a/sunspec2/tests/test_modbus_client.py +++ b/sunspec2/tests/test_modbus_client.py @@ -60,7 +60,7 @@ def test_read(self, monkeypatch): assert not d_tcp.common[0].SN.dirty # rtu - d_rtu = client.SunSpecModbusClientDeviceRTU(slave_id=1, name="COM2") + d_rtu = client.SunSpecModbusClientDeviceRTU(unit_id=1, name="COM2") rtu_buffer = [b'\x01\x03\x06Su', b'nS\x00\x01\x8d\xe4', b'\x01\x03\x02\x00B', @@ -111,7 +111,7 @@ def test_write(self, monkeypatch): monkeypatch.setattr(client.SunSpecModbusClientDeviceTCP, 'disconnect', MockSocket.mock_tcp_connect) # tcp - d_tcp = client.SunSpecModbusClientDeviceTCP(slave_id=1, ipaddr='127.0.0.1', ipport=8502) + d_tcp = client.SunSpecModbusClientDeviceTCP(unit_id=1, ipaddr='127.0.0.1', ipport=8502) # simulate a sequence of exchanges with the device tcp_buffer = [b'\x00\x00\x00\x00\x00\t\x01\x03\x06', # Readback first 6 registers b'SunS\x00\x01', # SunSpec ID + common model header (ID = 1) @@ -167,7 +167,7 @@ def test_write(self, monkeypatch): assert not d_tcp.common[0].SN.dirty # rtu - d_rtu = client.SunSpecModbusClientDeviceRTU(slave_id=1, name="COM2") + d_rtu = client.SunSpecModbusClientDeviceRTU(unit_id=1, name="COM2") rtu_buffer = [ # simulate a sequence of responses from the device scan b'\x01\x03\x06Su', b'nS\x00\x01\x8d\xe4', # Response: SunSpec ID + common model header (ID = 1) + CRC @@ -206,7 +206,7 @@ def test_write(self, monkeypatch): d_rtu.client.serial._set_buffer(rtu_read_buffer) d_rtu.common[0].write() - # 0x01: Slave Address (1). + # 0x01: Unit Address (1). # 0x03: Function Code (Read Holding Registers). T # 0x02: Byte Count (2). This tells you that the response contains 2 bytes of data. # 0x0002: Data (2 in decimal). This is the actual data read from the holding registers. DA = 2 @@ -228,7 +228,7 @@ def test_get_text(self, monkeypatch): monkeypatch.setattr(client.SunSpecModbusClientDeviceTCP, 'disconnect', MockSocket.mock_tcp_connect) # tcp - d_tcp = client.SunSpecModbusClientDeviceTCP(slave_id=1, ipaddr='127.0.0.1', ipport=8502) + d_tcp = client.SunSpecModbusClientDeviceTCP(unit_id=1, ipaddr='127.0.0.1', ipport=8502) tcp_buffer = [b'\x00\x00\x00\x00\x00\t\x01\x03\x06', b'SunS\x00\x01', b'\x00\x00\x00\x00\x00\x05\x01\x03\x02', @@ -258,7 +258,7 @@ def test_get_text(self, monkeypatch): expected_output = ' SN sn-123456789\n' assert d_tcp.common[0].SN.get_text() == expected_output - d_rtu = client.SunSpecModbusClientDeviceRTU(slave_id=1, name="COM2") + d_rtu = client.SunSpecModbusClientDeviceRTU(unit_id=1, name="COM2") rtu_buffer = [b'\x01\x03\x06Su', b'nS\x00\x01\x8d\xe4', b'\x01\x03\x02\x00B', @@ -297,7 +297,7 @@ def test_read(self, monkeypatch): monkeypatch.setattr(client.SunSpecModbusClientDeviceTCP, 'disconnect', MockSocket.mock_tcp_connect) # tcp - d_tcp = client.SunSpecModbusClientDeviceTCP(slave_id=1, ipaddr='127.0.0.1', ipport=8502) + d_tcp = client.SunSpecModbusClientDeviceTCP(unit_id=1, ipaddr='127.0.0.1', ipport=8502) tcp_buffer = [b'\x00\x00\x00\x00\x00\t\x01\x03\x06', b'SunS\x00\x01', b'\x00\x00\x00\x00\x00\x05\x01\x03\x02', @@ -413,7 +413,7 @@ def test_write(self, monkeypatch): monkeypatch.setattr(client.SunSpecModbusClientDeviceTCP, 'disconnect', MockSocket.mock_tcp_connect) # tcp - d_tcp = client.SunSpecModbusClientDeviceTCP(slave_id=1, ipaddr='127.0.0.1', ipport=8502) + d_tcp = client.SunSpecModbusClientDeviceTCP(unit_id=1, ipaddr='127.0.0.1', ipport=8502) tcp_buffer = [b'\x00\x00\x00\x00\x00\t\x01\x03\x06', b'SunS\x00\x01', b'\x00\x00\x00\x00\x00\x05\x01\x03\x02', @@ -483,7 +483,7 @@ def test_write(self, monkeypatch): assert not d_tcp.common[0].Vr.dirty # rtu - d_rtu = client.SunSpecModbusClientDeviceRTU(slave_id=1, name="COM2") + d_rtu = client.SunSpecModbusClientDeviceRTU(unit_id=1, name="COM2") rtu_buffer = [ b'\x01\x03\x06Su', b'nS\x00\x01\x8d\xe4', @@ -550,7 +550,7 @@ def test_get_text(self, monkeypatch): monkeypatch.setattr(client.SunSpecModbusClientDeviceTCP, 'disconnect', MockSocket.mock_tcp_connect) # tcp - d_tcp = client.SunSpecModbusClientDeviceTCP(slave_id=1, ipaddr='127.0.0.1', ipport=8502) + d_tcp = client.SunSpecModbusClientDeviceTCP(unit_id=1, ipaddr='127.0.0.1', ipport=8502) tcp_buffer = [b'\x00\x00\x00\x00\x00\t\x01\x03\x06', b'SunS\x00\x01', b'\x00\x00\x00\x00\x00\x05\x01\x03\x02', @@ -588,7 +588,7 @@ def test_get_text(self, monkeypatch): assert d_tcp.common[0].get_text() == expected_output # rtu - d_rtu = client.SunSpecModbusClientDeviceRTU(slave_id=1, name="COM2") + d_rtu = client.SunSpecModbusClientDeviceRTU(unit_id=1, name="COM2") rtu_buffer = [b'\x01\x03\x06Su', b'nS\x00\x01\x8d\xe4', b'\x01\x03\x02\x00B', @@ -620,7 +620,7 @@ def test_get_text(self, monkeypatch): class TestSunSpecModbusClientModel: def test___init__(self, monkeypatch): - d_rtu = client.SunSpecModbusClientDeviceRTU(slave_id=1, name="COM2") + d_rtu = client.SunSpecModbusClientDeviceRTU(unit_id=1, name="COM2") monkeypatch.setattr(serial, 'Serial', MockPort.mock_port) rtu_buffer = [ @@ -738,7 +738,7 @@ def test_get_text(self, monkeypatch): monkeypatch.setattr(client.SunSpecModbusClientDeviceTCP, 'disconnect', MockSocket.mock_tcp_connect) # tcp - d_tcp = client.SunSpecModbusClientDeviceTCP(slave_id=1, ipaddr='127.0.0.1', ipport=8502) + d_tcp = client.SunSpecModbusClientDeviceTCP(unit_id=1, ipaddr='127.0.0.1', ipport=8502) tcp_buffer = [b'\x00\x00\x00\x00\x00\t\x01\x03\x06', b'SunS\x00\x01', b'\x00\x00\x00\x00\x00\x05\x01\x03\x02', @@ -776,7 +776,7 @@ def test_get_text(self, monkeypatch): assert d_tcp.common[0].get_text() == expected_output # rtu - d_rtu = client.SunSpecModbusClientDeviceRTU(slave_id=1, name="COM2") + d_rtu = client.SunSpecModbusClientDeviceRTU(unit_id=1, name="COM2") rtu_buffer = [b'\x01\x03\x06Su', b'nS\x00\x01\x8d\xe4', b'\x01\x03\x02\x00B', @@ -806,7 +806,7 @@ def test_get_text(self, monkeypatch): assert d_rtu.common[0].get_text() == expected_output def test_read(self, monkeypatch): - d_rtu = client.SunSpecModbusClientDeviceRTU(slave_id=1, name="COM2") + d_rtu = client.SunSpecModbusClientDeviceRTU(unit_id=1, name="COM2") monkeypatch.setattr(serial, 'Serial', MockPort.mock_port) rtu_buffer = [b'\x01\x83\x02\xc0\xf1', @@ -1151,7 +1151,7 @@ def test_get_text(self, monkeypatch): class TestSunSpecModbusClientDeviceTCP: def test___init__(self): d = client.SunSpecModbusClientDeviceTCP() - assert d.slave_id == 1 + assert d.unit_id == 1 assert d.ipaddr == '127.0.0.1' assert d.ipport == 502 assert d.timeout is None @@ -1313,7 +1313,7 @@ class TestSunSpecModbusClientDeviceRTU: def test___init__(self, monkeypatch): monkeypatch.setattr(serial, 'Serial', MockPort.mock_port) d = client.SunSpecModbusClientDeviceRTU(1, "COMM2") - assert d.slave_id == 1 + assert d.unit_id == 1 assert d.name == "COMM2" assert d.client.__class__.__name__ == "ModbusClientRTU" assert d.ctx is None diff --git a/sunspec2/tests/test_modbus_modbus.py b/sunspec2/tests/test_modbus_modbus.py index e4ac92d..6171e46 100644 --- a/sunspec2/tests/test_modbus_modbus.py +++ b/sunspec2/tests/test_modbus_modbus.py @@ -126,7 +126,7 @@ def test_write(self, monkeypatch): class TestModbusClientTCP: def test___init__(self): c = modbus_client.ModbusClientTCP() - assert c.slave_id == 1 + assert c.unit_id == 1 assert c.ipaddr == '127.0.0.1' assert c.ipport == 502 assert c.timeout == 2 From 0deeff39128d159c62af6f56b9efddf3a21d8281 Mon Sep 17 00:00:00 2001 From: Sly Gryphon Date: Sat, 7 Jun 2025 20:32:16 +1000 Subject: [PATCH 3/3] docs: update documentation to use unit ID terminology --- README.rst | 12 ++++++------ sunspec2/docs/pysunspec.rst | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.rst b/README.rst index 7d81a45..6d0968c 100644 --- a/README.rst +++ b/README.rst @@ -165,20 +165,20 @@ type. TCP ^^^ -The following is how to open and initialize a TCP Device, where the slave ID is set to 1, the IP address of the TCP +The following is how to open and initialize a TCP Device, where the Unit ID is set to 1, the IP address of the TCP device is 127.0.0.1, and the port is 8502:: >>> import sunspec2.modbus.client as client - >>> d = client.SunSpecModbusClientDeviceTCP(slave_id=1, ipaddr='127.0.0.1', ipport=8502) + >>> d = client.SunSpecModbusClientDeviceTCP(unit_id=1, ipaddr='127.0.0.1', ipport=8502) RTU ^^^ -The following to open and initialize a RTU Device, where the slave ID is set to 1, and the name of the serial port is +The following to open and initialize a RTU Device, where the Unit ID is set to 1, and the name of the serial port is COM2:: >>> import sunspec2.modbus.client as client - >>> d = client.SunSpecModbusClientDeviceRTU(slave_id=1, name="COM2") + >>> d = client.SunSpecModbusClientDeviceRTU(unit_id=1, name="COM2") Device Image ^^^^^^^^^^^^ @@ -209,7 +209,7 @@ model ID. The first key is the model ID as an int, the second key is the model n that a device may contain more than one model with the same model ID, the dictionary keys refer to a list of model objects with that ID. Both keys refer to the same model list for a model ID. - >>> d = client.SunSpecModbusClientDeviceTCP(slave_id=1, ipaddr='127.0.0.1', ipport=8502) + >>> d = client.SunSpecModbusClientDeviceTCP(unit_id=1, ipaddr='127.0.0.1', ipport=8502) >>> d.scan() @@ -296,7 +296,7 @@ This section will go over the full steps on how to set a volt-var curve. Initialize device, and run device discovery with scan(): :: - >>> d = client.SunSpecModbusClientDeviceRTU(slave_id=1, name="COM2") + >>> d = client.SunSpecModbusClientDeviceRTU(unit_id=1, name="COM2") >>> d.scan() Confirm that model 705 (DERVoltVar) is on the device: :: diff --git a/sunspec2/docs/pysunspec.rst b/sunspec2/docs/pysunspec.rst index 10812db..2e098f2 100644 --- a/sunspec2/docs/pysunspec.rst +++ b/sunspec2/docs/pysunspec.rst @@ -146,20 +146,20 @@ type. TCP ^^^ -The following is how to open and initialize a TCP Device, where the slave ID is set to 1, the IP address of the TCP +The following is how to open and initialize a TCP Device, where the Unit ID is set to 1, the IP address of the TCP device is 127.0.0.1, and the port is 8502:: >>> import sunspec2.modbus.client as client - >>> d = client.SunSpecModbusClientDeviceTCP(slave_id=1, ipaddr='127.0.0.1', ipport=8502) + >>> d = client.SunSpecModbusClientDeviceTCP(unit_id=1, ipaddr='127.0.0.1', ipport=8502) RTU ^^^ -The following to open and initialize a RTU Device, where the slave ID is set to 1, and the name of the serial port is +The following to open and initialize a RTU Device, where the Unit ID is set to 1, and the name of the serial port is COM2:: >>> import sunspec2.modbus.client as client - >>> d = client.SunSpecModbusClientDeviceRTU(slave_id=1, name="COM2") + >>> d = client.SunSpecModbusClientDeviceRTU(unit_id=1, name="COM2") Device Image ^^^^^^^^^^^^ @@ -190,7 +190,7 @@ model ID. The first key is the model ID as an int, the second key is the model n that a device may contain more than one model with the same model ID, the dictionary keys refer to a list of model objects with that ID. Both keys refer to the same model list for a model ID. - >>> d = client.SunSpecModbusClientDeviceTCP(slave_id=1, ipaddr='127.0.0.1', ipport=8502) + >>> d = client.SunSpecModbusClientDeviceTCP(unit_id=1, ipaddr='127.0.0.1', ipport=8502) >>> d.scan() @@ -277,7 +277,7 @@ This section will go over the full steps on how to set a volt-var curve. Initialize device, and run device discovery with scan(): :: - >>> d = client.SunSpecModbusClientDeviceRTU(slave_id=1, name="COM2") + >>> d = client.SunSpecModbusClientDeviceRTU(unit_id=1, name="COM2") >>> d.scan() Confirm that model 705 (DERVoltVar) is on the device: ::