2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.io.transport.modbus.test;
15 import static org.hamcrest.CoreMatchers.*;
16 import static org.hamcrest.MatcherAssert.assertThat;
17 import static org.junit.jupiter.api.Assertions.assertThrows;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.stream.Collectors;
22 import java.util.stream.Stream;
24 import org.apache.commons.lang.NotImplementedException;
25 import org.junit.jupiter.params.ParameterizedTest;
26 import org.junit.jupiter.params.provider.MethodSource;
27 import org.openhab.core.library.types.DecimalType;
28 import org.openhab.core.library.types.IncreaseDecreaseType;
29 import org.openhab.core.library.types.OnOffType;
30 import org.openhab.core.library.types.OpenClosedType;
31 import org.openhab.core.types.Command;
32 import org.openhab.io.transport.modbus.ModbusBitUtilities;
33 import org.openhab.io.transport.modbus.ModbusConstants.ValueType;
34 import org.openhab.io.transport.modbus.ModbusRegisterArray;
37 * @author Sami Salonen - Initial contribution
39 public class BitUtilitiesCommandToRegistersTest {
41 private static short[] shorts(int... ints) {
42 short[] shorts = new short[ints.length];
43 for (int i = 0; i < ints.length; i++) {
44 short s = (short) ints[i];
50 public static Collection<Object[]> data() {
51 return Collections.unmodifiableList(Stream
52 .of(new Object[] { new DecimalType("1.0"), ValueType.BIT, IllegalArgumentException.class },
53 new Object[] { new DecimalType("1.0"), ValueType.INT8, IllegalArgumentException.class },
57 new Object[] { new DecimalType("1.0"), ValueType.INT16, shorts(1) },
58 new Object[] { new DecimalType("1.6"), ValueType.INT16, shorts(1) },
59 new Object[] { new DecimalType("2.6"), ValueType.INT16, shorts(2) },
60 new Object[] { new DecimalType("-1004.4"), ValueType.INT16, shorts(-1004), },
61 // within bounds for signed int16
62 new Object[] { new DecimalType("32000"), ValueType.INT16, shorts(32000), },
63 new Object[] { new DecimalType("-32000"), ValueType.INT16, shorts(-32000), },
64 // out bounds for signed int16, but not for uint16
65 new Object[] { new DecimalType("60000"), ValueType.INT16, shorts(60000), },
66 new Object[] { new DecimalType("64000"), ValueType.INT16, shorts(64000), }, //
68 // out of bounds of unsigned 16bit (0 to 65,535)
69 new DecimalType("70004.4"),
70 // 70004 -> 0x00011174 (int) -> 0x1174 (short) = 4468
71 ValueType.INT16, shorts(4468), },
73 // UINT16 (same as INT16)
75 new Object[] { new DecimalType("1.0"), ValueType.UINT16, shorts(1) },
76 new Object[] { new DecimalType("1.6"), ValueType.UINT16, shorts(1) },
77 new Object[] { new DecimalType("2.6"), ValueType.UINT16, shorts(2) },
78 new Object[] { new DecimalType("-1004.4"), ValueType.UINT16, shorts(-1004), },
79 // within bounds for signed int16
80 new Object[] { new DecimalType("32000"), ValueType.UINT16, shorts(32000), },
81 new Object[] { new DecimalType("-32000"), ValueType.UINT16, shorts(-32000), },
82 // out bounds for signed int16, but not for uint16
83 new Object[] { new DecimalType("60000"), ValueType.UINT16, shorts(60000), },
84 new Object[] { new DecimalType("64000"), ValueType.UINT16, shorts(64000), }, //
86 // out of bounds of unsigned 16bit (0 to 65,535)
87 new DecimalType("70004.4"),
88 // 70004 -> 0x00011174 (32bit) -> 0x1174 (16bit)
89 ValueType.UINT16, shorts(0x1174), },
93 new Object[] { new DecimalType("1.0"), ValueType.INT32, shorts(0, 1) },
94 new Object[] { new DecimalType("1.6"), ValueType.INT32, shorts(0, 1) },
95 new Object[] { new DecimalType("2.6"), ValueType.INT32, shorts(0, 2) },
96 new Object[] { new DecimalType("-1004.4"), ValueType.INT32,
97 // -1004 = 0xFFFFFC14 (32bit) =
98 shorts(0xFFFF, 0xFC14), },
99 new Object[] { new DecimalType("64000"), ValueType.INT32, shorts(0, 64000), }, //
100 // within signed int32 range: +-2,000,000,00
101 new Object[] { new DecimalType("-2000000000"), ValueType.INT32, shorts(0x88CA, 0x6C00), },
102 new Object[] { new DecimalType("2000000000"), ValueType.INT32, shorts(0x7735, 0x9400), },
103 // out bounds for signed int32, but not for uint32
104 new Object[] { new DecimalType("3000000000"), ValueType.INT32, shorts(0xB2D0, 0x5E00), }, //
106 // out of bounds of unsigned 32bit (0 to 4,294,967,295)
107 new DecimalType("5000000000"),
108 // 5000000000 -> 0x12a05f200 () -> 0x1174 (16bit)
109 ValueType.INT32, shorts(0x2a05, 0xf200), },
111 // UINT32 (same as INT32)
113 new Object[] { new DecimalType("1.0"), ValueType.UINT32, shorts(0, 1) },
114 new Object[] { new DecimalType("1.6"), ValueType.UINT32, shorts(0, 1) },
115 new Object[] { new DecimalType("2.6"), ValueType.UINT32, shorts(0, 2) },
116 new Object[] { new DecimalType("-1004.4"), ValueType.UINT32,
117 // -1004 = 0xFFFFFC14 (32bit) =
118 shorts(0xFFFF, 0xFC14), },
119 new Object[] { new DecimalType("64000"), ValueType.UINT32, shorts(0, 64000), }, //
120 // within signed int32 range: +-2,000,000,00
121 new Object[] { new DecimalType("-2000000000"), ValueType.UINT32, shorts(0x88CA, 0x6C00), },
122 new Object[] { new DecimalType("2000000000"), ValueType.UINT32, shorts(0x7735, 0x9400), },
123 // out bounds for signed int32, but not for uint32
124 new Object[] { new DecimalType("3000000000"), ValueType.UINT32, shorts(0xB2D0, 0x5E00), }, //
126 // out of bounds of unsigned 32bit (0 to 4,294,967,295)
127 new DecimalType("5000000000"),
128 // 5000000000 -> 0x12a05f200 () -> 0x1174 (16bit)
129 ValueType.UINT32, shorts(0x2a05, 0xf200), },
133 new Object[] { new DecimalType("1.0"), ValueType.INT32_SWAP, shorts(1, 0) },
134 new Object[] { new DecimalType("1.6"), ValueType.INT32_SWAP, shorts(1, 0) },
135 new Object[] { new DecimalType("2.6"), ValueType.INT32_SWAP, shorts(2, 0) },
136 new Object[] { new DecimalType("-1004.4"), ValueType.INT32_SWAP,
137 // -1004 = 0xFFFFFC14 (32bit)
138 shorts(0xFC14, 0xFFFF), },
139 new Object[] { new DecimalType("64000"), ValueType.INT32_SWAP, shorts(64000, 0), },
140 // within signed int32 range: +-2,000,000,00
141 new Object[] { new DecimalType("-2000000000"), ValueType.INT32_SWAP, shorts(0x6C00, 0x88CA), },
142 new Object[] { new DecimalType("2000000000"), ValueType.INT32_SWAP, shorts(0x9400, 0x7735), },
143 // out bounds for signed int32, but not for uint32
144 new Object[] { new DecimalType("3000000000"), ValueType.INT32_SWAP, shorts(0x5E00, 0xB2D0), }, //
146 // out of bounds of unsigned 32bit (0 to 4,294,967,295)
147 new DecimalType("5000000000"),
148 // 5000000000 -> 0x12a05f200
149 ValueType.INT32_SWAP, shorts(0xf200, 0x2a05), },
151 // UINT32_SWAP (same as INT32_SWAP)
153 new Object[] { new DecimalType("1.0"), ValueType.UINT32_SWAP, shorts(1, 0) },
154 new Object[] { new DecimalType("1.6"), ValueType.UINT32_SWAP, shorts(1, 0) },
155 new Object[] { new DecimalType("2.6"), ValueType.UINT32_SWAP, shorts(2, 0) },
156 new Object[] { new DecimalType("-1004.4"), ValueType.UINT32_SWAP,
157 // -1004 = 0xFFFFFC14 (32bit)
158 shorts(0xFC14, 0xFFFF), },
159 new Object[] { new DecimalType("64000"), ValueType.UINT32_SWAP, shorts(64000, 0), },
160 // within signed int32 range: +-2,000,000,00
161 new Object[] { new DecimalType("-2000000000"), ValueType.UINT32_SWAP, shorts(0x6C00, 0x88CA), },
162 new Object[] { new DecimalType("2000000000"), ValueType.UINT32_SWAP, shorts(0x9400, 0x7735), },
163 // out bounds for signed int32, but not for uint32
164 new Object[] { new DecimalType("3000000000"), ValueType.UINT32_SWAP, shorts(0x5E00, 0xB2D0), }, //
166 // out of bounds of unsigned 32bit (0 to 4,294,967,295)
167 new DecimalType("5000000000"),
168 // 5000000000 -> 0x12a05f200
169 ValueType.UINT32_SWAP, shorts(0xf200, 0x2a05), },
173 new Object[] { new DecimalType("1.0"), ValueType.FLOAT32, shorts(0x3F80, 0x0000) },
174 new Object[] { new DecimalType("1.6"), ValueType.FLOAT32, shorts(0x3FCC, 0xCCCD) },
175 new Object[] { new DecimalType("2.6"), ValueType.FLOAT32, shorts(0x4026, 0x6666) },
176 new Object[] { new DecimalType("-1004.4"), ValueType.FLOAT32, shorts(0xC47B, 0x199A), },
177 new Object[] { new DecimalType("64000"), ValueType.FLOAT32, shorts(0x477A, 0x0000), },
179 // out of bounds of unsigned 16bit (0 to 65,535)
180 new DecimalType("70004.4"), ValueType.FLOAT32, shorts(0x4788, 0xBA33), },
182 // out of bounds of unsigned 32bit (0 to 4,294,967,295)
183 new DecimalType("5000000000"), ValueType.FLOAT32, shorts(0x4F95, 0x02F9), },
187 new Object[] { new DecimalType("1.0"), ValueType.FLOAT32_SWAP, shorts(0x0000, 0x3F80) },
188 new Object[] { new DecimalType("1.6"), ValueType.FLOAT32_SWAP, shorts(0xCCCD, 0x3FCC) },
189 new Object[] { new DecimalType("2.6"), ValueType.FLOAT32_SWAP, shorts(0x6666, 0x4026) },
190 new Object[] { new DecimalType("-1004.4"), ValueType.FLOAT32_SWAP, shorts(0x199A, 0xC47B), },
191 new Object[] { new DecimalType("64000"), ValueType.FLOAT32_SWAP, shorts(0x0000, 0x477A), },
193 // out of bounds of unsigned 16bit (0 to 65,535)
194 new DecimalType("70004.4"), ValueType.FLOAT32_SWAP, shorts(0xBA33, 0x4788), },
196 // out of bounds of unsigned 32bit (0 to 4,294,967,295)
197 new DecimalType("5000000000"), ValueType.FLOAT32_SWAP, shorts(0x02F9, 0x4F95) },
199 new Object[] { OnOffType.ON, ValueType.FLOAT32_SWAP, shorts(0x0000, 0x3F80) },
200 new Object[] { OnOffType.OFF, ValueType.FLOAT32_SWAP, shorts(0x0000, 0x0000) },
202 new Object[] { OpenClosedType.OPEN, ValueType.FLOAT32_SWAP, shorts(0x0000, 0x3F80) },
203 new Object[] { OpenClosedType.OPEN, ValueType.INT16, shorts(1) },
205 new Object[] { OpenClosedType.CLOSED, ValueType.FLOAT32_SWAP, shorts(0x0000, 0x0000) },
206 new Object[] { OpenClosedType.CLOSED, ValueType.INT16, shorts(0x0000) },
207 // Unsupported command
208 new Object[] { IncreaseDecreaseType.INCREASE, ValueType.FLOAT32_SWAP,
209 NotImplementedException.class },
214 new Object[] { new DecimalType("1.0"), ValueType.INT64, shorts(0, 0, 0, 1) },
215 new Object[] { new DecimalType("1.6"), ValueType.INT64, shorts(0, 0, 0, 1) },
216 new Object[] { new DecimalType("2.6"), ValueType.INT64, shorts(0, 0, 0, 2) },
217 new Object[] { new DecimalType("-1004.4"), ValueType.INT64,
218 shorts(0xFFFF, 0xFFFF, 0xFFFF, 0xFC14), },
219 new Object[] { new DecimalType("64000"), ValueType.INT64, shorts(0, 0, 0, 64000), },
221 // out of bounds of unsigned 32bit
222 new DecimalType("34359738368"),
223 // 70004 -> 0x00011174 (32bit) -> 0x1174 (16bit)
224 ValueType.INT64, shorts(0x0, 0x8, 0x0, 0x0), },
225 // within signed int64 range: +-9,200,000,000,000,000,000
226 new Object[] { new DecimalType("-9200000000000000000"), ValueType.INT64,
227 shorts(0x8053, 0x08BE, 0x6268, 0x0000), },
228 new Object[] { new DecimalType("9200000000000000000"), ValueType.INT64,
229 shorts(0x7FAC, 0xF741, 0x9D98, 0x0000), },
230 // within unsigned int64 range (but out of range for signed int64)
231 new Object[] { new DecimalType("18200000000000000000"), ValueType.INT64,
232 shorts(0xFC93, 0x6392, 0x801C, 0x0000), },
234 // out of bounds of unsigned 64bit
235 new DecimalType("3498348904359085439088905"),
236 // should pick the low 64 bits
237 ValueType.INT64, shorts(0xDFC5, 0xBBB7, 0x772E, 0x7909), },
240 // UINT64 (same as INT64)
242 new Object[] { new DecimalType("1.0"), ValueType.UINT64, shorts(0, 0, 0, 1) },
243 new Object[] { new DecimalType("1.6"), ValueType.UINT64, shorts(0, 0, 0, 1) },
244 new Object[] { new DecimalType("2.6"), ValueType.UINT64, shorts(0, 0, 0, 2) },
245 new Object[] { new DecimalType("-1004.4"), ValueType.UINT64,
246 shorts(0xFFFF, 0xFFFF, 0xFFFF, 0xFC14), },
247 new Object[] { new DecimalType("64000"), ValueType.UINT64, shorts(0, 0, 0, 64000), },
249 // out of bounds of unsigned 32bit
250 new DecimalType("34359738368"),
251 // 70004 -> 0x00011174 (32bit) -> 0x1174 (16bit)
252 ValueType.UINT64, shorts(0x0, 0x8, 0x0, 0x0), },
253 // within signed int64 range: +-9,200,000,000,000,000,000
254 new Object[] { new DecimalType("-9200000000000000000"), ValueType.UINT64,
255 shorts(0x8053, 0x08BE, 0x6268, 0x0000), },
256 new Object[] { new DecimalType("9200000000000000000"), ValueType.UINT64,
257 shorts(0x7FAC, 0xF741, 0x9D98, 0x0000), },
258 // within unsigned int64 range (but out of range for signed int64)
259 new Object[] { new DecimalType("18200000000000000000"), ValueType.UINT64,
260 shorts(0xFC93, 0x6392, 0x801C, 0x0000), },
262 // out of bounds of unsigned 64bit
263 new DecimalType("3498348904359085439088905"),
264 // should pick the low 64 bits
265 ValueType.UINT64, shorts(0xDFC5, 0xBBB7, 0x772E, 0x7909), },
270 new Object[] { new DecimalType("1.0"), ValueType.INT64_SWAP, shorts(1, 0, 0, 0) },
271 new Object[] { new DecimalType("1.6"), ValueType.INT64_SWAP, shorts(1, 0, 0, 0) },
272 new Object[] { new DecimalType("2.6"), ValueType.INT64_SWAP, shorts(2, 0, 0, 0) },
273 new Object[] { new DecimalType("-1004.4"), ValueType.INT64_SWAP,
274 shorts(0xFC14, 0xFFFF, 0xFFFF, 0xFFFF), },
275 new Object[] { new DecimalType("64000"), ValueType.INT64_SWAP, shorts(64000, 0, 0, 0), },
277 // out of bounds of unsigned 32bit
278 new DecimalType("34359738368"),
279 // 70004 -> 0x00011174 (32bit) -> 0x1174 (16bit)
280 ValueType.INT64_SWAP, shorts(0x0, 0x0, 0x8, 0x0), },
282 // out of bounds of unsigned 64bit
283 new DecimalType("3498348904359085439088905"),
284 // should pick the low 64 bits
285 ValueType.INT64_SWAP, shorts(0x7909, 0x772E, 0xBBB7, 0xDFC5), },
288 // UINT64_SWAP (same as INT64_SWAP)
290 new Object[] { new DecimalType("1.0"), ValueType.UINT64_SWAP, shorts(1, 0, 0, 0) },
291 new Object[] { new DecimalType("1.6"), ValueType.UINT64_SWAP, shorts(1, 0, 0, 0) },
292 new Object[] { new DecimalType("2.6"), ValueType.UINT64_SWAP, shorts(2, 0, 0, 0) },
293 new Object[] { new DecimalType("-1004.4"), ValueType.UINT64_SWAP,
294 shorts(0xFC14, 0xFFFF, 0xFFFF, 0xFFFF), },
295 new Object[] { new DecimalType("64000"), ValueType.UINT64_SWAP, shorts(64000, 0, 0, 0), },
297 // out of bounds of unsigned 32bit
298 new DecimalType("34359738368"),
299 // 70004 -> 0x00011174 (32bit) -> 0x1174 (16bit)
300 ValueType.UINT64_SWAP, shorts(0x0, 0x0, 0x8, 0x0), },
302 // out of bounds of unsigned 64bit
303 new DecimalType("3498348904359085439088905"),
304 // should pick the low 64 bits
305 ValueType.UINT64_SWAP, shorts(0x7909, 0x772E, 0xBBB7, 0xDFC5), })
306 .collect(Collectors.toList()));
309 @SuppressWarnings({ "unchecked", "rawtypes" })
311 @MethodSource("data")
312 public void testCommandToRegisters(Command command, ValueType type, Object expectedResult) {
313 if (expectedResult instanceof Class && Exception.class.isAssignableFrom((Class) expectedResult)) {
314 assertThrows((Class) expectedResult, () -> ModbusBitUtilities.commandToRegisters(command, type));
318 ModbusRegisterArray registers = ModbusBitUtilities.commandToRegisters(command, type);
319 short[] expectedRegisters = (short[]) expectedResult;
321 assertThat(String.format("register index command=%s, type=%s", command, type), registers.size(),
322 is(equalTo(expectedRegisters.length)));
323 for (int i = 0; i < expectedRegisters.length; i++) {
324 int expectedRegisterDataUnsigned = expectedRegisters[i] & 0xffff;
325 int actual = registers.getRegister(i).getValue();
327 assertThat(String.format("register index i=%d, command=%s, type=%s", i, command, type), actual,
328 is(equalTo(expectedRegisterDataUnsigned)));