]> git.basschouten.com Git - openhab-addons.git/blob
b55c4eeb7e586b890dc9eb4528200dc5305dbabe
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.io.transport.modbus;
14
15 import java.math.BigDecimal;
16 import java.math.BigInteger;
17 import java.nio.charset.Charset;
18 import java.util.Optional;
19
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;
27
28 /**
29  * Utilities for working with binary data.
30  *
31  * @author Sami Salonen - Initial contribution
32  */
33 @NonNullByDefault
34 public class ModbusBitUtilities {
35
36     /**
37      * Read data from registers and convert the result to DecimalType
38      * Interpretation of <tt>index</tt> goes as follows depending on type
39      *
40      * BIT:
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.
46      * INT8:
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
51      * UINT8:
52      * - same as INT8 except value is interpreted as unsigned integer
53      * INT16:
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
56      * UINT16:
57      * - same as INT16 except value is interpreted as unsigned integer
58      * INT32:
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
62      * INT32_SWAP:
63      * - Same as INT32 but registers swapped
64      * UINT32:
65      * - same as INT32 except value is interpreted as unsigned integer
66      * UINT32_SWAP:
67      * - same as INT32_SWAP except value is interpreted as unsigned integer
68      * FLOAT32:
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
73      * FLOAT32_SWAP:
74      * - Same as FLOAT32 but registers swapped
75      * INT64:
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
79      * INT64_SWAP:
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
82      * UINT64:
83      * - same as INT64 except value is interpreted as unsigned integer
84      * UINT64_SWAP:
85      * - same as INT64_SWAP except value is interpreted as unsigned integer
86      *
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
90      *            from.
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
96      *             bug
97      * @throws IllegalArgumentException when <tt>index</tt> is out of bounds of registers
98      *
99      */
100     public static Optional<DecimalType> extractStateFromRegisters(ModbusRegisterArray registers, int index,
101             ModbusConstants.ValueType type) {
102         byte[] bytes = registers.getBytes();
103         switch (type) {
104             case BIT:
105                 return Optional.of(new DecimalType(extractBit(bytes, index)));
106             case INT8: {
107                 int registerIndex = index / 2;
108                 boolean hiByte = index % 2 == 1;
109                 return Optional.of(new DecimalType(extractSInt8(bytes, registerIndex, hiByte)));
110             }
111             case UINT8: {
112                 int registerIndex = index / 2;
113                 boolean hiByte = index % 2 == 1;
114                 return Optional.of(new DecimalType(extractUInt8(bytes, registerIndex, hiByte)));
115             }
116             case INT16:
117                 return Optional.of(new DecimalType(extractSInt16(bytes, index * 2)));
118             case UINT16:
119                 return Optional.of(new DecimalType(extractUInt16(bytes, index * 2)));
120             case INT32:
121                 return Optional.of(new DecimalType(extractSInt32(bytes, index * 2)));
122             case UINT32:
123                 return Optional.of(new DecimalType(extractUInt32(bytes, index * 2)));
124             case FLOAT32:
125                 try {
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();
130                 }
131             case INT64:
132                 return Optional.of(new DecimalType(extractSInt64(bytes, index * 2)));
133             case UINT64:
134                 return Optional.of(new DecimalType(new BigDecimal(extractUInt64(bytes, index * 2))));
135             case INT32_SWAP:
136                 return Optional.of(new DecimalType(extractSInt32Swap(bytes, index * 2)));
137             case UINT32_SWAP:
138                 return Optional.of(new DecimalType(extractUInt32Swap(bytes, index * 2)));
139             case FLOAT32_SWAP:
140                 try {
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();
145                 }
146             case INT64_SWAP:
147                 return Optional.of(new DecimalType(extractSInt64Swap(bytes, index * 2)));
148             case UINT64_SWAP:
149                 return Optional.of(new DecimalType(new BigDecimal(extractUInt64Swap(bytes, index * 2))));
150             default:
151                 throw new IllegalArgumentException(type.getConfigValue());
152         }
153     }
154
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,
165                             bytes.length / 2));
166         }
167     }
168
169     /**
170      * Extract single bit from registers represented by sequence of bytes
171      *
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.
176      *
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
181      */
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);
187     }
188
189     /**
190      * Extract single bit from registers represented by sequence of bytes
191      *
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.
194      *
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
200      */
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));
208         }
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));
216         }
217         return ((bytes[byteIndex] >>> indexWithinByte) & 1);
218     }
219
220     /**
221      * Extract signed 8-bit integer (byte) from registers represented by sequence of bytes
222      *
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
228      */
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);
232         return signed;
233     }
234
235     /**
236      * Extract signed 8-bit integer (byte) from registers represented by sequence of bytes
237      *
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
241      *
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
247      */
248     public static byte extractSInt8(byte[] bytes, int index) {
249         assertIndexAndType(bytes, index, ValueType.INT8);
250         byte signed = bytes[index];
251         return signed;
252     }
253
254     /**
255      * Extract unsigned 8-bit integer (byte) from registers represented by sequence of bytes
256      *
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
262      */
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);
266         return unsigned;
267     }
268
269     /**
270      * Extract unsigned 8-bit integer (byte) from registers represented by sequence of bytes
271      *
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
275      *
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
281      */
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;
287         return unsigned;
288     }
289
290     /**
291      * Extract signed 16-bit integer (short) from registers represented by sequence of bytes
292      *
293      * It is assumed that each register is encoded in most significant bit first order
294      *
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
299      */
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);
305         return signed;
306     }
307
308     /**
309      * Extract unsigned 16-bit integer from registers represented by sequence of bytes
310      *
311      * It is assumed that each register is encoded in most significant bit first order
312      *
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
317      */
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;
323         return unsigned;
324     }
325
326     /**
327      * Extract signed 32-bit integer from registers represented by sequence of bytes
328      *
329      * It is assumed that each register is encoded in most significant bit first order
330      *
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
335      */
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;
343         return signed;
344     }
345
346     /**
347      * Extract unsigned 32-bit integer from registers represented by sequence of bytes
348      *
349      * It is assumed that each register is encoded in most significant bit first order
350      *
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
355      */
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;
361         return unsigned;
362     }
363
364     /**
365      * Extract signed 32-bit integer from registers represented by sequence of bytes
366      *
367      * It is assumed that each register is encoded in most significant bit first order.
368      *
369      * This is identical with extractSInt32, but with registers swapped.
370      *
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
375      */
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;
384         return signed;
385     }
386
387     /**
388      * Extract unsigned 32-bit integer from registers represented by sequence of bytes
389      *
390      * It is assumed that each register is encoded in most significant bit first order.
391      *
392      * This is identical with extractUInt32, but with registers swapped.
393      *
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
398      */
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;
404         return unsigned;
405     }
406
407     /**
408      * Extract signed 64-bit integer from registers represented by sequence of bytes
409      *
410      * It is assumed that each register is encoded in most significant bit first order.
411      *
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
416      */
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();
428     }
429
430     /**
431      * Extract unsigned 64-bit integer from registers represented by sequence of bytes
432      *
433      * It is assumed that each register is encoded in most significant bit first order.
434      *
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
439      */
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 });
451     }
452
453     /**
454      * Extract signed 64-bit integer from registers represented by sequence of bytes
455      *
456      * It is assumed that each register is encoded in most significant bit first order.
457      *
458      * This is identical with extractInt64, but with registers swapped (registers with higher index before lower index).
459      *
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
464      */
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();
477     }
478
479     /**
480      * Extract unsigned 64-bit integer from registers represented by sequence of bytes
481      *
482      * It is assumed that each register is encoded in most significant bit first order.
483      *
484      * This is identical with extractUInt64, but with registers swapped (registers with higher index before lower
485      * index).
486      *
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
491      */
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 });
504     }
505
506     /**
507      * Extract single-precision 32-bit IEEE 754 floating point from registers represented by sequence of bytes
508      *
509      * It is assumed that each register is encoded in most significant bit first order.
510      *
511      * Note that this method can return floating point NaN and floating point infinity.
512      *
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
516      *         floating point
517      * @throws IllegalArgumentException when index is out of bounds
518      */
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);
527     }
528
529     /**
530      * Extract single-precision 32-bit IEEE 754 floating point from registers represented by sequence of bytes
531      *
532      * It is assumed that each register is encoded in most significant bit first order.
533      *
534      * This is identical with extractFloat32, but with registers swapped (registers with higher index before lower
535      * index).
536      *
537      * Note that this method can return floating point NaN and floating point infinity.
538      *
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
542      *         floating point
543      * @throws IllegalArgumentException when index is out of bounds
544      */
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);
554     }
555
556     /**
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.
563      *
564      * Registers are read in big-endian order, i.e. two registers consisting 4 bytes (ab, cd) are parsed as sequence of
565      * bytes (a,b,c,d).
566      *
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
574      */
575     public static String extractStringFromRegisters(ModbusRegisterArray registers, int registerIndex, int length,
576             Charset charset) {
577         return extractStringFromBytes(registers.getBytes(), registerIndex * 2, length, charset);
578     }
579
580     /**
581      * Read data from bytes and convert the result to String
582      *
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.
586      *
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
593      */
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));
599         }
600         if (byteIndex < 0) {
601             throw new IllegalArgumentException("Negative index values are not supported");
602         }
603         if (length < 0) {
604             throw new IllegalArgumentException("Negative string length is not supported");
605         }
606
607         int effectiveLength = length;
608
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') {
612                 effectiveLength = i;
613                 break;
614             }
615         }
616
617         return new String(bytes, byteIndex, effectiveLength, charset);
618     }
619
620     /**
621      * Convert command to array of registers using a specific value type
622      *
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
628      */
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)
633                     : DecimalType.ZERO;
634         } else if (command instanceof DecimalType) {
635             numericCommand = (DecimalType) command;
636         } else {
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()));
640         }
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()));
644         }
645         switch (type) {
646             case INT16:
647             case UINT16: {
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 });
653             }
654             case INT32:
655             case UINT32: {
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 });
663             }
664             case INT32_SWAP:
665             case UINT32_SWAP: {
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 });
674             }
675             case FLOAT32: {
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 });
684             }
685             case FLOAT32_SWAP: {
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 });
695             }
696             case INT64:
697             case UINT64: {
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 });
709             }
710             case INT64_SWAP:
711             case UINT64_SWAP: {
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 });
724             }
725             default:
726                 throw new NotImplementedException(
727                         String.format("Illegal type=%s. Missing implementation for this type", type));
728         }
729     }
730
731     /**
732      * Converts command to a boolean
733      *
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.
737      *
738      * @param command to convert to boolean
739      * @return Boolean value matching the command. Empty if command cannot be converted
740      */
741     public static Optional<Boolean> translateCommand2Boolean(Command command) {
742         if (command.equals(OnOffType.ON)) {
743             return Optional.of(Boolean.TRUE);
744         }
745         if (command.equals(OnOffType.OFF)) {
746             return Optional.of(Boolean.FALSE);
747         }
748         if (command.equals(OpenClosedType.OPEN)) {
749             return Optional.of(Boolean.TRUE);
750         }
751         if (command.equals(OpenClosedType.CLOSED)) {
752             return Optional.of(Boolean.FALSE);
753         }
754         if (command instanceof DecimalType) {
755             return Optional.of(!command.equals(DecimalType.ZERO));
756         }
757         return Optional.empty();
758     }
759 }