@@ -327,6 +327,39 @@ def test_read_coils_partially(self) -> None:
327
327
self .assertTrue (all (isinstance (x , bool ) for x in coil_status ))
328
328
self .assertEqual (coil_status , expectation_list_partial )
329
329
330
+ def test_read_coils_specific_of_multiple (self ) -> None :
331
+ """Test reading specific coils of client defined as list"""
332
+ # the offset based on the specified register
333
+ # e.g. register = 150, offset = 3, qty = 5, the requested coils are
334
+ # 153-158
335
+ base_coil_offset = 3
336
+ coil_qty = 5 # read only 5 coils of multiple defined ones
337
+
338
+ coil_address = (
339
+ self ._register_definitions ['COILS' ]['MANY_COILS' ]['register' ] +
340
+ base_coil_offset
341
+ )
342
+ expectation_list_full = list (
343
+ map (bool ,
344
+ self ._register_definitions ['COILS' ]['MANY_COILS' ]['val' ])
345
+ )
346
+ expectation_list = expectation_list_full [
347
+ base_coil_offset :base_coil_offset + coil_qty
348
+ ]
349
+
350
+ coil_status = self ._host .read_coils (
351
+ slave_addr = self ._client_addr ,
352
+ starting_addr = coil_address ,
353
+ coil_qty = coil_qty )
354
+
355
+ self .test_logger .debug (
356
+ 'Status of COIL {} length {}: {}, expectation: {}' .
357
+ format (coil_address , coil_qty , coil_status , expectation_list ))
358
+ self .assertIsInstance (coil_status , list )
359
+ self .assertEqual (len (coil_status ), coil_qty )
360
+ self .assertTrue (all (isinstance (x , bool ) for x in coil_status ))
361
+ self .assertEqual (coil_status , expectation_list )
362
+
330
363
def test_read_discrete_inputs_single (self ) -> None :
331
364
"""Test reading discrete inputs of client"""
332
365
ist_address = \
@@ -748,6 +781,73 @@ def test_write_single_coil(self) -> None:
748
781
self .assertTrue (all (isinstance (x , bool ) for x in coil_status ))
749
782
self .assertEqual (coil_status , expectation_list )
750
783
784
+ # test setting a coil in a list of coils
785
+ base_coil_offset = 3
786
+ coil_qty = 1
787
+ coil_address = (
788
+ self ._register_definitions ['COILS' ]['MANY_COILS' ]['register' ] +
789
+ base_coil_offset
790
+ )
791
+ expectation_list_full = list (
792
+ map (bool ,
793
+ self ._register_definitions ['COILS' ]['MANY_COILS' ]['val' ])
794
+ )
795
+ expectation_list = expectation_list_full [
796
+ base_coil_offset :base_coil_offset + coil_qty
797
+ ]
798
+
799
+ #
800
+ # Check clean system (client register data is as initially defined)
801
+ #
802
+ # verify current state by reading coil states
803
+ coil_status = self ._host .read_coils (
804
+ slave_addr = self ._client_addr ,
805
+ starting_addr = coil_address ,
806
+ coil_qty = coil_qty )
807
+
808
+ self .test_logger .debug (
809
+ 'Initial status of COIL {}: {}, expectation: {}' .format (
810
+ coil_address ,
811
+ coil_status ,
812
+ expectation_list ))
813
+ self .assertIsInstance (coil_status , list )
814
+ self .assertEqual (len (coil_status ), coil_qty )
815
+ self .assertTrue (all (isinstance (x , bool ) for x in coil_status ))
816
+ self .assertEqual (coil_status , expectation_list )
817
+
818
+ #
819
+ # Test setting coil to True
820
+ #
821
+ # update coil state of client with a different than the current state
822
+ new_coil_val = not expectation_list [0 ]
823
+ expectation_list [0 ] = new_coil_val
824
+
825
+ operation_status = self ._host .write_single_coil (
826
+ slave_addr = self ._client_addr ,
827
+ output_address = coil_address ,
828
+ output_value = new_coil_val )
829
+
830
+ self .test_logger .debug (
831
+ 'Result of setting COIL {} to {}: {}, expectation: {}' .format (
832
+ coil_address , new_coil_val , operation_status , True ))
833
+ self .assertIsInstance (operation_status , bool )
834
+ self .assertTrue (operation_status )
835
+
836
+ # verify setting of state by reading data back again
837
+ coil_status = self ._host .read_coils (
838
+ slave_addr = self ._client_addr ,
839
+ starting_addr = coil_address ,
840
+ coil_qty = coil_qty )
841
+
842
+ self .test_logger .debug ('Status of COIL {}: {}, expectation: {}' .
843
+ format (coil_address ,
844
+ coil_status ,
845
+ expectation_list ))
846
+ self .assertIsInstance (coil_status , list )
847
+ self .assertEqual (len (coil_status ), coil_qty )
848
+ self .assertTrue (all (isinstance (x , bool ) for x in coil_status ))
849
+ self .assertEqual (coil_status , expectation_list )
850
+
751
851
def test_write_single_register (self ) -> None :
752
852
"""Test updating single holding register of client"""
753
853
hreg_address = \
@@ -934,6 +1034,83 @@ def test_write_multiple_coils(self) -> None:
934
1034
# https://github.com/brainelectronics/micropython-modbus/issues/38
935
1035
# self.assertEqual(coil_status, expectation_list)
936
1036
1037
+ def test_write_multiple_coils_specific_of_multiple (self ) -> None :
1038
+ """Test updating specific coils of client defined as list"""
1039
+ # test with more than 8 coils
1040
+ coil_address = \
1041
+ self ._register_definitions ['COILS' ]['MANY_COILS' ]['register' ]
1042
+ coil_qty = \
1043
+ self ._register_definitions ['COILS' ]['MANY_COILS' ]['len' ]
1044
+ expectation_list = list (
1045
+ map (bool , self ._register_definitions ['COILS' ]['MANY_COILS' ]['val' ])
1046
+ )
1047
+
1048
+ #
1049
+ # Check clean system (client register data is as initially defined)
1050
+ #
1051
+ # verify current state by reading coil states
1052
+ coil_status = self ._host .read_coils (
1053
+ slave_addr = self ._client_addr ,
1054
+ starting_addr = coil_address ,
1055
+ coil_qty = coil_qty )
1056
+
1057
+ self .test_logger .debug (
1058
+ 'Initial status of COIL {} length {}: {}, expectation: {}' .format (
1059
+ coil_address , coil_qty , coil_status , expectation_list ))
1060
+ self .assertIsInstance (coil_status , list )
1061
+ self .assertEqual (len (coil_status ), coil_qty )
1062
+ self .assertTrue (all (isinstance (x , bool ) for x in coil_status ))
1063
+ self .assertEqual (coil_status , expectation_list )
1064
+
1065
+ #
1066
+ # Test setting coils to inverted initial states
1067
+ #
1068
+ # update coil states of client with a different than the current state
1069
+ new_coil_vals_full = [not val for val in expectation_list ]
1070
+
1071
+ # the offset based on the specified register
1072
+ # e.g. register = 150, offset = 3, qty = 5, the requested coils are
1073
+ # 153-158
1074
+ base_coil_offset = 3
1075
+ coil_qty = 5 # read only 5 coils of multiple defined ones
1076
+
1077
+ new_coil_vals = new_coil_vals_full [
1078
+ base_coil_offset :(base_coil_offset + coil_qty )
1079
+ ]
1080
+ expectation_list = (
1081
+ expectation_list [:base_coil_offset ] +
1082
+ new_coil_vals +
1083
+ expectation_list [base_coil_offset + coil_qty :]
1084
+ )
1085
+
1086
+ operation_status = self ._host .write_multiple_coils (
1087
+ slave_addr = self ._client_addr ,
1088
+ starting_address = coil_address ,
1089
+ output_values = new_coil_vals )
1090
+
1091
+ self .test_logger .debug (
1092
+ 'Result of setting COIL {} length {} to {}: {}, expectation: {}' .
1093
+ format (
1094
+ coil_address , coil_qty , new_coil_vals , operation_status , True ))
1095
+ self .assertIsInstance (operation_status , bool )
1096
+ self .assertTrue (operation_status )
1097
+
1098
+ # verify setting of states by reading data back again
1099
+ coil_status = self ._host .read_coils (
1100
+ slave_addr = self ._client_addr ,
1101
+ starting_addr = coil_address ,
1102
+ coil_qty = coil_qty )
1103
+
1104
+ self .test_logger .debug (
1105
+ 'Status of COIL {} length {}: {}, expectation: {}' .format (
1106
+ coil_address , coil_qty , coil_status , expectation_list ))
1107
+ self .assertIsInstance (coil_status , list )
1108
+ self .assertEqual (len (coil_status ), coil_qty )
1109
+ self .assertTrue (all (isinstance (x , bool ) for x in coil_status ))
1110
+ # Reading coil data bits is reversed, see #38
1111
+ # https://github.com/brainelectronics/micropython-modbus/issues/38
1112
+ # self.assertEqual(coil_status, expectation_list)
1113
+
937
1114
def test_write_multiple_registers (self ) -> None :
938
1115
"""Test updating multiple holding register of client"""
939
1116
hreg_address = \
0 commit comments