]> git.basschouten.com Git - openhab-addons.git/blob
bcf5aa358e9685e5fc4cd05a3833d70a1b4aaf80
[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.BigInteger;
16 import java.nio.BufferOverflowException;
17 import java.nio.InvalidMarkException;
18 import java.util.Optional;
19 import java.util.concurrent.atomic.AtomicInteger;
20 import java.util.concurrent.atomic.AtomicReference;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24
25 /**
26  * ByteBuffer-like interface for working with different types of data stored in byte arrays
27  *
28  * @author Sami Salonen - Initial contribution
29  */
30 @NonNullByDefault
31 public class ValueBuffer {
32     private final byte[] bytes;
33     private final AtomicInteger byteIndex = new AtomicInteger();
34     private volatile AtomicReference<@Nullable AtomicInteger> mark = new AtomicReference<>();
35
36     /**
37      * Wrap modbus registers and create a new instance of ValueBuffer
38      *
39      * The instance will have position of 0.
40      *
41      * @param array set of registers
42      * @return new instance of ValueBuffer referencing bytes represented by modbus register array
43      */
44     public static ValueBuffer wrap(ModbusRegisterArray array) {
45         return new ValueBuffer(array.getBytes());
46     }
47
48     /**
49      * Wrap given bytes and create a new instance of ValueBuffer
50      *
51      * The instance will have position of 0.
52      *
53      *
54      * @param array set of bytes to wrap
55      * @return new instance of ValueBuffer referencing bytes
56      */
57     public static ValueBuffer wrap(byte[] array) {
58         return new ValueBuffer(array);
59     }
60
61     private ValueBuffer(byte[] bytes) {
62         this.bytes = bytes;
63     }
64
65     /**
66      * Returns this buffer's position.
67      *
68      * @return The position of this buffer
69      */
70     public int position() {
71         return byteIndex.get();
72     }
73
74     /**
75      * Sets this buffer's position. If the mark is defined and larger than the new position then it is discarded.
76      *
77      * @return this buffer
78      */
79     public ValueBuffer position(int byteIndex) {
80         this.mark.getAndUpdate(curMark -> {
81             if (curMark == null) {
82                 return null;
83             } else if (curMark.get() > byteIndex) {
84                 return null;
85             } else {
86                 return curMark;
87             }
88         });
89         this.byteIndex.set(byteIndex);
90         return this;
91     }
92
93     /**
94      * Sets this buffer's mark at its position.
95      *
96      * @return this buffer
97      */
98     public ValueBuffer mark() {
99         mark = new AtomicReference<>(new AtomicInteger(byteIndex.get()));
100         return this;
101     }
102
103     /**
104      * Resets this buffer's position to the previously-marked position.
105      * Invoking this method neither changes nor discards the mark's value.
106      *
107      * @return this buffer
108      * @throws InvalidMarkException If the mark has not been set
109      */
110     public ValueBuffer reset() throws InvalidMarkException {
111         int mark = Optional.ofNullable(this.mark.get()).map(i -> i.get()).orElse(-1);
112         if (mark < 0) {
113             throw new InvalidMarkException();
114         }
115         byteIndex.set(mark);
116         return this;
117     }
118
119     /**
120      * Returns the number of bytes between the current position and the end.
121      *
122      * @return The number of bytes remaining in this buffer
123      */
124     public int remaining() {
125         return bytes.length - byteIndex.get();
126     }
127
128     /**
129      * Returns underlying bytes
130      *
131      * @return reference to underlying bytes
132      */
133     public byte[] array() {
134         return bytes;
135     }
136
137     /**
138      * Tells whether there are any bytes left between current position and the end
139      *
140      * @return true if, and only if, there is at least one byte remaining in this buffer
141      */
142     public boolean hasRemaining() {
143         return remaining() > 0;
144     }
145
146     /**
147      * Starting from current position, read dst.length number of bytes and copy the data to dst
148      *
149      * @param dst copied bytes
150      * @return this buffer
151      * @throws BufferOverflowException If there is insufficient space in this buffer for the remaining bytes in the
152      *             source buffer
153      */
154     public ValueBuffer get(byte[] dst) {
155         int start = byteIndex.getAndAdd(dst.length);
156         try {
157             System.arraycopy(bytes, start, dst, 0, dst.length);
158         } catch (IndexOutOfBoundsException e) {
159             throw new BufferOverflowException();
160         }
161         return this;
162     }
163
164     /**
165      * Extract signed 8-bit integer at current position, and advance position.
166      *
167      * @return signed 8-bit integer (byte)
168      * @see ModbusBitUtilities.extractSInt8
169      * @throws IllegalArgumentException when there are not enough bytes in this ValueBuffer
170      */
171     public byte getSInt8() {
172         return ModbusBitUtilities.extractSInt8(bytes, byteIndex.getAndAdd(1));
173     }
174
175     /**
176      * Extract unsigned 8-bit integer at current position, and advance position.
177      *
178      * @return unsigned 8-bit integer
179      * @see ModbusBitUtilities.extractUInt8
180      * @throws IllegalArgumentException when there are not enough bytes in this ValueBuffer
181      */
182     public short getUInt8() {
183         return ModbusBitUtilities.extractUInt8(bytes, byteIndex.getAndAdd(1));
184     }
185
186     /**
187      * Extract signed 16-bit integer at current position, and advance position.
188      *
189      * @return signed 16-bit integer (short)
190      * @see ModbusBitUtilities.extractSInt16
191      * @throws IllegalArgumentException when there are not enough bytes in this ValueBuffer
192      */
193     public short getSInt16() {
194         return ModbusBitUtilities.extractSInt16(bytes, byteIndex.getAndAdd(2));
195     }
196
197     /**
198      * Extract unsigned 16-bit integer at current position, and advance position.
199      *
200      * @return unsigned 16-bit integer
201      * @see ModbusBitUtilities.extractUInt16
202      * @throws IllegalArgumentException when there are not enough bytes in this ValueBuffer
203      */
204     public int getUInt16() {
205         return ModbusBitUtilities.extractUInt16(bytes, byteIndex.getAndAdd(2));
206     }
207
208     /**
209      * Extract signed 32-bit integer at current position, and advance position.
210      *
211      * @return signed 32-bit integer
212      * @see ModbusBitUtilities.extractSInt32
213      * @throws IllegalArgumentException when there are not enough bytes in this ValueBuffer
214      */
215     public int getSInt32() {
216         return ModbusBitUtilities.extractSInt32(bytes, byteIndex.getAndAdd(4));
217     }
218
219     /**
220      * Extract unsigned 32-bit integer at current position, and advance position.
221      *
222      * @return unsigned 32-bit integer
223      * @see ModbusBitUtilities.extractUInt32
224      * @throws IllegalArgumentException when there are not enough bytes in this ValueBuffer
225      */
226     public long getUInt32() {
227         return ModbusBitUtilities.extractUInt32(bytes, byteIndex.getAndAdd(4));
228     }
229
230     /**
231      * Extract signed 32-bit integer at current position, and advance position.
232      *
233      * This is identical with getSInt32, but with registers swapped.
234      *
235      * @return signed 32-bit integer
236      * @see ModbusBitUtilities.extractSInt32Swap
237      * @throws IllegalArgumentException when there are not enough bytes in this ValueBuffer
238      */
239     public int getSInt32Swap() {
240         return ModbusBitUtilities.extractSInt32Swap(bytes, byteIndex.getAndAdd(4));
241     }
242
243     /**
244      * Extract unsigned 32-bit integer at current position, and advance position.
245      *
246      * This is identical with getUInt32, but with registers swapped.
247      *
248      * @return unsigned 32-bit integer
249      * @see ModbusBitUtilities.extractUInt32Swap
250      * @throws IllegalArgumentException when there are not enough bytes in this ValueBuffer
251      */
252     public long getUInt32Swap() {
253         return ModbusBitUtilities.extractUInt32Swap(bytes, byteIndex.getAndAdd(4));
254     }
255
256     /**
257      * Extract signed 64-bit integer at current position, and advance position.
258      *
259      * @return signed 64-bit integer
260      * @see ModbusBitUtilities.extractInt64
261      * @throws IllegalArgumentException when there are not enough bytes in this ValueBuffer
262      */
263     public long getSInt64() {
264         return ModbusBitUtilities.extractSInt64(bytes, byteIndex.getAndAdd(8));
265     }
266
267     /**
268      * Extract unsigned 64-bit integer at current position, and advance position.
269      *
270      * @return unsigned 64-bit integer
271      * @see ModbusBitUtilities.extractUInt64
272      * @throws IllegalArgumentException when there are not enough bytes in this ValueBuffer
273      */
274     public BigInteger getUInt64() {
275         return ModbusBitUtilities.extractUInt64(bytes, byteIndex.getAndAdd(8));
276     }
277
278     /**
279      * Extract signed 64-bit integer at current position, and advance position.
280      *
281      * This is identical with getSInt64, but with registers swapped.
282      *
283      * @return signed 64-bit integer
284      * @see ModbusBitUtilities.extractInt64Swap
285      * @throws IllegalArgumentException when there are not enough bytes in this ValueBuffer
286      */
287     public long getSInt64Swap() {
288         return ModbusBitUtilities.extractSInt64Swap(bytes, byteIndex.getAndAdd(8));
289     }
290
291     /**
292      * Extract unsigned 64-bit integer at current position, and advance position.
293      *
294      * This is identical with getUInt64, but with registers swapped.
295      *
296      * @return unsigned 64-bit integer
297      * @see ModbusBitUtilities.extractUInt64Swap
298      * @throws IllegalArgumentException when there are not enough bytes in this ValueBuffer
299      */
300     public BigInteger getUInt64Swap() {
301         return ModbusBitUtilities.extractUInt64Swap(bytes, byteIndex.getAndAdd(8));
302     }
303
304     /**
305      * Extract single-precision 32-bit IEEE 754 floating point at current position, and advance position.
306      *
307      * Note that this method can return floating point NaN and floating point infinity.
308      *
309      * @return single-precision 32-bit IEEE 754 floating point
310      * @see ModbusBitUtilities.extractFloat32
311      * @throws IllegalArgumentException when there are not enough bytes in this ValueBuffer
312      */
313     public float getFloat32() {
314         return ModbusBitUtilities.extractFloat32(bytes, byteIndex.getAndAdd(4));
315     }
316
317     /**
318      * Extract single-precision 32-bit IEEE 754 floating point at current position, and advance position.
319      *
320      * This is identical with getFloat32, but with registers swapped.
321      *
322      * Note that this method can return floating point NaN and floating point infinity.
323      *
324      * @return single-precision 32-bit IEEE 754 floating point
325      * @see ModbusBitUtilities.extractFloat32
326      * @throws IllegalArgumentException when there are not enough bytes in this ValueBuffer
327      */
328     public float getFloat32Swap() {
329         return ModbusBitUtilities.extractFloat32Swap(bytes, byteIndex.getAndAdd(4));
330     }
331 }