]> git.basschouten.com Git - openhab-addons.git/blob
d9084ab866078b9c7347570304a3438ad450642a
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.binding.lifx.internal.protocol;
14
15 import java.nio.ByteBuffer;
16
17 import org.openhab.binding.lifx.internal.fields.ByteField;
18 import org.openhab.binding.lifx.internal.fields.Field;
19 import org.openhab.binding.lifx.internal.fields.MACAddress;
20 import org.openhab.binding.lifx.internal.fields.MACAddressField;
21 import org.openhab.binding.lifx.internal.fields.UInt16Field;
22 import org.openhab.binding.lifx.internal.fields.UInt32Field;
23 import org.openhab.binding.lifx.internal.fields.UInt8Field;
24
25 /**
26  * Represents an abstract packet, providing conversion functionality to and from
27  * {@link ByteBuffer}s for common packet (preamble) fields. Subtypes of this
28  * class can provide conversion functionality for specialized fields.
29  *
30  * <p>
31  * Defining new packet types essentially involves extending this class,
32  * defining the fields and implementing {@link #packetType()},
33  * {@link #packetLength()}, and {@link #packetBytes()}. By convention, packet
34  * type should be stored in a {@code public static final int PACKET_TYPE} field
35  * in each subtype, followed by a listing of fields contained in the packet.
36  * Field definitions should remain accessible to outside classes in the event
37  * they need to worked with directly elsewhere.
38  *
39  * @author Tim Buckley - Initial Contribution
40  * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification
41  */
42 public abstract class Packet {
43
44     public static final Field<Integer> FIELD_SIZE = new UInt16Field().little();
45     public static final Field<Integer> FIELD_PROTOCOL = new UInt16Field().little();
46     public static final Field<Long> FIELD_SOURCE = new UInt32Field().little();
47     public static final Field<MACAddress> FIELD_TARGET = new MACAddressField();
48     public static final Field<ByteBuffer> FIELD_RESERVED_1 = new ByteField(6);
49     public static final Field<Integer> FIELD_ACK = new UInt8Field();
50     public static final Field<Integer> FIELD_SEQUENCE = new UInt8Field().little();
51     public static final Field<ByteBuffer> FIELD_RESERVED_2 = new ByteField(8);
52     public static final Field<Integer> FIELD_PACKET_TYPE = new UInt16Field().little();
53     public static final Field<ByteBuffer> FIELD_RESERVED_3 = new ByteField(2);
54
55     /**
56      * An ordered array of all fields contained in the common packet preamble.
57      */
58     public static final Field<?>[] PREAMBLE_FIELDS = new Field[] { FIELD_SIZE, FIELD_PROTOCOL, FIELD_SOURCE,
59             FIELD_TARGET, FIELD_RESERVED_1, FIELD_ACK, FIELD_SEQUENCE, FIELD_RESERVED_2, FIELD_PACKET_TYPE,
60             FIELD_RESERVED_3 };
61
62     protected int size;
63     protected int protocol;
64     protected long source;
65     protected MACAddress target;
66     protected ByteBuffer reserved1;
67     protected int ackbyte;
68     protected int sequence;
69     protected ByteBuffer reserved2;
70     protected int packetType;
71     protected ByteBuffer reserved3;
72
73     protected long timeStamp;
74
75     public int getSize() {
76         return size;
77     }
78
79     public void setSize(int size) {
80         this.size = size;
81     }
82
83     public int getOrigin() {
84         return (protocol & 0xC000) >> 14;
85     }
86
87     public void setOrigin(int origin) {
88         protocol = (protocol & ~(1 << 14)) | (origin << 14);
89     }
90
91     public boolean getTagged() {
92         return (protocol & 0x2000) >> 13 == 1 ? true : false;
93     }
94
95     public void setTagged(boolean flag) {
96         protocol = (protocol & ~(1 << 13)) | ((flag ? 1 : 0) << 13);
97     }
98
99     public boolean getAddressable() {
100         return (protocol & 0x1000) >> 12 == 1 ? true : false;
101     }
102
103     public void setAddressable(boolean flag) {
104         this.protocol = (protocol & ~(1 << 12)) | ((flag ? 1 : 0) << 12);
105     }
106
107     public int getProtocol() {
108         return protocol & 0x0FFF;
109     }
110
111     public void setProtocol(int protocol) {
112         this.protocol = this.protocol | protocol;
113     }
114
115     public long getSource() {
116         return source;
117     }
118
119     public void setSource(long source) {
120         this.source = source;
121     }
122
123     public MACAddress getTarget() {
124         return target;
125     }
126
127     public void setTarget(MACAddress lightAddress) {
128         this.target = lightAddress != null ? lightAddress : MACAddress.BROADCAST_ADDRESS;
129     }
130
131     public ByteBuffer getReserved1() {
132         return reserved1;
133     }
134
135     public void setReserved1(ByteBuffer reserved2) {
136         this.reserved1 = reserved2;
137     }
138
139     public boolean getAckRequired() {
140         return (ackbyte & 0x02) >> 1 == 1 ? true : false;
141     }
142
143     public void setAckRequired(boolean flag) {
144         this.ackbyte = (ackbyte & ~(1 << 1)) | ((flag ? 1 : 0) << 1);
145     }
146
147     public boolean getResponseRequired() {
148         return (ackbyte & 0x01) >> 0 == 1 ? true : false;
149     }
150
151     public void setResponseRequired(boolean flag) {
152         this.ackbyte = (ackbyte & ~(1 << 0)) | ((flag ? 1 : 0) << 0);
153     }
154
155     public int getSequence() {
156         return sequence;
157     }
158
159     public void setSequence(int sequence) {
160         if (0 <= sequence && sequence < 256) {
161             this.sequence = sequence;
162         } else {
163             throw new IllegalArgumentException("Sequence number '" + sequence + "' is not in range [0, 255]");
164         }
165     }
166
167     public ByteBuffer getReserved2() {
168         return reserved2;
169     }
170
171     public void setReserved2(ByteBuffer reserved3) {
172         this.reserved2 = reserved3;
173     }
174
175     public int getPacketType() {
176         return packetType;
177     }
178
179     public void setPacketType(int packetType) {
180         this.packetType = packetType;
181     }
182
183     public ByteBuffer getReserved3() {
184         return reserved3;
185     }
186
187     public void setReserved3(ByteBuffer reserved4) {
188         this.reserved3 = reserved4;
189     }
190
191     public long getTimeStamp() {
192         return timeStamp;
193     }
194
195     /**
196      * Creates an empty packet, setting some default values via
197      * {@link #preambleDefaults()}.
198      */
199     public Packet() {
200         preambleDefaults();
201         timeStamp = System.currentTimeMillis();
202     }
203
204     /**
205      * Parses, in order, the defined preamble fields, storing collected values.
206      * The buffer's position will be left at the end of the parsed fields and
207      * should be equal to the value returned by {@link #preambleLength()}.
208      *
209      * @param bytes the buffer to read from.
210      */
211     protected void parsePreamble(ByteBuffer bytes) {
212         size = FIELD_SIZE.value(bytes);
213         protocol = FIELD_PROTOCOL.value(bytes);
214         source = FIELD_SOURCE.value(bytes);
215         target = FIELD_TARGET.value(bytes);
216         reserved1 = FIELD_RESERVED_1.value(bytes);
217         ackbyte = FIELD_ACK.value(bytes);
218         sequence = FIELD_SEQUENCE.value(bytes);
219         reserved2 = FIELD_RESERVED_2.value(bytes);
220         packetType = FIELD_PACKET_TYPE.value(bytes);
221         reserved3 = FIELD_RESERVED_3.value(bytes);
222     }
223
224     /**
225      * Calculates the length of the packet header, defined as the sum of the
226      * lengths of all defined fields (see {@link #PREAMBLE_FIELDS}).
227      *
228      * @return the sum of the length of preamble fields
229      */
230     protected int preambleLength() {
231         int sum = 0;
232
233         for (Field<?> f : PREAMBLE_FIELDS) {
234             sum += f.getLength();
235         }
236
237         return sum;
238     }
239
240     /**
241      * Returns a new {@code ByteBuffer} containing the encoded preamble. Note
242      * that the returned buffer will have its position set at the end of the
243      * buffer and will need to have {@link ByteBuffer#rewind()} called before
244      * use.
245      *
246      * <p>
247      * The length of the buffer is the sum of the lengths of the defined
248      * preamble fields (see {@link #PREAMBLE_FIELDS} for an ordered list), which
249      * may also be accessed via {@link #preambleLength()}.
250      *
251      * <p>
252      * Certain fields are set to default values based on other class methods.
253      * For example, the size and packet type fields will be set to the values
254      * returned from {@link #length()} and {@link #packetType()}, respectively.
255      * Other defaults (such as the protocol, light address, site, and timestamp)
256      * may be specified either by directly setting the relevant protected
257      * variables or by overriding {@link #preambleDefaults()}.
258      *
259      * @return a new buffer containing the encoded preamble
260      */
261     protected ByteBuffer preambleBytes() {
262         return ByteBuffer.allocate(preambleLength()).put(FIELD_SIZE.bytes(length())).put(FIELD_PROTOCOL.bytes(protocol))
263                 .put(FIELD_SOURCE.bytes(source)).put(FIELD_TARGET.bytes(target))
264                 .put(ByteBuffer.allocate(FIELD_RESERVED_1.getLength())) // empty
265                 .put(FIELD_ACK.bytes(ackbyte)).put(FIELD_SEQUENCE.bytes(sequence))
266                 .put(ByteBuffer.allocate(FIELD_RESERVED_2.getLength())) // empty
267                 .put(FIELD_PACKET_TYPE.bytes(packetType())).put(ByteBuffer.allocate(FIELD_RESERVED_3.getLength())); // empty
268     }
269
270     /**
271      * Sets default preamble values. If needed, subclasses may override these
272      * values by specifically overriding this method, or by setting individual
273      * values within the constructor, as this method is called automatically
274      * during initialization.
275      */
276     protected void preambleDefaults() {
277         size = 0;
278         protocol = 1024;
279         target = new MACAddress();
280         sequence = 0;
281         packetType = packetType();
282     }
283
284     /**
285      * Returns the packet type. Note that this value is technically distinct
286      * from {@code getPacketType()} in that it returns the packet type the
287      * current {@code Packet} subtype is designed to parse, while
288      * {@code getPacketType()} returns the actual {@code packetType} field of
289      * a parsed packet. However, these values should always match.
290      *
291      * @return the packet type intended to be handled by this Packet subtype
292      */
293     public abstract int packetType();
294
295     /**
296      * Returns the length of the payload specific to this packet subtype. The
297      * length of the preamble is specifically excluded.
298      *
299      * @return the length of this specialized packet payload
300      */
301     protected abstract int packetLength();
302
303     /**
304      * Parses the given {@link ByteBuffer} into class fields. Subtypes may
305      * implement {@link #parsePacket(ByteBuffer)} to parse additional fields;
306      * the preamble by default is always parsed.
307      *
308      * @param bytes the buffer to extract data from
309      */
310     public void parse(ByteBuffer bytes) {
311         bytes.rewind();
312         parsePreamble(bytes);
313         parsePacket(bytes);
314     }
315
316     /**
317      * Extracts data from the given {@link ByteBuffer} into fields specific to
318      * this packet subtype. The preamble will already have been parsed; as such,
319      * the buffer will be positioned at the end of the preamble. If needed,
320      * {@link #preambleLength()} may be used to restore the position of the
321      * buffer.
322      *
323      * @param bytes the raw bytes to parse
324      */
325     protected abstract void parsePacket(ByteBuffer bytes);
326
327     /**
328      * Returns a {@link ByteBuffer} containing the full payload for this packet,
329      * including the populated preamble and any specialized packet payload. The
330      * returned buffer will be at position zero.
331      *
332      * @return the full packet payload
333      */
334     public ByteBuffer bytes() {
335         ByteBuffer preamble = preambleBytes();
336         preamble.rewind();
337
338         ByteBuffer packet = packetBytes();
339         packet.rewind();
340
341         ByteBuffer ret = ByteBuffer.allocate(length()).put(preamble).put(packet);
342         ret.rewind();
343
344         return ret;
345     }
346
347     /**
348      * Returns a {@link ByteBuffer} containing the payload for this packet. Its
349      * length must match the value of {@link #packetLength()}. This specifically
350      * excludes preamble fields and should contain only data specific to the
351      * packet subtype.
352      * <p>
353      * Note that returned ByteBuffers will have {@link ByteBuffer#rewind()}
354      * called automatically before they are appended to the final packet
355      * buffer.
356      *
357      * @return the packet payload
358      */
359     protected abstract ByteBuffer packetBytes();
360
361     /**
362      * Gets the total length of this packet, in bytes. Specifically, this method
363      * is the sum of the preamble ({@link #preambleLength()}) and the payload
364      * length ({@link #packetLength()}); subtypes should override methods for
365      * those values if desired.
366      *
367      * @return the total length of this packet
368      */
369     public int length() {
370         return preambleLength() + packetLength();
371     }
372
373     /**
374      * Returns a list of expected response packet types. An empty array means
375      * no responses are expected (suitable for response packet definitions),
376      *
377      * @return a list of expected responses
378      */
379     public abstract int[] expectedResponses();
380
381     public boolean isExpectedResponse(int type) {
382         for (int a : expectedResponses()) {
383             if (a == type) {
384                 return true;
385             }
386         }
387
388         return false;
389     }
390
391     public boolean isFulfilled(Packet somePacket) {
392         if (isExpectedResponse(somePacket.getPacketType())) {
393             return true;
394         }
395         return false;
396     }
397 }