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;
15 import java.math.BigDecimal;
16 import java.math.BigInteger;
17 import java.nio.charset.Charset;
18 import java.util.Optional;
20 import org.apache.commons.lang.NotImplementedException;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.openhab.core.library.types.DecimalType;
23 import org.openhab.core.library.types.OnOffType;
24 import org.openhab.core.library.types.OpenClosedType;
25 import org.openhab.core.types.Command;
26 import org.openhab.io.transport.modbus.ModbusConstants.ValueType;
29 * Utilities for working with binary data.
31 * @author Sami Salonen - Initial contribution
34 public class ModbusBitUtilities {
37 * Read data from registers and convert the result to DecimalType
38 * Interpretation of <tt>index</tt> goes as follows depending on type
41 * - a single bit is read from the registers
42 * - indices between 0...15 (inclusive) represent bits of the first register
43 * - indices between 16...31 (inclusive) represent bits of the second register, etc.
44 * - index 0 refers to the least significant bit of the first register
45 * - index 1 refers to the second least significant bit of the first register, etc.
47 * - a byte (8 bits) from the registers is interpreted as signed integer
48 * - index 0 refers to low byte of the first register, 1 high byte of first register
49 * - index 2 refers to low byte of the second register, 3 high byte of second register, etc.
50 * - it is assumed that each high and low byte is encoded in most significant bit first order
52 * - same as INT8 except value is interpreted as unsigned integer
54 * - register with index (counting from zero) is interpreted as 16 bit signed integer.
55 * - it is assumed that each register is encoded in most significant bit first order
57 * - same as INT16 except value is interpreted as unsigned integer
59 * - registers (index) and (index + 1) are interpreted as signed 32bit integer.
60 * - it assumed that the first register contains the most significant 16 bits
61 * - it is assumed that each register is encoded in most significant bit first order
63 * - Same as INT32 but registers swapped
65 * - same as INT32 except value is interpreted as unsigned integer
67 * - same as INT32_SWAP except value is interpreted as unsigned integer
69 * - registers (index) and (index + 1) are interpreted as signed 32bit floating point number.
70 * - it assumed that the first register contains the most significant 16 bits
71 * - it is assumed that each register is encoded in most significant bit first order
72 * - floating point NaN and infinity will return as empty optional
74 * - Same as FLOAT32 but registers swapped
76 * - registers (index), (index + 1), (index + 2), (index + 3) are interpreted as signed 64bit integer.
77 * - it assumed that the first register contains the most significant 16 bits
78 * - it is assumed that each register is encoded in most significant bit first order
80 * - same as INT64 but registers swapped, that is, registers (index + 3), (index + 2), (index + 1), (index + 1) are
81 * interpreted as signed 64bit integer
83 * - same as INT64 except value is interpreted as unsigned integer
85 * - same as INT64_SWAP except value is interpreted as unsigned integer
87 * @param registers list of registers, each register represent 16bit of data
88 * @param index zero based item index. Interpretation of this depends on type, see examples above.
89 * With type larger or equal to 16 bits, the index tells the register index to start reading
91 * With type less than 16 bits, the index tells the N'th item to read from the registers.
92 * @param type item type, e.g. unsigned 16bit integer (<tt>ModbusBindingProvider.ValueType.UINT16</tt>)
93 * @return number representation queried value, <tt>DecimalType</tt>. Empty optional is returned
94 * with NaN and infinity floating point values
95 * @throws NotImplementedException in cases where implementation is lacking for the type. This can be considered a
97 * @throws IllegalArgumentException when <tt>index</tt> is out of bounds of registers
100 public static Optional<DecimalType> extractStateFromRegisters(ModbusRegisterArray registers, int index,
101 ModbusConstants.ValueType type) {
102 byte[] bytes = registers.getBytes();
105 return Optional.of(new DecimalType(extractBit(bytes, index)));
107 int registerIndex = index / 2;
108 boolean hiByte = index % 2 == 1;
109 return Optional.of(new DecimalType(extractSInt8(bytes, registerIndex, hiByte)));
112 int registerIndex = index / 2;
113 boolean hiByte = index % 2 == 1;
114 return Optional.of(new DecimalType(extractUInt8(bytes, registerIndex, hiByte)));
117 return Optional.of(new DecimalType(extractSInt16(bytes, index * 2)));
119 return Optional.of(new DecimalType(extractUInt16(bytes, index * 2)));
121 return Optional.of(new DecimalType(extractSInt32(bytes, index * 2)));
123 return Optional.of(new DecimalType(extractUInt32(bytes, index * 2)));
126 return Optional.of(new DecimalType(extractFloat32(bytes, index * 2)));
127 } catch (NumberFormatException e) {
128 // floating point NaN or infinity encountered
129 return Optional.empty();
132 return Optional.of(new DecimalType(extractSInt64(bytes, index * 2)));
134 return Optional.of(new DecimalType(new BigDecimal(extractUInt64(bytes, index * 2))));
136 return Optional.of(new DecimalType(extractSInt32Swap(bytes, index * 2)));
138 return Optional.of(new DecimalType(extractUInt32Swap(bytes, index * 2)));
141 return Optional.of(new DecimalType(extractFloat32Swap(bytes, index * 2)));
142 } catch (NumberFormatException e) {
143 // floating point NaN or infinity encountered
144 return Optional.empty();
147 return Optional.of(new DecimalType(extractSInt64Swap(bytes, index * 2)));
149 return Optional.of(new DecimalType(new BigDecimal(extractUInt64Swap(bytes, index * 2))));
151 throw new IllegalArgumentException(type.getConfigValue());
155 private static void assertIndexAndType(byte[] bytes, int index, ValueType type) {
156 int typeBits = type.getBits();
157 // for 8-bit types and larger, index specifies the index of the byte. For bits, index specifies the index of the
158 // bit (of the whole data)
159 int indexPositionAsBitIndex = Math.min(type.getBits(), 8) * index;
160 int endBitIndex = indexPositionAsBitIndex + typeBits - 1;
161 int lastValidIndex = bytes.length * 8 - 1;
162 if (endBitIndex > lastValidIndex || index < 0) {
163 throw new IllegalArgumentException(
164 String.format("Index=%d with type=%s is out-of-bounds given registers of size %d ", index, type,
170 * Extract single bit from registers represented by sequence of bytes
172 * - indices between 0...15 (inclusive) represent bits of the first register
173 * - indices between 16...31 (inclusive) represent bits of the second register, etc.
174 * - index 0 refers to the least significant bit of the first register
175 * - index 1 refers to the second least significant bit of the first register, etc.
177 * @param bytes registers represented by sequence of bytes
178 * @param index index of bit
179 * @return 0 when bit is set, 1 otherwise
180 * @throws IllegalArgumentException when index is out of bounds
182 public static int extractBit(byte[] bytes, int index) {
183 assertIndexAndType(bytes, index, ValueType.BIT);
184 int registerIndex = index / 16;
185 int bitIndexWithinRegister = index % 16;
186 return extractBit(bytes, registerIndex, bitIndexWithinRegister);
190 * Extract single bit from registers represented by sequence of bytes
192 * bitIndexWithinRegister between 0...15 (inclusive) represent bits of the first register, where 0 refers to the
193 * least significant bit of the register, index 1 refers to the second least significant bit of the register, etc.
195 * @param bytes registers represented by sequence of bytes
196 * @param registerIndex index of register. First register has index of 0.
197 * @param bitIndexWithinRegister bit index within the register
198 * @return 0 when bit is set, 1 otherwise
199 * @throws IllegalArgumentException when registerIndex and/or bitIndexWithinRegister is out of bounds
201 public static int extractBit(byte[] bytes, int registerIndex, int bitIndexWithinRegister) {
202 if (bitIndexWithinRegister < 0 || bitIndexWithinRegister > 15) {
203 throw new IllegalArgumentException(
204 String.format("bitIndexWithinRegister=%d is out-of-bounds (max 15)", bitIndexWithinRegister));
205 } else if (registerIndex < 0) {
206 throw new IllegalArgumentException(
207 String.format("registerIndex=%d is out-of-bounds", bitIndexWithinRegister));
209 boolean hiByte = bitIndexWithinRegister >= 8;
210 int indexWithinByte = bitIndexWithinRegister % 8;
211 int byteIndex = 2 * registerIndex + (hiByte ? 0 : 1);
212 if (byteIndex >= bytes.length) {
213 throw new IllegalArgumentException(String.format(
214 "registerIndex=%d, bitIndexWithinRegister=%d is out-of-bounds with registers of size %d",
215 registerIndex, bitIndexWithinRegister, bytes.length / 2));
217 return ((bytes[byteIndex] >>> indexWithinByte) & 1);
221 * Extract signed 8-bit integer (byte) from registers represented by sequence of bytes
223 * @param bytes registers represented by sequence of bytes
224 * @param registerIndex index of register. First register has index of 0.
225 * @param hiByte whether to extract hi byte or lo byte
226 * @return 0 when bit is set, 1 otherwise
227 * @throws IllegalArgumentException when index is out of bounds
229 public static byte extractSInt8(byte[] bytes, int registerIndex, boolean hiByte) {
230 int byteIndex = 2 * registerIndex + (hiByte ? 0 : 1);
231 byte signed = extractSInt8(bytes, byteIndex);
236 * Extract signed 8-bit integer (byte) from registers represented by sequence of bytes
238 * - index 0 refers to low byte of the first register, 1 high byte of first register
239 * - index 2 refers to low byte of the second register, 3 high byte of second register, etc.
240 * - it is assumed that each high and low byte is encoded in most significant bit first order
242 * @param bytes registers represented by sequence of bytes
243 * @param registerIndex index of register. First register has index of 0.
244 * @param index index of the byte in registers
245 * @return 0 when bit is set, 1 otherwise
246 * @throws IllegalArgumentException when index is out of bounds
248 public static byte extractSInt8(byte[] bytes, int index) {
249 assertIndexAndType(bytes, index, ValueType.INT8);
250 byte signed = bytes[index];
255 * Extract unsigned 8-bit integer (byte) from registers represented by sequence of bytes
257 * @param bytes registers represented by sequence of bytes
258 * @param registerIndex index of register. First register has index of 0.
259 * @param hiByte whether to extract hi byte or lo byte
260 * @return 0 when bit is set, 1 otherwise
261 * @throws IllegalArgumentException when registerIndex is out of bounds
263 public static short extractUInt8(byte[] bytes, int registerIndex, boolean hiByte) {
264 int byteIndex = 2 * registerIndex + (hiByte ? 0 : 1);
265 short unsigned = extractUInt8(bytes, byteIndex);
270 * Extract unsigned 8-bit integer (byte) from registers represented by sequence of bytes
272 * - index 0 refers to low byte of the first register, 1 high byte of first register
273 * - index 2 refers to low byte of the second register, 3 high byte of second register, etc.
274 * - it is assumed that each high and low byte is encoded in most significant bit first order
276 * @param bytes registers represented by sequence of bytes
277 * @param registerIndex index of register. First register has index of 0.
278 * @param index index of the byte in registers
279 * @return 0 when bit is set, 1 otherwise
280 * @throws IllegalArgumentException when index is out of bounds
282 public static short extractUInt8(byte[] bytes, int index) {
283 assertIndexAndType(bytes, index, ValueType.UINT8);
284 int signed = extractSInt8(bytes, index);
285 short unsigned = (short) (signed & 0xff);
286 assert unsigned >= 0;
291 * Extract signed 16-bit integer (short) from registers represented by sequence of bytes
293 * It is assumed that each register is encoded in most significant bit first order
295 * @param bytes registers represented by sequence of bytes
296 * @param index index of register. First register has index of 0.
297 * @return register with index interpreted as 16 bit signed integer
298 * @throws IllegalArgumentException when index is out of bounds
300 public static short extractSInt16(byte[] bytes, int index) {
301 assertIndexAndType(bytes, index, ValueType.INT16);
302 int hi = (bytes[index] & 0xff);
303 int lo = (bytes[index + 1] & 0xff);
304 short signed = (short) ((hi << 8) | lo);
309 * Extract unsigned 16-bit integer from registers represented by sequence of bytes
311 * It is assumed that each register is encoded in most significant bit first order
313 * @param bytes registers represented by sequence of bytes
314 * @param index index of register. First register has index of 0.
315 * @return register with index interpreted as 16 bit unsigned integer
316 * @throws IllegalArgumentException when index is out of bounds
318 public static int extractUInt16(byte[] bytes, int index) {
319 assertIndexAndType(bytes, index, ValueType.UINT16);
320 int signed = extractSInt16(bytes, index);
321 int unsigned = signed & 0xffff;
322 assert unsigned >= 0;
327 * Extract signed 32-bit integer from registers represented by sequence of bytes
329 * It is assumed that each register is encoded in most significant bit first order
331 * @param bytes registers represented by sequence of bytes
332 * @param index index of first register. First register has index of 0.
333 * @return registers (index) and (index+1) interpreted as 32 bit signed integer
334 * @throws IllegalArgumentException when index is out of bounds
336 public static int extractSInt32(byte[] bytes, int index) {
337 assertIndexAndType(bytes, index, ValueType.INT32);
338 int hi1 = bytes[index + 0] & 0xff;
339 int lo1 = bytes[index + 1] & 0xff;
340 int hi2 = bytes[index + 2] & 0xff;
341 int lo2 = bytes[index + 3] & 0xff;
342 int signed = (hi1 << 24) | (lo1 << 16) | (hi2 << 8) | lo2;
347 * Extract unsigned 32-bit integer from registers represented by sequence of bytes
349 * It is assumed that each register is encoded in most significant bit first order
351 * @param bytes registers represented by sequence of bytes
352 * @param index index of first register. First register has index of 0.
353 * @return registers (index) and (index+1) interpreted as 32 bit unsigned integer
354 * @throws IllegalArgumentException when index is out of bounds
356 public static long extractUInt32(byte[] bytes, int index) {
357 assertIndexAndType(bytes, index, ValueType.UINT32);
358 long signed = extractSInt32(bytes, index);
359 long unsigned = signed & 0xffff_ffffL;
360 assert unsigned >= 0;
365 * Extract signed 32-bit integer from registers represented by sequence of bytes
367 * It is assumed that each register is encoded in most significant bit first order.
369 * This is identical with extractSInt32, but with registers swapped.
371 * @param bytes registers represented by sequence of bytes
372 * @param index index of first register. First register has index of 0.
373 * @return registers (index+1), (index) interpreted as 32 bit signed integer
374 * @throws IllegalArgumentException when index is out of bounds
376 public static int extractSInt32Swap(byte[] bytes, int index) {
377 assertIndexAndType(bytes, index, ValueType.INT32_SWAP);
378 // swapped order of registers, high 16 bits *follow* low 16 bits
379 int hi1 = bytes[index + 2] & 0xff;
380 int lo1 = bytes[index + 3] & 0xff;
381 int hi2 = bytes[index + 0] & 0xff;
382 int lo2 = bytes[index + 1] & 0xff;
383 int signed = (hi1 << 24) | (lo1 << 16) | (hi2 << 8) | lo2;
388 * Extract unsigned 32-bit integer from registers represented by sequence of bytes
390 * It is assumed that each register is encoded in most significant bit first order.
392 * This is identical with extractUInt32, but with registers swapped.
394 * @param bytes registers represented by sequence of bytes
395 * @param index index of first register. First register has index of 0.
396 * @return registers (index+1), (index) interpreted as 32 bit unsigned integer
397 * @throws IllegalArgumentException when index is out of bounds
399 public static long extractUInt32Swap(byte[] bytes, int index) {
400 assertIndexAndType(bytes, index, ValueType.UINT32_SWAP);
401 long signed = extractSInt32Swap(bytes, index);
402 long unsigned = signed & 0xffff_ffffL;
403 assert unsigned >= 0;
408 * Extract signed 64-bit integer from registers represented by sequence of bytes
410 * It is assumed that each register is encoded in most significant bit first order.
412 * @param bytes registers represented by sequence of bytes
413 * @param index index of first register. First register has index of 0.
414 * @return registers (index), (index+1), (index+2), (index+3) interpreted as 64 bit signed integer
415 * @throws IllegalArgumentException when index is out of bounds
417 public static long extractSInt64(byte[] bytes, int index) {
418 assertIndexAndType(bytes, index, ValueType.INT64);
419 byte hi1 = (byte) (bytes[index + 0] & 0xff);
420 byte lo1 = (byte) (bytes[index + 1] & 0xff);
421 byte hi2 = (byte) (bytes[index + 2] & 0xff);
422 byte lo2 = (byte) (bytes[index + 3] & 0xff);
423 byte hi3 = (byte) (bytes[index + 4] & 0xff);
424 byte lo3 = (byte) (bytes[index + 5] & 0xff);
425 byte hi4 = (byte) (bytes[index + 6] & 0xff);
426 byte lo4 = (byte) (bytes[index + 7] & 0xff);
427 return new BigInteger(new byte[] { hi1, lo1, hi2, lo2, hi3, lo3, hi4, lo4 }).longValue();
431 * Extract unsigned 64-bit integer from registers represented by sequence of bytes
433 * It is assumed that each register is encoded in most significant bit first order.
435 * @param bytes registers represented by sequence of bytes
436 * @param index index of first register. First register has index of 0.
437 * @return registers (index), (index+1), (index+2), (index+3) interpreted as 64 bit unsigned integer
438 * @throws IllegalArgumentException when index is out of bounds
440 public static BigInteger extractUInt64(byte[] bytes, int index) {
441 assertIndexAndType(bytes, index, ValueType.UINT64);
442 byte hi1 = (byte) (bytes[index + 0] & 0xff);
443 byte lo1 = (byte) (bytes[index + 1] & 0xff);
444 byte hi2 = (byte) (bytes[index + 2] & 0xff);
445 byte lo2 = (byte) (bytes[index + 3] & 0xff);
446 byte hi3 = (byte) (bytes[index + 4] & 0xff);
447 byte lo3 = (byte) (bytes[index + 5] & 0xff);
448 byte hi4 = (byte) (bytes[index + 6] & 0xff);
449 byte lo4 = (byte) (bytes[index + 7] & 0xff);
450 return new BigInteger(1, new byte[] { hi1, lo1, hi2, lo2, hi3, lo3, hi4, lo4 });
454 * Extract signed 64-bit integer from registers represented by sequence of bytes
456 * It is assumed that each register is encoded in most significant bit first order.
458 * This is identical with extractInt64, but with registers swapped (registers with higher index before lower index).
460 * @param bytes registers represented by sequence of bytes
461 * @param index index of first register. First register has index of 0.
462 * @return registers (index+3), (index+2), (index+1), (index) interpreted as 64 bit signed integer
463 * @throws IllegalArgumentException when index is out of bounds
465 public static long extractSInt64Swap(byte[] bytes, int index) {
466 assertIndexAndType(bytes, index, ValueType.INT64_SWAP);
467 // Swapped order of registers
468 byte hi1 = (byte) (bytes[index + 6] & 0xff);
469 byte lo1 = (byte) (bytes[index + 7] & 0xff);
470 byte hi2 = (byte) (bytes[index + 4] & 0xff);
471 byte lo2 = (byte) (bytes[index + 5] & 0xff);
472 byte hi3 = (byte) (bytes[index + 2] & 0xff);
473 byte lo3 = (byte) (bytes[index + 3] & 0xff);
474 byte hi4 = (byte) (bytes[index + 0] & 0xff);
475 byte lo4 = (byte) (bytes[index + 1] & 0xff);
476 return new BigInteger(new byte[] { hi1, lo1, hi2, lo2, hi3, lo3, hi4, lo4 }).longValue();
480 * Extract unsigned 64-bit integer from registers represented by sequence of bytes
482 * It is assumed that each register is encoded in most significant bit first order.
484 * This is identical with extractUInt64, but with registers swapped (registers with higher index before lower
487 * @param bytes registers represented by sequence of bytes
488 * @param index index of first register. First register has index of 0.
489 * @return registers (index+3), (index+2), (index+1), (index) interpreted as 64 bit unsigned integer
490 * @throws IllegalArgumentException when index is out of bounds
492 public static BigInteger extractUInt64Swap(byte[] bytes, int index) {
493 assertIndexAndType(bytes, index, ValueType.UINT64_SWAP);
494 // Swapped order of registers
495 byte hi1 = (byte) (bytes[index + 6] & 0xff);
496 byte lo1 = (byte) (bytes[index + 7] & 0xff);
497 byte hi2 = (byte) (bytes[index + 4] & 0xff);
498 byte lo2 = (byte) (bytes[index + 5] & 0xff);
499 byte hi3 = (byte) (bytes[index + 2] & 0xff);
500 byte lo3 = (byte) (bytes[index + 3] & 0xff);
501 byte hi4 = (byte) (bytes[index + 0] & 0xff);
502 byte lo4 = (byte) (bytes[index + 1] & 0xff);
503 return new BigInteger(1, new byte[] { hi1, lo1, hi2, lo2, hi3, lo3, hi4, lo4 });
507 * Extract single-precision 32-bit IEEE 754 floating point from registers represented by sequence of bytes
509 * It is assumed that each register is encoded in most significant bit first order.
511 * Note that this method can return floating point NaN and floating point infinity.
513 * @param bytes registers represented by sequence of bytes
514 * @param index index of first register. First register has index of 0.
515 * @return registers (index), (index+1), (index+2), (index+3) interpreted as single-precision 32-bit IEEE 754
517 * @throws IllegalArgumentException when index is out of bounds
519 public static float extractFloat32(byte[] bytes, int index) {
520 assertIndexAndType(bytes, index, ValueType.FLOAT32);
521 int hi1 = bytes[index + 0] & 0xff;
522 int lo1 = bytes[index + 1] & 0xff;
523 int hi2 = bytes[index + 2] & 0xff;
524 int lo2 = bytes[index + 3] & 0xff;
525 int bits32 = (hi1 << 24) | (lo1 << 16) | (hi2 << 8) | lo2;
526 return Float.intBitsToFloat(bits32);
530 * Extract single-precision 32-bit IEEE 754 floating point from registers represented by sequence of bytes
532 * It is assumed that each register is encoded in most significant bit first order.
534 * This is identical with extractFloat32, but with registers swapped (registers with higher index before lower
537 * Note that this method can return floating point NaN and floating point infinity.
539 * @param bytes registers represented by sequence of bytes
540 * @param index index of first register. First register has index of 0.
541 * @return registers (index+3), (index+2), (index+1), (index) interpreted as single-precision 32-bit IEEE 754
543 * @throws IllegalArgumentException when index is out of bounds
545 public static float extractFloat32Swap(byte[] bytes, int index) {
546 assertIndexAndType(bytes, index, ValueType.FLOAT32_SWAP);
547 // swapped order of registers, high 16 bits *follow* low 16 bits
548 int hi1 = bytes[index + 2] & 0xff;
549 int lo1 = bytes[index + 3] & 0xff;
550 int hi2 = bytes[index + 0] & 0xff;
551 int lo2 = bytes[index + 1] & 0xff;
552 int bits32 = (hi1 << 24) | (lo1 << 16) | (hi2 << 8) | lo2;
553 return Float.intBitsToFloat(bits32);
557 * Read data from registers and convert the result to String
558 * Strings should start the the first byte of a register, but could
559 * have an odd number of characters.
560 * Raw byte array values are converted using the charset parameter
561 * and a maximum of length bytes are read. However reading stops at the first
562 * NUL byte encountered.
564 * Registers are read in big-endian order, i.e. two registers consisting 4 bytes (ab, cd) are parsed as sequence of
567 * @param registers list of registers, each register represent 16bit of data
568 * @param registerIndex zero based register index. Registers are handled as 16bit registers,
569 * this parameter defines the starting register.
570 * @param length maximum length of string in 8bit characters (number of bytes considered)
571 * @param charset the character set used to construct the string.
572 * @return string representation queried value
573 * @throws IllegalArgumentException when <tt>index</tt> is out of bounds of registers
575 public static String extractStringFromRegisters(ModbusRegisterArray registers, int registerIndex, int length,
577 return extractStringFromBytes(registers.getBytes(), registerIndex * 2, length, charset);
581 * Read data from bytes and convert the result to String
583 * Raw byte array values are converted using the charset parameter
584 * and a maximum of length bytes are read. However reading stops at the first
585 * NUL byte encountered.
587 * @param bytes bytes representing the registers
588 * @param byteIndex zero based byte index
589 * @param length maximum length of string in 8bit characters (number of bytes considered)
590 * @param charset the character set used to construct the string.
591 * @return string representation queried value
592 * @throws IllegalArgumentException when <tt>index</tt> is out of bounds of registers
594 public static String extractStringFromBytes(byte[] bytes, int byteIndex, int length, Charset charset) {
595 if (byteIndex + length > bytes.length) {
596 throw new IllegalArgumentException(
597 String.format("byteIndex=%d with length=%d is out-of-bounds given registers of size %d", byteIndex,
598 length, bytes.length));
601 throw new IllegalArgumentException("Negative index values are not supported");
604 throw new IllegalArgumentException("Negative string length is not supported");
607 int effectiveLength = length;
609 // Find first zero byte in registers and call reduce length such that we stop before it
610 for (int i = 0; i < length; i++) {
611 if (bytes[byteIndex + i] == '\0') {
617 return new String(bytes, byteIndex, effectiveLength, charset);
621 * Convert command to array of registers using a specific value type
623 * @param command command to be converted
624 * @param type value type to use in conversion
625 * @return array of registers
626 * @throws NotImplementedException in cases where implementation is lacking for the type. This is thrown with 1-bit
627 * and 8-bit value types
629 public static ModbusRegisterArray commandToRegisters(Command command, ModbusConstants.ValueType type) {
630 DecimalType numericCommand;
631 if (command instanceof OnOffType || command instanceof OpenClosedType) {
632 numericCommand = translateCommand2Boolean(command).get() ? new DecimalType(BigDecimal.ONE)
634 } else if (command instanceof DecimalType) {
635 numericCommand = (DecimalType) command;
637 throw new NotImplementedException(String.format(
638 "Command '%s' of class '%s' cannot be converted to registers. Please use OnOffType, OpenClosedType, or DecimalType commands.",
639 command, command.getClass().getName()));
641 if (type.getBits() != 16 && type.getBits() != 32 && type.getBits() != 64) {
642 throw new IllegalArgumentException(String.format(
643 "Illegal type=%s (bits=%d). Only 16bit and 32bit types are supported", type, type.getBits()));
648 short shortValue = numericCommand.shortValue();
649 // big endian byte ordering
650 byte hi = (byte) (shortValue >> 8);
651 byte lo = (byte) shortValue;
652 return new ModbusRegisterArray(new byte[] { hi, lo });
656 int intValue = numericCommand.intValue();
657 // big endian byte ordering
658 byte hi1 = (byte) (intValue >> 24);
659 byte lo1 = (byte) (intValue >> 16);
660 byte hi2 = (byte) (intValue >> 8);
661 byte lo2 = (byte) intValue;
662 return new ModbusRegisterArray(new byte[] { hi1, lo1, hi2, lo2 });
666 int intValue = numericCommand.intValue();
667 // big endian byte ordering
668 byte hi1 = (byte) (intValue >> 24);
669 byte lo1 = (byte) (intValue >> 16);
670 byte hi2 = (byte) (intValue >> 8);
671 byte lo2 = (byte) intValue;
672 // Swapped order of registers
673 return new ModbusRegisterArray(new byte[] { hi2, lo2, hi1, lo1 });
676 float floatValue = numericCommand.floatValue();
677 int intBits = Float.floatToIntBits(floatValue);
678 // big endian byte ordering
679 byte hi1 = (byte) (intBits >> 24);
680 byte lo1 = (byte) (intBits >> 16);
681 byte hi2 = (byte) (intBits >> 8);
682 byte lo2 = (byte) intBits;
683 return new ModbusRegisterArray(new byte[] { hi1, lo1, hi2, lo2 });
686 float floatValue = numericCommand.floatValue();
687 int intBits = Float.floatToIntBits(floatValue);
688 // big endian byte ordering
689 byte hi1 = (byte) (intBits >> 24);
690 byte lo1 = (byte) (intBits >> 16);
691 byte hi2 = (byte) (intBits >> 8);
692 byte lo2 = (byte) intBits;
693 // Swapped order of registers
694 return new ModbusRegisterArray(new byte[] { hi2, lo2, hi1, lo1 });
698 long longValue = numericCommand.longValue();
699 // big endian byte ordering
700 byte hi1 = (byte) (longValue >> 56);
701 byte lo1 = (byte) (longValue >> 48);
702 byte hi2 = (byte) (longValue >> 40);
703 byte lo2 = (byte) (longValue >> 32);
704 byte hi3 = (byte) (longValue >> 24);
705 byte lo3 = (byte) (longValue >> 16);
706 byte hi4 = (byte) (longValue >> 8);
707 byte lo4 = (byte) longValue;
708 return new ModbusRegisterArray(new byte[] { hi1, lo1, hi2, lo2, hi3, lo3, hi4, lo4 });
712 long longValue = numericCommand.longValue();
713 // big endian byte ordering
714 byte hi1 = (byte) (longValue >> 56);
715 byte lo1 = (byte) (longValue >> 48);
716 byte hi2 = (byte) (longValue >> 40);
717 byte lo2 = (byte) (longValue >> 32);
718 byte hi3 = (byte) (longValue >> 24);
719 byte lo3 = (byte) (longValue >> 16);
720 byte hi4 = (byte) (longValue >> 8);
721 byte lo4 = (byte) longValue;
722 // Swapped order of registers
723 return new ModbusRegisterArray(new byte[] { hi4, lo4, hi3, lo3, hi2, lo2, hi1, lo1 });
726 throw new NotImplementedException(
727 String.format("Illegal type=%s. Missing implementation for this type", type));
732 * Converts command to a boolean
734 * true value is represented by {@link OnOffType.ON}, {@link OpenClosedType.OPEN}.
735 * false value is represented by {@link OnOffType.OFF}, {@link OpenClosedType.CLOSED}.
736 * Furthermore, {@link DecimalType} are converted to boolean true if they are unequal to zero.
738 * @param command to convert to boolean
739 * @return Boolean value matching the command. Empty if command cannot be converted
741 public static Optional<Boolean> translateCommand2Boolean(Command command) {
742 if (command.equals(OnOffType.ON)) {
743 return Optional.of(Boolean.TRUE);
745 if (command.equals(OnOffType.OFF)) {
746 return Optional.of(Boolean.FALSE);
748 if (command.equals(OpenClosedType.OPEN)) {
749 return Optional.of(Boolean.TRUE);
751 if (command.equals(OpenClosedType.CLOSED)) {
752 return Optional.of(Boolean.FALSE);
754 if (command instanceof DecimalType) {
755 return Optional.of(!command.equals(DecimalType.ZERO));
757 return Optional.empty();