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.junit.Assert.assertThat;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.stream.Collectors;
21 import java.util.stream.Stream;
23 import org.apache.commons.lang.NotImplementedException;
24 import org.junit.Rule;
25 import org.junit.Test;
26 import org.junit.rules.ExpectedException;
27 import org.junit.runner.RunWith;
28 import org.junit.runners.Parameterized;
29 import org.junit.runners.Parameterized.Parameters;
30 import org.openhab.core.library.types.DecimalType;
31 import org.openhab.core.library.types.IncreaseDecreaseType;
32 import org.openhab.core.library.types.OnOffType;
33 import org.openhab.core.library.types.OpenClosedType;
34 import org.openhab.core.types.Command;
35 import org.openhab.io.transport.modbus.ModbusBitUtilities;
36 import org.openhab.io.transport.modbus.ModbusConstants.ValueType;
37 import org.openhab.io.transport.modbus.ModbusRegisterArray;
40 * @author Sami Salonen - Initial contribution
42 @RunWith(Parameterized.class)
43 public class BitUtilitiesCommandToRegistersTest {
45 private final Command command;
46 private final ValueType type;
47 private final Object expectedResult;
50 public final ExpectedException shouldThrow = ExpectedException.none();
52 public BitUtilitiesCommandToRegistersTest(Command command, ValueType type, Object expectedResult) {
53 this.command = command;
55 this.expectedResult = expectedResult; // Exception or array of 16bit integers
58 private static short[] shorts(int... ints) {
59 short[] shorts = new short[ints.length];
60 for (int i = 0; i < ints.length; i++) {
61 short s = (short) ints[i];
68 public static Collection<Object[]> data() {
69 return Collections.unmodifiableList(Stream
70 .of(new Object[] { new DecimalType("1.0"), ValueType.BIT, IllegalArgumentException.class },
71 new Object[] { new DecimalType("1.0"), ValueType.INT8, IllegalArgumentException.class },
75 new Object[] { new DecimalType("1.0"), ValueType.INT16, shorts(1) },
76 new Object[] { new DecimalType("1.6"), ValueType.INT16, shorts(1) },
77 new Object[] { new DecimalType("2.6"), ValueType.INT16, shorts(2) },
78 new Object[] { new DecimalType("-1004.4"), ValueType.INT16, shorts(-1004), },
79 // within bounds for signed int16
80 new Object[] { new DecimalType("32000"), ValueType.INT16, shorts(32000), },
81 new Object[] { new DecimalType("-32000"), ValueType.INT16, shorts(-32000), },
82 // out bounds for signed int16, but not for uint16
83 new Object[] { new DecimalType("60000"), ValueType.INT16, shorts(60000), },
84 new Object[] { new DecimalType("64000"), ValueType.INT16, shorts(64000), }, //
86 // out of bounds of unsigned 16bit (0 to 65,535)
87 new DecimalType("70004.4"),
88 // 70004 -> 0x00011174 (int) -> 0x1174 (short) = 4468
89 ValueType.INT16, shorts(4468), },
91 // UINT16 (same as INT16)
93 new Object[] { new DecimalType("1.0"), ValueType.UINT16, shorts(1) },
94 new Object[] { new DecimalType("1.6"), ValueType.UINT16, shorts(1) },
95 new Object[] { new DecimalType("2.6"), ValueType.UINT16, shorts(2) },
96 new Object[] { new DecimalType("-1004.4"), ValueType.UINT16, shorts(-1004), },
97 // within bounds for signed int16
98 new Object[] { new DecimalType("32000"), ValueType.UINT16, shorts(32000), },
99 new Object[] { new DecimalType("-32000"), ValueType.UINT16, shorts(-32000), },
100 // out bounds for signed int16, but not for uint16
101 new Object[] { new DecimalType("60000"), ValueType.UINT16, shorts(60000), },
102 new Object[] { new DecimalType("64000"), ValueType.UINT16, shorts(64000), }, //
104 // out of bounds of unsigned 16bit (0 to 65,535)
105 new DecimalType("70004.4"),
106 // 70004 -> 0x00011174 (32bit) -> 0x1174 (16bit)
107 ValueType.UINT16, shorts(0x1174), },
111 new Object[] { new DecimalType("1.0"), ValueType.INT32, shorts(0, 1) },
112 new Object[] { new DecimalType("1.6"), ValueType.INT32, shorts(0, 1) },
113 new Object[] { new DecimalType("2.6"), ValueType.INT32, shorts(0, 2) },
114 new Object[] { new DecimalType("-1004.4"), ValueType.INT32,
115 // -1004 = 0xFFFFFC14 (32bit) =
116 shorts(0xFFFF, 0xFC14), },
117 new Object[] { new DecimalType("64000"), ValueType.INT32, shorts(0, 64000), }, //
118 // within signed int32 range: +-2,000,000,00
119 new Object[] { new DecimalType("-2000000000"), ValueType.INT32, shorts(0x88CA, 0x6C00), },
120 new Object[] { new DecimalType("2000000000"), ValueType.INT32, shorts(0x7735, 0x9400), },
121 // out bounds for signed int32, but not for uint32
122 new Object[] { new DecimalType("3000000000"), ValueType.INT32, shorts(0xB2D0, 0x5E00), }, //
124 // out of bounds of unsigned 32bit (0 to 4,294,967,295)
125 new DecimalType("5000000000"),
126 // 5000000000 -> 0x12a05f200 () -> 0x1174 (16bit)
127 ValueType.INT32, shorts(0x2a05, 0xf200), },
129 // UINT32 (same as INT32)
131 new Object[] { new DecimalType("1.0"), ValueType.UINT32, shorts(0, 1) },
132 new Object[] { new DecimalType("1.6"), ValueType.UINT32, shorts(0, 1) },
133 new Object[] { new DecimalType("2.6"), ValueType.UINT32, shorts(0, 2) },
134 new Object[] { new DecimalType("-1004.4"), ValueType.UINT32,
135 // -1004 = 0xFFFFFC14 (32bit) =
136 shorts(0xFFFF, 0xFC14), },
137 new Object[] { new DecimalType("64000"), ValueType.UINT32, shorts(0, 64000), }, //
138 // within signed int32 range: +-2,000,000,00
139 new Object[] { new DecimalType("-2000000000"), ValueType.UINT32, shorts(0x88CA, 0x6C00), },
140 new Object[] { new DecimalType("2000000000"), ValueType.UINT32, shorts(0x7735, 0x9400), },
141 // out bounds for signed int32, but not for uint32
142 new Object[] { new DecimalType("3000000000"), ValueType.UINT32, shorts(0xB2D0, 0x5E00), }, //
144 // out of bounds of unsigned 32bit (0 to 4,294,967,295)
145 new DecimalType("5000000000"),
146 // 5000000000 -> 0x12a05f200 () -> 0x1174 (16bit)
147 ValueType.UINT32, shorts(0x2a05, 0xf200), },
151 new Object[] { new DecimalType("1.0"), ValueType.INT32_SWAP, shorts(1, 0) },
152 new Object[] { new DecimalType("1.6"), ValueType.INT32_SWAP, shorts(1, 0) },
153 new Object[] { new DecimalType("2.6"), ValueType.INT32_SWAP, shorts(2, 0) },
154 new Object[] { new DecimalType("-1004.4"), ValueType.INT32_SWAP,
155 // -1004 = 0xFFFFFC14 (32bit)
156 shorts(0xFC14, 0xFFFF), },
157 new Object[] { new DecimalType("64000"), ValueType.INT32_SWAP, shorts(64000, 0), },
158 // within signed int32 range: +-2,000,000,00
159 new Object[] { new DecimalType("-2000000000"), ValueType.INT32_SWAP, shorts(0x6C00, 0x88CA), },
160 new Object[] { new DecimalType("2000000000"), ValueType.INT32_SWAP, shorts(0x9400, 0x7735), },
161 // out bounds for signed int32, but not for uint32
162 new Object[] { new DecimalType("3000000000"), ValueType.INT32_SWAP, shorts(0x5E00, 0xB2D0), }, //
164 // out of bounds of unsigned 32bit (0 to 4,294,967,295)
165 new DecimalType("5000000000"),
166 // 5000000000 -> 0x12a05f200
167 ValueType.INT32_SWAP, shorts(0xf200, 0x2a05), },
169 // UINT32_SWAP (same as INT32_SWAP)
171 new Object[] { new DecimalType("1.0"), ValueType.UINT32_SWAP, shorts(1, 0) },
172 new Object[] { new DecimalType("1.6"), ValueType.UINT32_SWAP, shorts(1, 0) },
173 new Object[] { new DecimalType("2.6"), ValueType.UINT32_SWAP, shorts(2, 0) },
174 new Object[] { new DecimalType("-1004.4"), ValueType.UINT32_SWAP,
175 // -1004 = 0xFFFFFC14 (32bit)
176 shorts(0xFC14, 0xFFFF), },
177 new Object[] { new DecimalType("64000"), ValueType.UINT32_SWAP, shorts(64000, 0), },
178 // within signed int32 range: +-2,000,000,00
179 new Object[] { new DecimalType("-2000000000"), ValueType.UINT32_SWAP, shorts(0x6C00, 0x88CA), },
180 new Object[] { new DecimalType("2000000000"), ValueType.UINT32_SWAP, shorts(0x9400, 0x7735), },
181 // out bounds for signed int32, but not for uint32
182 new Object[] { new DecimalType("3000000000"), ValueType.UINT32_SWAP, shorts(0x5E00, 0xB2D0), }, //
184 // out of bounds of unsigned 32bit (0 to 4,294,967,295)
185 new DecimalType("5000000000"),
186 // 5000000000 -> 0x12a05f200
187 ValueType.UINT32_SWAP, shorts(0xf200, 0x2a05), },
191 new Object[] { new DecimalType("1.0"), ValueType.FLOAT32, shorts(0x3F80, 0x0000) },
192 new Object[] { new DecimalType("1.6"), ValueType.FLOAT32, shorts(0x3FCC, 0xCCCD) },
193 new Object[] { new DecimalType("2.6"), ValueType.FLOAT32, shorts(0x4026, 0x6666) },
194 new Object[] { new DecimalType("-1004.4"), ValueType.FLOAT32, shorts(0xC47B, 0x199A), },
195 new Object[] { new DecimalType("64000"), ValueType.FLOAT32, shorts(0x477A, 0x0000), },
197 // out of bounds of unsigned 16bit (0 to 65,535)
198 new DecimalType("70004.4"), ValueType.FLOAT32, shorts(0x4788, 0xBA33), },
200 // out of bounds of unsigned 32bit (0 to 4,294,967,295)
201 new DecimalType("5000000000"), ValueType.FLOAT32, shorts(0x4F95, 0x02F9), },
205 new Object[] { new DecimalType("1.0"), ValueType.FLOAT32_SWAP, shorts(0x0000, 0x3F80) },
206 new Object[] { new DecimalType("1.6"), ValueType.FLOAT32_SWAP, shorts(0xCCCD, 0x3FCC) },
207 new Object[] { new DecimalType("2.6"), ValueType.FLOAT32_SWAP, shorts(0x6666, 0x4026) },
208 new Object[] { new DecimalType("-1004.4"), ValueType.FLOAT32_SWAP, shorts(0x199A, 0xC47B), },
209 new Object[] { new DecimalType("64000"), ValueType.FLOAT32_SWAP, shorts(0x0000, 0x477A), },
211 // out of bounds of unsigned 16bit (0 to 65,535)
212 new DecimalType("70004.4"), ValueType.FLOAT32_SWAP, shorts(0xBA33, 0x4788), },
214 // out of bounds of unsigned 32bit (0 to 4,294,967,295)
215 new DecimalType("5000000000"), ValueType.FLOAT32_SWAP, shorts(0x02F9, 0x4F95) },
217 new Object[] { OnOffType.ON, ValueType.FLOAT32_SWAP, shorts(0x0000, 0x3F80) },
218 new Object[] { OnOffType.OFF, ValueType.FLOAT32_SWAP, shorts(0x0000, 0x0000) },
220 new Object[] { OpenClosedType.OPEN, ValueType.FLOAT32_SWAP, shorts(0x0000, 0x3F80) },
221 new Object[] { OpenClosedType.OPEN, ValueType.INT16, shorts(1) },
223 new Object[] { OpenClosedType.CLOSED, ValueType.FLOAT32_SWAP, shorts(0x0000, 0x0000) },
224 new Object[] { OpenClosedType.CLOSED, ValueType.INT16, shorts(0x0000) },
225 // Unsupported command
226 new Object[] { IncreaseDecreaseType.INCREASE, ValueType.FLOAT32_SWAP,
227 NotImplementedException.class },
232 new Object[] { new DecimalType("1.0"), ValueType.INT64, shorts(0, 0, 0, 1) },
233 new Object[] { new DecimalType("1.6"), ValueType.INT64, shorts(0, 0, 0, 1) },
234 new Object[] { new DecimalType("2.6"), ValueType.INT64, shorts(0, 0, 0, 2) },
235 new Object[] { new DecimalType("-1004.4"), ValueType.INT64,
236 shorts(0xFFFF, 0xFFFF, 0xFFFF, 0xFC14), },
237 new Object[] { new DecimalType("64000"), ValueType.INT64, shorts(0, 0, 0, 64000), },
239 // out of bounds of unsigned 32bit
240 new DecimalType("34359738368"),
241 // 70004 -> 0x00011174 (32bit) -> 0x1174 (16bit)
242 ValueType.INT64, shorts(0x0, 0x8, 0x0, 0x0), },
243 // within signed int64 range: +-9,200,000,000,000,000,000
244 new Object[] { new DecimalType("-9200000000000000000"), ValueType.INT64,
245 shorts(0x8053, 0x08BE, 0x6268, 0x0000), },
246 new Object[] { new DecimalType("9200000000000000000"), ValueType.INT64,
247 shorts(0x7FAC, 0xF741, 0x9D98, 0x0000), },
248 // within unsigned int64 range (but out of range for signed int64)
249 new Object[] { new DecimalType("18200000000000000000"), ValueType.INT64,
250 shorts(0xFC93, 0x6392, 0x801C, 0x0000), },
252 // out of bounds of unsigned 64bit
253 new DecimalType("3498348904359085439088905"),
254 // should pick the low 64 bits
255 ValueType.INT64, shorts(0xDFC5, 0xBBB7, 0x772E, 0x7909), },
258 // UINT64 (same as INT64)
260 new Object[] { new DecimalType("1.0"), ValueType.UINT64, shorts(0, 0, 0, 1) },
261 new Object[] { new DecimalType("1.6"), ValueType.UINT64, shorts(0, 0, 0, 1) },
262 new Object[] { new DecimalType("2.6"), ValueType.UINT64, shorts(0, 0, 0, 2) },
263 new Object[] { new DecimalType("-1004.4"), ValueType.UINT64,
264 shorts(0xFFFF, 0xFFFF, 0xFFFF, 0xFC14), },
265 new Object[] { new DecimalType("64000"), ValueType.UINT64, shorts(0, 0, 0, 64000), },
267 // out of bounds of unsigned 32bit
268 new DecimalType("34359738368"),
269 // 70004 -> 0x00011174 (32bit) -> 0x1174 (16bit)
270 ValueType.UINT64, shorts(0x0, 0x8, 0x0, 0x0), },
271 // within signed int64 range: +-9,200,000,000,000,000,000
272 new Object[] { new DecimalType("-9200000000000000000"), ValueType.UINT64,
273 shorts(0x8053, 0x08BE, 0x6268, 0x0000), },
274 new Object[] { new DecimalType("9200000000000000000"), ValueType.UINT64,
275 shorts(0x7FAC, 0xF741, 0x9D98, 0x0000), },
276 // within unsigned int64 range (but out of range for signed int64)
277 new Object[] { new DecimalType("18200000000000000000"), ValueType.UINT64,
278 shorts(0xFC93, 0x6392, 0x801C, 0x0000), },
280 // out of bounds of unsigned 64bit
281 new DecimalType("3498348904359085439088905"),
282 // should pick the low 64 bits
283 ValueType.UINT64, shorts(0xDFC5, 0xBBB7, 0x772E, 0x7909), },
288 new Object[] { new DecimalType("1.0"), ValueType.INT64_SWAP, shorts(1, 0, 0, 0) },
289 new Object[] { new DecimalType("1.6"), ValueType.INT64_SWAP, shorts(1, 0, 0, 0) },
290 new Object[] { new DecimalType("2.6"), ValueType.INT64_SWAP, shorts(2, 0, 0, 0) },
291 new Object[] { new DecimalType("-1004.4"), ValueType.INT64_SWAP,
292 shorts(0xFC14, 0xFFFF, 0xFFFF, 0xFFFF), },
293 new Object[] { new DecimalType("64000"), ValueType.INT64_SWAP, shorts(64000, 0, 0, 0), },
295 // out of bounds of unsigned 32bit
296 new DecimalType("34359738368"),
297 // 70004 -> 0x00011174 (32bit) -> 0x1174 (16bit)
298 ValueType.INT64_SWAP, shorts(0x0, 0x0, 0x8, 0x0), },
300 // out of bounds of unsigned 64bit
301 new DecimalType("3498348904359085439088905"),
302 // should pick the low 64 bits
303 ValueType.INT64_SWAP, shorts(0x7909, 0x772E, 0xBBB7, 0xDFC5), },
306 // UINT64_SWAP (same as INT64_SWAP)
308 new Object[] { new DecimalType("1.0"), ValueType.UINT64_SWAP, shorts(1, 0, 0, 0) },
309 new Object[] { new DecimalType("1.6"), ValueType.UINT64_SWAP, shorts(1, 0, 0, 0) },
310 new Object[] { new DecimalType("2.6"), ValueType.UINT64_SWAP, shorts(2, 0, 0, 0) },
311 new Object[] { new DecimalType("-1004.4"), ValueType.UINT64_SWAP,
312 shorts(0xFC14, 0xFFFF, 0xFFFF, 0xFFFF), },
313 new Object[] { new DecimalType("64000"), ValueType.UINT64_SWAP, shorts(64000, 0, 0, 0), },
315 // out of bounds of unsigned 32bit
316 new DecimalType("34359738368"),
317 // 70004 -> 0x00011174 (32bit) -> 0x1174 (16bit)
318 ValueType.UINT64_SWAP, shorts(0x0, 0x0, 0x8, 0x0), },
320 // out of bounds of unsigned 64bit
321 new DecimalType("3498348904359085439088905"),
322 // should pick the low 64 bits
323 ValueType.UINT64_SWAP, shorts(0x7909, 0x772E, 0xBBB7, 0xDFC5), })
324 .collect(Collectors.toList()));
327 @SuppressWarnings({ "unchecked", "rawtypes" })
329 public void testCommandToRegisters() {
330 if (expectedResult instanceof Class && Exception.class.isAssignableFrom((Class) expectedResult)) {
331 shouldThrow.expect((Class) expectedResult);
334 ModbusRegisterArray registers = ModbusBitUtilities.commandToRegisters(this.command, this.type);
335 short[] expectedRegisters = (short[]) expectedResult;
337 assertThat(String.format("register index command=%s, type=%s", command, type), registers.size(),
338 is(equalTo(expectedRegisters.length)));
339 for (int i = 0; i < expectedRegisters.length; i++) {
340 int expectedRegisterDataUnsigned = expectedRegisters[i] & 0xffff;
341 int actual = registers.getRegister(i).getValue();
343 assertThat(String.format("register index i=%d, command=%s, type=%s", i, command, type), actual,
344 is(equalTo(expectedRegisterDataUnsigned)));