2 * Copyright (c) 2010-2021 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.binding.lifx.internal.protocol;
15 import java.nio.ByteBuffer;
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;
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.
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.
39 * @author Tim Buckley - Initial Contribution
40 * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification
42 public abstract class Packet {
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);
56 * An ordered array of all fields contained in the common packet preamble.
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,
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;
73 protected long timeStamp;
75 public int getSize() {
79 public void setSize(int size) {
83 public int getOrigin() {
84 return (protocol & 0xC000) >> 14;
87 public void setOrigin(int origin) {
88 protocol = (protocol & ~(1 << 14)) | (origin << 14);
91 public boolean getTagged() {
92 return (protocol & 0x2000) >> 13 == 1 ? true : false;
95 public void setTagged(boolean flag) {
96 protocol = (protocol & ~(1 << 13)) | ((flag ? 1 : 0) << 13);
99 public boolean getAddressable() {
100 return (protocol & 0x1000) >> 12 == 1 ? true : false;
103 public void setAddressable(boolean flag) {
104 this.protocol = (protocol & ~(1 << 12)) | ((flag ? 1 : 0) << 12);
107 public int getProtocol() {
108 return protocol & 0x0FFF;
111 public void setProtocol(int protocol) {
112 this.protocol = this.protocol | protocol;
115 public long getSource() {
119 public void setSource(long source) {
120 this.source = source;
123 public MACAddress getTarget() {
127 public void setTarget(MACAddress lightAddress) {
128 this.target = lightAddress != null ? lightAddress : MACAddress.BROADCAST_ADDRESS;
131 public ByteBuffer getReserved1() {
135 public void setReserved1(ByteBuffer reserved2) {
136 this.reserved1 = reserved2;
139 public boolean getAckRequired() {
140 return (ackbyte & 0x02) >> 1 == 1 ? true : false;
143 public void setAckRequired(boolean flag) {
144 this.ackbyte = (ackbyte & ~(1 << 1)) | ((flag ? 1 : 0) << 1);
147 public boolean getResponseRequired() {
148 return (ackbyte & 0x01) >> 0 == 1 ? true : false;
151 public void setResponseRequired(boolean flag) {
152 this.ackbyte = (ackbyte & ~(1 << 0)) | ((flag ? 1 : 0) << 0);
155 public int getSequence() {
159 public void setSequence(int sequence) {
160 if (0 <= sequence && sequence < 256) {
161 this.sequence = sequence;
163 throw new IllegalArgumentException("Sequence number '" + sequence + "' is not in range [0, 255]");
167 public ByteBuffer getReserved2() {
171 public void setReserved2(ByteBuffer reserved3) {
172 this.reserved2 = reserved3;
175 public int getPacketType() {
179 public void setPacketType(int packetType) {
180 this.packetType = packetType;
183 public ByteBuffer getReserved3() {
187 public void setReserved3(ByteBuffer reserved4) {
188 this.reserved3 = reserved4;
191 public long getTimeStamp() {
196 * Creates an empty packet, setting some default values via
197 * {@link #preambleDefaults()}.
201 timeStamp = System.currentTimeMillis();
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()}.
209 * @param bytes the buffer to read from.
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);
225 * Calculates the length of the packet header, defined as the sum of the
226 * lengths of all defined fields (see {@link #PREAMBLE_FIELDS}).
228 * @return the sum of the length of preamble fields
230 protected int preambleLength() {
233 for (Field<?> f : PREAMBLE_FIELDS) {
234 sum += f.getLength();
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
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()}.
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()}.
259 * @return a new buffer containing the encoded preamble
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
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.
276 protected void preambleDefaults() {
279 target = new MACAddress();
281 packetType = packetType();
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.
291 * @return the packet type intended to be handled by this Packet subtype
293 public abstract int packetType();
296 * Returns the length of the payload specific to this packet subtype. The
297 * length of the preamble is specifically excluded.
299 * @return the length of this specialized packet payload
301 protected abstract int packetLength();
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.
308 * @param bytes the buffer to extract data from
310 public void parse(ByteBuffer bytes) {
312 parsePreamble(bytes);
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
323 * @param bytes the raw bytes to parse
325 protected abstract void parsePacket(ByteBuffer bytes);
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.
332 * @return the full packet payload
334 public ByteBuffer bytes() {
335 ByteBuffer preamble = preambleBytes();
338 ByteBuffer packet = packetBytes();
341 ByteBuffer ret = ByteBuffer.allocate(length()).put(preamble).put(packet);
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
353 * Note that returned ByteBuffers will have {@link ByteBuffer#rewind()}
354 * called automatically before they are appended to the final packet
357 * @return the packet payload
359 protected abstract ByteBuffer packetBytes();
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.
367 * @return the total length of this packet
369 public int length() {
370 return preambleLength() + packetLength();
374 * Returns a list of expected response packet types. An empty array means
375 * no responses are expected (suitable for response packet definitions),
377 * @return a list of expected responses
379 public abstract int[] expectedResponses();
381 public boolean isExpectedResponse(int type) {
382 for (int a : expectedResponses()) {
391 public boolean isFulfilled(Packet somePacket) {
392 if (isExpectedResponse(somePacket.getPacketType())) {