Skip to content

Commit

Permalink
Merge 11ff445 into b97de0d
Browse files Browse the repository at this point in the history
  • Loading branch information
brainelectronics authored Jan 6, 2023
2 parents b97de0d + 11ff445 commit 6ef6f0d
Show file tree
Hide file tree
Showing 8 changed files with 299 additions and 22 deletions.
13 changes: 12 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
<!-- ## [Unreleased] -->

## Released
## [2.3.1] - 2023-01-06
### Added
- Unittest to read multiple coils at any location if defined as list, verifies #35
- Unittests to write a single coil or multiple coils at any location if defined as list, verifies fix #15 and #24

### Fixed
- All configured register of a client can be accessed and modified individually, see #35
- Resolved overlapping register positions in example JSON file
- Register length of `EXAMPLE_IREG` in TCP and RTU examples corrected to 1 instead of 2

## [2.3.0] - 2023-01-03
### Added
- Custom callback functions can be registered on client (ModbusRTU or ModbusTCP) side with new parameters `on_set_cb` and `on_get_cb` available from [modbus.py](umodbus/modbus.py) functions `add_coil` and `add_hreg`. Functions `add_ist` and `add_ireg` support only `on_get_cb`, see #31
Expand Down Expand Up @@ -233,8 +243,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- PEP8 style issues on all files of [`lib/uModbus`](lib/uModbus)

<!-- Links -->
[Unreleased]: https://github.com/brainelectronics/micropython-modbus/compare/2.3.0...develop
[Unreleased]: https://github.com/brainelectronics/micropython-modbus/compare/2.3.1...develop

[2.3.1]: https://github.com/brainelectronics/micropython-modbus/tree/2.3.1
[2.3.0]: https://github.com/brainelectronics/micropython-modbus/tree/2.3.0
[2.2.0]: https://github.com/brainelectronics/micropython-modbus/tree/2.2.0
[2.1.3]: https://github.com/brainelectronics/micropython-modbus/tree/2.1.3
Expand Down
2 changes: 1 addition & 1 deletion examples/rtu_client_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
"IREGS": {
"EXAMPLE_IREG": {
"register": 10,
"len": 2,
"len": 1,
"val": 60001
}
}
Expand Down
2 changes: 1 addition & 1 deletion examples/rtu_host_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
"IREGS": {
"EXAMPLE_IREG": {
"register": 10,
"len": 2,
"len": 1,
"val": 60001
}
}
Expand Down
2 changes: 1 addition & 1 deletion examples/tcp_client_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def reset_data_registers_cb(reg_type, address, val):
"IREGS": {
"EXAMPLE_IREG": {
"register": 10,
"len": 2,
"len": 1,
"val": 60001
}
}
Expand Down
2 changes: 1 addition & 1 deletion examples/tcp_host_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
"IREGS": {
"EXAMPLE_IREG": {
"register": 10,
"len": 2,
"len": 1,
"val": 60001
}
}
Expand Down
4 changes: 2 additions & 2 deletions registers/example.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"unit": ""
},
"ANOTHER_EXAMPLE_COIL": {
"register": 126,
"register": 127,
"len": 3,
"val": [0, 1, 0],
"description": "Example COILS registers with length of 3, Coils (setter+getter) [0, 1]",
Expand Down Expand Up @@ -97,7 +97,7 @@
"unit": ""
},
"ANOTHER_EXAMPLE_ISTS": {
"register": 69,
"register": 70,
"len": 3,
"val": [0, 1, 0],
"description": "Example ISTS registers with length of 3, Ists (only getter) [0, 1]",
Expand Down
177 changes: 177 additions & 0 deletions tests/test_tcp_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,39 @@ def test_read_coils_partially(self) -> None:
self.assertTrue(all(isinstance(x, bool) for x in coil_status))
self.assertEqual(coil_status, expectation_list_partial)

def test_read_coils_specific_of_multiple(self) -> None:
"""Test reading specific coils of client defined as list"""
# the offset based on the specified register
# e.g. register = 150, offset = 3, qty = 5, the requested coils are
# 153-158
base_coil_offset = 3
coil_qty = 5 # read only 5 coils of multiple defined ones

coil_address = (
self._register_definitions['COILS']['MANY_COILS']['register'] +
base_coil_offset
)
expectation_list_full = list(
map(bool,
self._register_definitions['COILS']['MANY_COILS']['val'])
)
expectation_list = expectation_list_full[
base_coil_offset:base_coil_offset + coil_qty
]

coil_status = self._host.read_coils(
slave_addr=self._client_addr,
starting_addr=coil_address,
coil_qty=coil_qty)

self.test_logger.debug(
'Status of COIL {} length {}: {}, expectation: {}'.
format(coil_address, coil_qty, coil_status, expectation_list))
self.assertIsInstance(coil_status, list)
self.assertEqual(len(coil_status), coil_qty)
self.assertTrue(all(isinstance(x, bool) for x in coil_status))
self.assertEqual(coil_status, expectation_list)

def test_read_discrete_inputs_single(self) -> None:
"""Test reading discrete inputs of client"""
ist_address = \
Expand Down Expand Up @@ -748,6 +781,73 @@ def test_write_single_coil(self) -> None:
self.assertTrue(all(isinstance(x, bool) for x in coil_status))
self.assertEqual(coil_status, expectation_list)

# test setting a coil in a list of coils
base_coil_offset = 3
coil_qty = 1
coil_address = (
self._register_definitions['COILS']['MANY_COILS']['register'] +
base_coil_offset
)
expectation_list_full = list(
map(bool,
self._register_definitions['COILS']['MANY_COILS']['val'])
)
expectation_list = expectation_list_full[
base_coil_offset:base_coil_offset + coil_qty
]

#
# Check clean system (client register data is as initially defined)
#
# verify current state by reading coil states
coil_status = self._host.read_coils(
slave_addr=self._client_addr,
starting_addr=coil_address,
coil_qty=coil_qty)

self.test_logger.debug(
'Initial status of COIL {}: {}, expectation: {}'.format(
coil_address,
coil_status,
expectation_list))
self.assertIsInstance(coil_status, list)
self.assertEqual(len(coil_status), coil_qty)
self.assertTrue(all(isinstance(x, bool) for x in coil_status))
self.assertEqual(coil_status, expectation_list)

#
# Test setting coil to True
#
# update coil state of client with a different than the current state
new_coil_val = not expectation_list[0]
expectation_list[0] = new_coil_val

operation_status = self._host.write_single_coil(
slave_addr=self._client_addr,
output_address=coil_address,
output_value=new_coil_val)

self.test_logger.debug(
'Result of setting COIL {} to {}: {}, expectation: {}'.format(
coil_address, new_coil_val, operation_status, True))
self.assertIsInstance(operation_status, bool)
self.assertTrue(operation_status)

# verify setting of state by reading data back again
coil_status = self._host.read_coils(
slave_addr=self._client_addr,
starting_addr=coil_address,
coil_qty=coil_qty)

self.test_logger.debug('Status of COIL {}: {}, expectation: {}'.
format(coil_address,
coil_status,
expectation_list))
self.assertIsInstance(coil_status, list)
self.assertEqual(len(coil_status), coil_qty)
self.assertTrue(all(isinstance(x, bool) for x in coil_status))
self.assertEqual(coil_status, expectation_list)

def test_write_single_register(self) -> None:
"""Test updating single holding register of client"""
hreg_address = \
Expand Down Expand Up @@ -934,6 +1034,83 @@ def test_write_multiple_coils(self) -> None:
# https://github.com/brainelectronics/micropython-modbus/issues/38
# self.assertEqual(coil_status, expectation_list)

def test_write_multiple_coils_specific_of_multiple(self) -> None:
"""Test updating specific coils of client defined as list"""
# test with more than 8 coils
coil_address = \
self._register_definitions['COILS']['MANY_COILS']['register']
coil_qty = \
self._register_definitions['COILS']['MANY_COILS']['len']
expectation_list = list(
map(bool, self._register_definitions['COILS']['MANY_COILS']['val'])
)

#
# Check clean system (client register data is as initially defined)
#
# verify current state by reading coil states
coil_status = self._host.read_coils(
slave_addr=self._client_addr,
starting_addr=coil_address,
coil_qty=coil_qty)

self.test_logger.debug(
'Initial status of COIL {} length {}: {}, expectation: {}'.format(
coil_address, coil_qty, coil_status, expectation_list))
self.assertIsInstance(coil_status, list)
self.assertEqual(len(coil_status), coil_qty)
self.assertTrue(all(isinstance(x, bool) for x in coil_status))
self.assertEqual(coil_status, expectation_list)

#
# Test setting coils to inverted initial states
#
# update coil states of client with a different than the current state
new_coil_vals_full = [not val for val in expectation_list]

# the offset based on the specified register
# e.g. register = 150, offset = 3, qty = 5, the requested coils are
# 153-158
base_coil_offset = 3
coil_qty = 5 # read only 5 coils of multiple defined ones

new_coil_vals = new_coil_vals_full[
base_coil_offset:(base_coil_offset + coil_qty)
]
expectation_list = (
expectation_list[:base_coil_offset] +
new_coil_vals +
expectation_list[base_coil_offset + coil_qty:]
)

operation_status = self._host.write_multiple_coils(
slave_addr=self._client_addr,
starting_address=coil_address,
output_values=new_coil_vals)

self.test_logger.debug(
'Result of setting COIL {} length {} to {}: {}, expectation: {}'.
format(
coil_address, coil_qty, new_coil_vals, operation_status, True))
self.assertIsInstance(operation_status, bool)
self.assertTrue(operation_status)

# verify setting of states by reading data back again
coil_status = self._host.read_coils(
slave_addr=self._client_addr,
starting_addr=coil_address,
coil_qty=coil_qty)

self.test_logger.debug(
'Status of COIL {} length {}: {}, expectation: {}'.format(
coil_address, coil_qty, coil_status, expectation_list))
self.assertIsInstance(coil_status, list)
self.assertEqual(len(coil_status), coil_qty)
self.assertTrue(all(isinstance(x, bool) for x in coil_status))
# Reading coil data bits is reversed, see #38
# https://github.com/brainelectronics/micropython-modbus/issues/38
# self.assertEqual(coil_status, expectation_list)

def test_write_multiple_registers(self) -> None:
"""Test updating multiple holding register of client"""
hreg_address = \
Expand Down
Loading

0 comments on commit 6ef6f0d

Please sign in to comment.