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.Optional;
22 import java.util.stream.Collectors;
23 import java.util.stream.Stream;
25 import org.eclipse.jdt.annotation.NonNull;
26 import org.junit.jupiter.params.ParameterizedTest;
27 import org.junit.jupiter.params.provider.MethodSource;
28 import org.openhab.core.library.types.DecimalType;
29 import org.openhab.io.transport.modbus.ModbusBitUtilities;
30 import org.openhab.io.transport.modbus.ModbusConstants.ValueType;
31 import org.openhab.io.transport.modbus.ModbusRegisterArray;
34 * @author Sami Salonen - Initial contribution
36 public class BitUtilitiesExtractStateFromRegistersTest {
38 private static ModbusRegisterArray shortArrayToRegisterArray(int... arr) {
39 return new ModbusRegisterArray(arr);
42 public static Collection<Object[]> data() {
43 return Collections.unmodifiableList(Stream.of(
47 new Object[] { new DecimalType("1.0"), ValueType.BIT,
48 shortArrayToRegisterArray(1 << 5 | 1 << 4 | 1 << 15), 4 },
49 new Object[] { new DecimalType("1.0"), ValueType.BIT,
50 shortArrayToRegisterArray(1 << 5 | 1 << 4 | 1 << 15), 15 },
51 new Object[] { new DecimalType("0.0"), ValueType.BIT, shortArrayToRegisterArray(1 << 5), 7 },
52 new Object[] { new DecimalType("1.0"), ValueType.BIT, shortArrayToRegisterArray(1 << 5), 5 },
53 new Object[] { new DecimalType("0.0"), ValueType.BIT, shortArrayToRegisterArray(1 << 5), 4 },
54 new Object[] { new DecimalType("0.0"), ValueType.BIT, shortArrayToRegisterArray(1 << 5), 0 },
55 new Object[] { new DecimalType("0.0"), ValueType.BIT, shortArrayToRegisterArray(0, 0), 15 },
56 new Object[] { new DecimalType("1.0"), ValueType.BIT, shortArrayToRegisterArray(1 << 5, 1 << 4), 5 },
57 new Object[] { new DecimalType("1.0"), ValueType.BIT, shortArrayToRegisterArray(1 << 5, 1 << 4), 20 },
58 new Object[] { IllegalArgumentException.class, ValueType.BIT, shortArrayToRegisterArray(1 << 5), 16 },
59 new Object[] { IllegalArgumentException.class, ValueType.BIT, shortArrayToRegisterArray(1 << 5), 200 },
60 new Object[] { IllegalArgumentException.class, ValueType.BIT, shortArrayToRegisterArray(), 0 },
61 new Object[] { IllegalArgumentException.class, ValueType.BIT, shortArrayToRegisterArray(0, 0), 32 },
65 new Object[] { new DecimalType("5.0"), ValueType.INT8, shortArrayToRegisterArray(5), 0 },
66 new Object[] { new DecimalType("-5.0"), ValueType.INT8, shortArrayToRegisterArray(-5), 0 },
67 new Object[] { new DecimalType("3.0"), ValueType.INT8,
68 shortArrayToRegisterArray(((byte) 6 << 8) | (byte) 3), 0 },
69 new Object[] { new DecimalType("6.0"), ValueType.INT8,
70 shortArrayToRegisterArray(((byte) 6 << 8) | (byte) 3), 1 },
71 new Object[] { new DecimalType("4.0"), ValueType.INT8,
72 shortArrayToRegisterArray(((byte) 6 << 8) | (byte) 3, 4), 2 },
73 new Object[] { new DecimalType("6.0"), ValueType.INT8,
74 shortArrayToRegisterArray(55, ((byte) 6 << 8) | (byte) 3), 3 },
75 new Object[] { IllegalArgumentException.class, ValueType.INT8, shortArrayToRegisterArray(1), 2 },
76 new Object[] { IllegalArgumentException.class, ValueType.INT8, shortArrayToRegisterArray(1, 2), 4 },
80 new Object[] { new DecimalType("5.0"), ValueType.UINT8, shortArrayToRegisterArray(5), 0 },
81 new Object[] { new DecimalType("251.0"), ValueType.UINT8, shortArrayToRegisterArray(-5), 0 },
82 new Object[] { new DecimalType("3.0"), ValueType.UINT8,
83 shortArrayToRegisterArray(((byte) 6 << 8) | (byte) 3), 0 },
84 new Object[] { new DecimalType("6.0"), ValueType.UINT8,
85 shortArrayToRegisterArray(((byte) 6 << 8) | (byte) 3), 1 },
86 new Object[] { new DecimalType("4.0"), ValueType.UINT8,
87 shortArrayToRegisterArray(((byte) 6 << 8) | (byte) 3, 4), 2 },
88 new Object[] { new DecimalType("6.0"), ValueType.UINT8,
89 shortArrayToRegisterArray(55, ((byte) 6 << 8) | (byte) 3), 3 },
90 new Object[] { IllegalArgumentException.class, ValueType.UINT8, shortArrayToRegisterArray(1), 2 },
91 new Object[] { IllegalArgumentException.class, ValueType.UINT8, shortArrayToRegisterArray(1, 2), 4 },
96 new Object[] { new DecimalType("1.0"), ValueType.INT16, shortArrayToRegisterArray(1), 0 },
97 new Object[] { new DecimalType("2.0"), ValueType.INT16, shortArrayToRegisterArray(2), 0 },
98 new Object[] { new DecimalType("-1004"), ValueType.INT16, shortArrayToRegisterArray(-1004), 0 },
99 new Object[] { new DecimalType("-1536"), ValueType.INT16, shortArrayToRegisterArray(64000), 0 },
100 new Object[] { new DecimalType("-1004"), ValueType.INT16, shortArrayToRegisterArray(4, -1004), 1 },
101 new Object[] { new DecimalType("-1004"), ValueType.INT16, shortArrayToRegisterArray(-1004, 4), 0 },
102 new Object[] { IllegalArgumentException.class, ValueType.INT16, shortArrayToRegisterArray(4, -1004),
107 new Object[] { new DecimalType("1.0"), ValueType.UINT16, shortArrayToRegisterArray(1), 0 },
108 new Object[] { new DecimalType("2.0"), ValueType.UINT16, shortArrayToRegisterArray(2), 0 },
109 new Object[] { new DecimalType("64532"), ValueType.UINT16, shortArrayToRegisterArray(-1004), 0 },
110 new Object[] { new DecimalType("64000"), ValueType.UINT16, shortArrayToRegisterArray(64000), 0 },
111 new Object[] { new DecimalType("64532"), ValueType.UINT16, shortArrayToRegisterArray(4, -1004), 1 },
112 new Object[] { new DecimalType("64532"), ValueType.UINT16, shortArrayToRegisterArray(-1004, 4), 0 },
113 new Object[] { IllegalArgumentException.class, ValueType.UINT16, shortArrayToRegisterArray(4, -1004),
118 new Object[] { new DecimalType("1.0"), ValueType.INT32, shortArrayToRegisterArray(0, 1), 0 },
119 new Object[] { new DecimalType("2.0"), ValueType.INT32, shortArrayToRegisterArray(0, 2), 0 },
120 new Object[] { new DecimalType("-1004"), ValueType.INT32,
121 // -1004 = 0xFFFFFC14 (32bit) =
122 shortArrayToRegisterArray(0xFFFF, 0xFC14), 0 },
123 new Object[] { new DecimalType("64000"), ValueType.INT32, shortArrayToRegisterArray(0, 64000), 0 },
124 new Object[] { new DecimalType("-1004"), ValueType.INT32,
125 // -1004 = 0xFFFFFC14 (32bit) =
126 shortArrayToRegisterArray(0x4, 0xFFFF, 0xFC14), 1 },
127 new Object[] { new DecimalType("-1004"), ValueType.INT32,
128 // -1004 = 0xFFFFFC14 (32bit) =
129 shortArrayToRegisterArray(0xFFFF, 0xFC14, 0x4), 0 },
130 new Object[] { IllegalArgumentException.class, ValueType.INT32, shortArrayToRegisterArray(4, -1004),
132 new Object[] { IllegalArgumentException.class, ValueType.INT32, shortArrayToRegisterArray(4, -1004),
134 new Object[] { IllegalArgumentException.class, ValueType.INT32, shortArrayToRegisterArray(0, 0, 0), 2 },
138 new Object[] { new DecimalType("1.0"), ValueType.UINT32, shortArrayToRegisterArray(0, 1), 0 },
139 new Object[] { new DecimalType("2.0"), ValueType.UINT32, shortArrayToRegisterArray(0, 2), 0 },
140 new Object[] { new DecimalType("4294966292"), ValueType.UINT32,
141 // 4294966292 = 0xFFFFFC14 (32bit) =
142 shortArrayToRegisterArray(0xFFFF, 0xFC14), 0 },
143 new Object[] { new DecimalType("64000"), ValueType.UINT32, shortArrayToRegisterArray(0, 64000), 0 },
145 // out of bounds of unsigned 16bit (0 to 65,535)
146 new DecimalType("70004"),
147 // 70004 -> 0x00011174 (32bit) -> 0x1174 (16bit)
148 ValueType.UINT32, shortArrayToRegisterArray(1, 4468), 0 },
149 new Object[] { new DecimalType("4294966292"), ValueType.UINT32,
150 // 4294966292 = 0xFFFFFC14 (32bit) =
151 shortArrayToRegisterArray(0xFFFF, 0xFC14, 0x5), 0 },
152 new Object[] { new DecimalType("4294966292"), ValueType.UINT32,
153 // 4294966292 = 0xFFFFFC14 (32bit) =
154 shortArrayToRegisterArray(0x5, 0xFFFF, 0xFC14), 1 },
155 new Object[] { IllegalArgumentException.class, ValueType.UINT32, shortArrayToRegisterArray(4, -1004),
157 new Object[] { IllegalArgumentException.class, ValueType.UINT32, shortArrayToRegisterArray(4, -1004),
159 new Object[] { IllegalArgumentException.class, ValueType.UINT32, shortArrayToRegisterArray(0, 0, 0),
164 new Object[] { new DecimalType("1.0"), ValueType.INT32_SWAP, shortArrayToRegisterArray(1, 0), 0 },
165 new Object[] { new DecimalType("2.0"), ValueType.INT32_SWAP, shortArrayToRegisterArray(2, 0), 0 },
166 new Object[] { new DecimalType("-1004"), ValueType.INT32_SWAP,
167 // -1004 = 0xFFFFFC14 (32bit) =
168 shortArrayToRegisterArray(0xFC14, 0xFFFF), 0 },
169 new Object[] { new DecimalType("64000"), ValueType.INT32_SWAP, shortArrayToRegisterArray(64000, 0), 0 },
170 new Object[] { new DecimalType("-1004"), ValueType.INT32_SWAP,
171 // -1004 = 0xFFFFFC14 (32bit) =
172 shortArrayToRegisterArray(0x4, 0xFC14, 0xFFFF), 1 },
173 new Object[] { new DecimalType("-1004"), ValueType.INT32_SWAP,
174 // -1004 = 0xFFFFFC14 (32bit) =
175 shortArrayToRegisterArray(0xFC14, 0xFFFF, 0x4), 0 },
176 new Object[] { IllegalArgumentException.class, ValueType.INT32_SWAP,
177 shortArrayToRegisterArray(4, -1004), 1 },
178 new Object[] { IllegalArgumentException.class, ValueType.INT32_SWAP,
179 shortArrayToRegisterArray(4, -1004), 2 },
180 new Object[] { IllegalArgumentException.class, ValueType.INT32_SWAP, shortArrayToRegisterArray(0, 0, 0),
185 new Object[] { new DecimalType("1.0"), ValueType.UINT32_SWAP, shortArrayToRegisterArray(1, 0), 0 },
186 new Object[] { new DecimalType("2.0"), ValueType.UINT32_SWAP, shortArrayToRegisterArray(2, 0), 0 },
187 new Object[] { new DecimalType("4294966292"), ValueType.UINT32_SWAP,
188 // 4294966292 = 0xFFFFFC14 (32bit) =
189 shortArrayToRegisterArray(0xFC14, 0xFFFF), 0 },
190 new Object[] { new DecimalType("64000"), ValueType.UINT32_SWAP, shortArrayToRegisterArray(64000, 0),
193 // out of bounds of unsigned 16bit (0 to 65,535)
194 new DecimalType("70004"),
195 // 70004 -> 0x00011174 (32bit) -> 0x1174 (16bit)
196 ValueType.UINT32_SWAP, shortArrayToRegisterArray(4468, 1), 0 },
197 new Object[] { new DecimalType("4294966292"), ValueType.UINT32_SWAP,
198 // 4294966292 = 0xFFFFFC14 (32bit) =
199 shortArrayToRegisterArray(0xFC14, 0xFFFF, 0x5), 0 },
200 new Object[] { new DecimalType("4294966292"), ValueType.UINT32_SWAP,
201 // 4294966292 = 0xFFFFFC14 (32bit) =
202 shortArrayToRegisterArray(0x5, 0xFC14, 0xFFFF), 1 },
203 new Object[] { IllegalArgumentException.class, ValueType.UINT32_SWAP,
204 shortArrayToRegisterArray(4, -1004), 1 },
205 new Object[] { IllegalArgumentException.class, ValueType.UINT32_SWAP,
206 shortArrayToRegisterArray(4, -1004), 2 },
207 new Object[] { IllegalArgumentException.class, ValueType.UINT32_SWAP,
208 shortArrayToRegisterArray(0, 0, 0), 2 },
212 new Object[] { new DecimalType("1.0"), ValueType.FLOAT32, shortArrayToRegisterArray(0x3F80, 0x0000),
214 new Object[] { new DecimalType(1.6f), ValueType.FLOAT32, shortArrayToRegisterArray(0x3FCC, 0xCCCD), 0 },
215 new Object[] { new DecimalType(2.6f), ValueType.FLOAT32, shortArrayToRegisterArray(0x4026, 0x6666), 0 },
216 new Object[] { new DecimalType(-1004.4f), ValueType.FLOAT32, shortArrayToRegisterArray(0xC47B, 0x199A),
218 new Object[] { new DecimalType("64000"), ValueType.FLOAT32, shortArrayToRegisterArray(0x477A, 0x0000),
221 // out of bounds of unsigned 16bit (0 to 65,535)
222 new DecimalType(70004.4f), ValueType.FLOAT32, shortArrayToRegisterArray(0x4788, 0xBA33), 0 },
224 // out of bounds of unsigned 32bit (0 to 4,294,967,295)
225 new DecimalType("5000000000"), ValueType.FLOAT32, shortArrayToRegisterArray(0x4F95, 0x02F9),
227 new Object[] { new DecimalType(-1004.4f), ValueType.FLOAT32,
228 shortArrayToRegisterArray(0x4, 0xC47B, 0x199A), 1 },
229 new Object[] { new DecimalType(-1004.4f), ValueType.FLOAT32,
230 shortArrayToRegisterArray(0xC47B, 0x199A, 0x4), 0 },
231 new Object[] { // equivalent of NaN
232 Optional.empty(), ValueType.FLOAT32, shortArrayToRegisterArray(0x7fc0, 0x0000), 0 },
233 new Object[] { new DecimalType(-1004.4f), ValueType.FLOAT32,
234 shortArrayToRegisterArray(0x4, 0x0, 0x0, 0x0, 0xC47B, 0x199A), 4 },
235 new Object[] { IllegalArgumentException.class, ValueType.FLOAT32, shortArrayToRegisterArray(4, -1004),
237 new Object[] { IllegalArgumentException.class, ValueType.FLOAT32, shortArrayToRegisterArray(4, -1004),
239 new Object[] { IllegalArgumentException.class, ValueType.FLOAT32, shortArrayToRegisterArray(0, 0, 0),
244 new Object[] { new DecimalType("1.0"), ValueType.FLOAT32_SWAP,
245 shortArrayToRegisterArray(0x0000, 0x3F80), 0 },
246 new Object[] { new DecimalType(1.6f), ValueType.FLOAT32_SWAP, shortArrayToRegisterArray(0xCCCD, 0x3FCC),
248 new Object[] { new DecimalType(2.6f), ValueType.FLOAT32_SWAP, shortArrayToRegisterArray(0x6666, 0x4026),
250 new Object[] { new DecimalType(-1004.4f), ValueType.FLOAT32_SWAP,
251 shortArrayToRegisterArray(0x199A, 0xC47B), 0 },
252 new Object[] { new DecimalType("64000"), ValueType.FLOAT32_SWAP,
253 shortArrayToRegisterArray(0x0000, 0x477A), 0 },
254 new Object[] { // equivalent of NaN
255 Optional.empty(), ValueType.FLOAT32_SWAP, shortArrayToRegisterArray(0x0000, 0x7fc0), 0 },
257 // out of bounds of unsigned 16bit (0 to 65,535)
258 new DecimalType(70004.4f), ValueType.FLOAT32_SWAP, shortArrayToRegisterArray(0xBA33, 0x4788),
261 // out of bounds of unsigned 32bit (0 to 4,294,967,295)
262 new DecimalType("5000000000"), ValueType.FLOAT32_SWAP,
263 shortArrayToRegisterArray(0x02F9, 0x4F95), 0 },
264 new Object[] { new DecimalType(-1004.4f), ValueType.FLOAT32_SWAP,
265 shortArrayToRegisterArray(0x4, 0x199A, 0xC47B), 1 },
266 new Object[] { new DecimalType(-1004.4f), ValueType.FLOAT32_SWAP,
267 shortArrayToRegisterArray(0x199A, 0xC47B, 0x4), 0 },
268 new Object[] { IllegalArgumentException.class, ValueType.FLOAT32_SWAP,
269 shortArrayToRegisterArray(4, -1004), 1 },
270 new Object[] { IllegalArgumentException.class, ValueType.FLOAT32_SWAP,
271 shortArrayToRegisterArray(4, -1004), 2 },
272 new Object[] { IllegalArgumentException.class, ValueType.FLOAT32_SWAP,
273 shortArrayToRegisterArray(0, 0, 0), 2 },
278 new Object[] { new DecimalType("1.0"), ValueType.INT64, shortArrayToRegisterArray(0, 0, 0, 1), 0 },
279 new Object[] { new DecimalType("2.0"), ValueType.INT64, shortArrayToRegisterArray(0, 0, 0, 2), 0 },
280 new Object[] { new DecimalType("-1004"), ValueType.INT64,
281 shortArrayToRegisterArray(0xFFFF, 0xFFFF, 0xFFFF, 0xFC14), 0 },
282 new Object[] { new DecimalType("64000"), ValueType.INT64, shortArrayToRegisterArray(0, 0, 0, 64000),
285 // out of bounds of unsigned 32bit
286 new DecimalType("34359738368"), ValueType.INT64, shortArrayToRegisterArray(0x0, 0x8, 0x0, 0x0),
288 new Object[] { new DecimalType("-2322243636186679031"), ValueType.INT64,
289 shortArrayToRegisterArray(0xDFC5, 0xBBB7, 0x772E, 0x7909), 0 },
290 // would read over the registers
291 new Object[] { IllegalArgumentException.class, ValueType.INT64,
292 shortArrayToRegisterArray(0xDFC5, 0xBBB7, 0x772E, 0x7909), 1 },
293 // would read over the registers
294 new Object[] { IllegalArgumentException.class, ValueType.INT64,
295 shortArrayToRegisterArray(0xDFC5, 0xBBB7, 0x772E, 0x7909), 2 },
296 // 4 registers expected, only 3 available
297 new Object[] { IllegalArgumentException.class, ValueType.INT64,
298 shortArrayToRegisterArray(0xDFC5, 0xBBB7, 0x772E), 0 },
303 new Object[] { new DecimalType("1.0"), ValueType.UINT64, shortArrayToRegisterArray(0, 0, 0, 1), 0 },
304 new Object[] { new DecimalType("2.0"), ValueType.UINT64, shortArrayToRegisterArray(0, 0, 0, 2), 0 },
305 new Object[] { new DecimalType("18446744073709550612"), ValueType.UINT64,
306 shortArrayToRegisterArray(0xFFFF, 0xFFFF, 0xFFFF, 0xFC14), 0 },
307 new Object[] { new DecimalType("64000"), ValueType.UINT64, shortArrayToRegisterArray(0, 0, 0, 64000),
310 // out of bounds of unsigned 32bit
311 new DecimalType("34359738368"), ValueType.UINT64, shortArrayToRegisterArray(0x0, 0x8, 0x0, 0x0),
313 new Object[] { new DecimalType("16124500437522872585"), ValueType.UINT64,
314 shortArrayToRegisterArray(0xDFC5, 0xBBB7, 0x772E, 0x7909), 0 },
319 new Object[] { new DecimalType("1.0"), ValueType.INT64_SWAP, shortArrayToRegisterArray(1, 0, 0, 0), 0 },
320 new Object[] { new DecimalType("2.0"), ValueType.INT64_SWAP, shortArrayToRegisterArray(2, 0, 0, 0), 0 },
321 new Object[] { new DecimalType("-1004"), ValueType.INT64_SWAP,
322 shortArrayToRegisterArray(0xFC14, 0xFFFF, 0xFFFF, 0xFFFF), 0 },
323 new Object[] { new DecimalType("64000"), ValueType.INT64_SWAP,
324 shortArrayToRegisterArray(64000, 0, 0, 0), 0 },
326 // out of bounds of unsigned 32bit
327 new DecimalType("34359738368"),
328 // 70004 -> 0x00011174 (32bit) -> 0x1174 (16bit)
329 ValueType.INT64_SWAP, shortArrayToRegisterArray(0x0, 0x0, 0x8, 0x0), 0 },
330 new Object[] { new DecimalType("-2322243636186679031"), ValueType.INT64_SWAP,
332 shortArrayToRegisterArray(0x7909, 0x772E, 0xBBB7, 0xDFC5), 0 },
337 new Object[] { new DecimalType("1.0"), ValueType.UINT64_SWAP, shortArrayToRegisterArray(1, 0, 0, 0),
339 new Object[] { new DecimalType("2.0"), ValueType.UINT64_SWAP, shortArrayToRegisterArray(2, 0, 0, 0),
341 new Object[] { new DecimalType("18446744073709550612"), ValueType.UINT64_SWAP,
342 shortArrayToRegisterArray(0xFC14, 0xFFFF, 0xFFFF, 0xFFFF), 0 },
343 new Object[] { new DecimalType("64000"), ValueType.UINT64_SWAP,
344 shortArrayToRegisterArray(64000, 0, 0, 0), 0 },
346 // out of bounds of unsigned 32bit
347 new DecimalType("34359738368"), ValueType.UINT64_SWAP,
348 shortArrayToRegisterArray(0x0, 0x0, 0x8, 0x0), 0 },
350 // out of bounds of unsigned 64bit
351 new DecimalType("16124500437522872585"), ValueType.UINT64_SWAP,
352 shortArrayToRegisterArray(0x7909, 0x772E, 0xBBB7, 0xDFC5), 0 })
353 .collect(Collectors.toList()));
356 @SuppressWarnings({ "unchecked", "rawtypes" })
358 @MethodSource("data")
359 public void testextractStateFromRegisters(Object expectedResult, ValueType type, ModbusRegisterArray registers,
361 if (expectedResult instanceof Class && Exception.class.isAssignableFrom((Class) expectedResult)) {
362 assertThrows((Class) expectedResult,
363 () -> ModbusBitUtilities.extractStateFromRegisters(registers, index, type));
367 Optional<@NonNull DecimalType> actualState = ModbusBitUtilities.extractStateFromRegisters(registers, index,
369 // Wrap given expectedResult to Optional, if necessary
370 Optional<@NonNull DecimalType> expectedStateWrapped = expectedResult instanceof DecimalType
371 ? Optional.of((DecimalType) expectedResult)
372 : (Optional<@NonNull DecimalType>) expectedResult;
373 assertThat(String.format("registers=%s, index=%d, type=%s", registers, index, type), actualState,
374 is(equalTo(expectedStateWrapped)));