kopia lustrzana https://github.com/bertrik/ttnhabbridge
Update Cayenne code with support for packed encoding.
rodzic
dd9b7fa50e
commit
4635e8c79e
|
@ -4,7 +4,7 @@ package nl.sikken.bertrik.cayenne;
|
||||||
* Cayenne parsing exception.
|
* Cayenne parsing exception.
|
||||||
*/
|
*/
|
||||||
public class CayenneException extends Exception {
|
public class CayenneException extends Exception {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,5 +24,5 @@ public class CayenneException extends Exception {
|
||||||
public CayenneException(Throwable e) {
|
public CayenneException(Throwable e) {
|
||||||
super(e);
|
super(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import java.util.Locale;
|
||||||
* Representation of one measurement item in a cayenne message.
|
* Representation of one measurement item in a cayenne message.
|
||||||
*/
|
*/
|
||||||
public final class CayenneItem {
|
public final class CayenneItem {
|
||||||
|
|
||||||
private final int channel;
|
private final int channel;
|
||||||
private final ECayenneItem type;
|
private final ECayenneItem type;
|
||||||
private final Number[] values;
|
private final Number[] values;
|
||||||
|
@ -19,8 +19,8 @@ public final class CayenneItem {
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param channel the unique channel
|
* @param channel the unique channel
|
||||||
* @param type the type
|
* @param type the type
|
||||||
* @param values the values
|
* @param values the values
|
||||||
*/
|
*/
|
||||||
public CayenneItem(int channel, ECayenneItem type, Number[] values) {
|
public CayenneItem(int channel, ECayenneItem type, Number[] values) {
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
|
@ -32,11 +32,11 @@ public final class CayenneItem {
|
||||||
* Constructor for a single value
|
* Constructor for a single value
|
||||||
*
|
*
|
||||||
* @param channel the unique channel
|
* @param channel the unique channel
|
||||||
* @param type the type
|
* @param type the type
|
||||||
* @param value the value
|
* @param value the value
|
||||||
*/
|
*/
|
||||||
public CayenneItem(int channel, ECayenneItem type, Number value) {
|
public CayenneItem(int channel, ECayenneItem type, Number value) {
|
||||||
this(channel, type, new Number[] {value});
|
this(channel, type, new Number[] { value });
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getChannel() {
|
public int getChannel() {
|
||||||
|
@ -50,7 +50,7 @@ public final class CayenneItem {
|
||||||
public Number[] getValues() {
|
public Number[] getValues() {
|
||||||
return values.clone();
|
return values.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Number getValue() {
|
public Number getValue() {
|
||||||
return values[0];
|
return values[0];
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ public final class CayenneItem {
|
||||||
public String[] format() {
|
public String[] format() {
|
||||||
return type.format(values);
|
return type.format(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses one item from the byte buffer and returns it.
|
* Parses one item from the byte buffer and returns it.
|
||||||
*
|
*
|
||||||
|
@ -69,6 +69,21 @@ public final class CayenneItem {
|
||||||
public static CayenneItem parse(ByteBuffer bb) throws CayenneException {
|
public static CayenneItem parse(ByteBuffer bb) throws CayenneException {
|
||||||
try {
|
try {
|
||||||
int channel = bb.get();
|
int channel = bb.get();
|
||||||
|
return parsePacked(bb, channel);
|
||||||
|
} catch (BufferUnderflowException e) {
|
||||||
|
throw new CayenneException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses one item from the byte buffer and returns it.
|
||||||
|
*
|
||||||
|
* @param bb the byte buffer
|
||||||
|
* @return a new cayenne item
|
||||||
|
* @throws CayenneException if an error occurs during parsing
|
||||||
|
*/
|
||||||
|
public static CayenneItem parsePacked(ByteBuffer bb, int channel) throws CayenneException {
|
||||||
|
try {
|
||||||
int type = bb.get() & 0xFF;
|
int type = bb.get() & 0xFF;
|
||||||
ECayenneItem ct = ECayenneItem.parse(type);
|
ECayenneItem ct = ECayenneItem.parse(type);
|
||||||
Number[] values = ct.parse(bb);
|
Number[] values = ct.parse(bb);
|
||||||
|
@ -77,21 +92,21 @@ public final class CayenneItem {
|
||||||
throw new CayenneException(e);
|
throw new CayenneException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format(Locale.ROOT, "{chan=%d,type=%s,value=%s}", channel, type, Arrays.toString(format()));
|
return String.format(Locale.ROOT, "{chan=%d,type=%s,value=%s}", channel, type, Arrays.toString(format()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void encode(ByteBuffer bb) throws CayenneException {
|
public void encode(ByteBuffer bb) throws CayenneException {
|
||||||
try {
|
try {
|
||||||
bb.put((byte)channel);
|
bb.put((byte) channel);
|
||||||
bb.put((byte)type.getType());
|
bb.put((byte) type.getType());
|
||||||
type.encode(bb, values);
|
type.encode(bb, values);
|
||||||
} catch (BufferOverflowException e) {
|
} catch (BufferOverflowException e) {
|
||||||
throw new CayenneException(e);
|
throw new CayenneException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,30 @@ import java.util.List;
|
||||||
* A cayenne message containing cayenne data items.
|
* A cayenne message containing cayenne data items.
|
||||||
*/
|
*/
|
||||||
public final class CayenneMessage {
|
public final class CayenneMessage {
|
||||||
|
|
||||||
|
private final ECayennePayloadFormat format;
|
||||||
private final List<CayenneItem> items = new ArrayList<>();
|
private final List<CayenneItem> items = new ArrayList<>();
|
||||||
|
|
||||||
|
public CayenneMessage() {
|
||||||
|
this(ECayennePayloadFormat.DYNAMIC_SENSOR_PAYLOAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param format the payload format (e.g. parsed from the LoRaWAN port),
|
||||||
|
* currently only DYNAMIC_SENSOR_PAYLOAD and PACKED_SENSOR_PAYLOAD
|
||||||
|
* are supported.
|
||||||
|
*/
|
||||||
|
public CayenneMessage(ECayennePayloadFormat format) {
|
||||||
|
switch (format) {
|
||||||
|
case DYNAMIC_SENSOR_PAYLOAD:
|
||||||
|
case PACKED_SENSOR_PAYLOAD:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Payload format not supported: " + format);
|
||||||
|
}
|
||||||
|
this.format = format;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the byte array into a cayenne message.
|
* Parses the byte array into a cayenne message.
|
||||||
*
|
*
|
||||||
|
@ -21,16 +42,26 @@ public final class CayenneMessage {
|
||||||
* @return the cayenne message
|
* @return the cayenne message
|
||||||
* @throws CayenneException in case of a parsing problem
|
* @throws CayenneException in case of a parsing problem
|
||||||
*/
|
*/
|
||||||
public static CayenneMessage parse(byte[] data) throws CayenneException {
|
public void parse(byte[] data) throws CayenneException {
|
||||||
CayenneMessage message = new CayenneMessage();
|
|
||||||
ByteBuffer bb = ByteBuffer.wrap(data);
|
ByteBuffer bb = ByteBuffer.wrap(data);
|
||||||
|
int channel = 0;
|
||||||
while (bb.hasRemaining()) {
|
while (bb.hasRemaining()) {
|
||||||
CayenneItem item = CayenneItem.parse(bb);
|
CayenneItem item;
|
||||||
message.add(item);
|
switch (format) {
|
||||||
|
case DYNAMIC_SENSOR_PAYLOAD:
|
||||||
|
item = CayenneItem.parse(bb);
|
||||||
|
break;
|
||||||
|
case PACKED_SENSOR_PAYLOAD:
|
||||||
|
item = CayenneItem.parsePacked(bb, channel);
|
||||||
|
channel++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unsupported cayenne payload: " + format);
|
||||||
|
}
|
||||||
|
add(item);
|
||||||
}
|
}
|
||||||
return message;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a cayenne measurement item to the message.
|
* Adds a cayenne measurement item to the message.
|
||||||
*
|
*
|
||||||
|
@ -39,29 +70,31 @@ public final class CayenneMessage {
|
||||||
public void add(CayenneItem item) {
|
public void add(CayenneItem item) {
|
||||||
items.add(item);
|
items.add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes the cayenne message into a byte array.
|
* Encodes the cayenne message into a byte array.
|
||||||
*
|
*
|
||||||
* @param maxSize the maximum size of the cayenne message
|
* @param maxSize the maximum size of the cayenne message
|
||||||
* @return the byte array.
|
* @return the byte array.
|
||||||
* @throws CayenneException in case something went wrong during encoding (e.g. message too big)
|
* @throws CayenneException in case something went wrong during encoding (e.g.
|
||||||
|
* message too big)
|
||||||
*/
|
*/
|
||||||
public byte[] encode(int maxSize) throws CayenneException {
|
public byte[] encode(int maxSize) throws CayenneException {
|
||||||
ByteBuffer bb = ByteBuffer.allocate(maxSize).order(ByteOrder.LITTLE_ENDIAN);
|
ByteBuffer bb = ByteBuffer.allocate(maxSize).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
for (CayenneItem item : items) {
|
for (CayenneItem item : items) {
|
||||||
item.encode(bb);
|
item.encode(bb);
|
||||||
}
|
}
|
||||||
return Arrays.copyOfRange(bb.array(), 0, bb.position());
|
return Arrays.copyOfRange(bb.array(), 0, bb.position());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return an immutable list of measurement items in the order it appears in the raw data
|
* @return an immutable list of measurement items in the order it appears in the
|
||||||
|
* raw data
|
||||||
*/
|
*/
|
||||||
public List<CayenneItem> getItems() {
|
public List<CayenneItem> getItems() {
|
||||||
return Collections.unmodifiableList(items);
|
return Collections.unmodifiableList(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds an item by type.
|
* Finds an item by type.
|
||||||
*
|
*
|
||||||
|
@ -71,7 +104,7 @@ public final class CayenneMessage {
|
||||||
public CayenneItem ofType(ECayenneItem type) {
|
public CayenneItem ofType(ECayenneItem type) {
|
||||||
return items.stream().filter(i -> (i.getType() == type)).findFirst().orElse(null);
|
return items.stream().filter(i -> (i.getType() == type)).findFirst().orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds an item by channel.
|
* Finds an item by channel.
|
||||||
*
|
*
|
||||||
|
@ -86,5 +119,5 @@ public final class CayenneMessage {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return Arrays.toString(items.toArray());
|
return Arrays.toString(items.toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,5 +95,5 @@ public enum ECayenneItem {
|
||||||
public void encode(ByteBuffer bb, Number[] values) {
|
public void encode(ByteBuffer bb, Number[] values) {
|
||||||
formatter.encode(bb, values);
|
formatter.encode(bb, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
package nl.sikken.bertrik.cayenne;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See https://community.mydevices.com/t/cayenne-lpp-2-0/7510
|
||||||
|
*/
|
||||||
|
public enum ECayennePayloadFormat {
|
||||||
|
|
||||||
|
DYNAMIC_SENSOR_PAYLOAD(1),
|
||||||
|
PACKED_SENSOR_PAYLOAD(2),
|
||||||
|
FULL_SCALE_GPS_PAYLOAD(3),
|
||||||
|
|
||||||
|
ACTUATOR_COMMANDS(10),
|
||||||
|
DEVICE_PERIOD_CONFIGURATION(11),
|
||||||
|
SENSOR_PERIOD_CONFIGURATION(13),
|
||||||
|
SENSOR_ENABLE_CONFIGURATION(14);
|
||||||
|
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
ECayennePayloadFormat(int port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<Integer, ECayennePayloadFormat> MAP = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
Arrays.stream(values()).forEach(v -> MAP.put(v.port, v));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ECayennePayloadFormat fromPort(int port) {
|
||||||
|
return MAP.getOrDefault(port, DYNAMIC_SENSOR_PAYLOAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,57 +4,59 @@ import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around CayenneMessage to make composing a Cayenne message a bit easier.
|
* Wrapper around CayenneMessage to make composing a Cayenne message a bit
|
||||||
|
* easier.
|
||||||
*
|
*
|
||||||
* This makes it similar to the C API shown on https://mydevices.com/cayenne/docs/lora/#lora-cayenne-low-power-payload
|
* This makes it similar to the C API shown on
|
||||||
|
* https://mydevices.com/cayenne/docs/lora/#lora-cayenne-low-power-payload
|
||||||
*/
|
*/
|
||||||
public final class SimpleCayenne {
|
public final class SimpleCayenne {
|
||||||
|
|
||||||
private final CayenneMessage message = new CayenneMessage();
|
private final CayenneMessage message = new CayenneMessage();
|
||||||
private final Set<Integer> channels = new HashSet<>();
|
private final Set<Integer> channels = new HashSet<>();
|
||||||
|
|
||||||
public void addDigitalInput(int channel, int value) throws CayenneException {
|
public void addDigitalInput(int channel, int value) throws CayenneException {
|
||||||
CayenneItem item = new CayenneItem(channel, ECayenneItem.DIGITAL_INPUT, value);
|
CayenneItem item = new CayenneItem(channel, ECayenneItem.DIGITAL_INPUT, value);
|
||||||
addItem(item);
|
addItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDigitalOutput(int channel, int value) throws CayenneException {
|
public void addDigitalOutput(int channel, int value) throws CayenneException {
|
||||||
CayenneItem item = new CayenneItem(channel, ECayenneItem.DIGITAL_OUTPUT, value);
|
CayenneItem item = new CayenneItem(channel, ECayenneItem.DIGITAL_OUTPUT, value);
|
||||||
addItem(item);
|
addItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addAnalogInput(int channel, double value) throws CayenneException {
|
public void addAnalogInput(int channel, double value) throws CayenneException {
|
||||||
CayenneItem item = new CayenneItem(channel, ECayenneItem.ANALOG_INPUT, value);
|
CayenneItem item = new CayenneItem(channel, ECayenneItem.ANALOG_INPUT, value);
|
||||||
addItem(item);
|
addItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addAnalogOutput(int channel, double value) throws CayenneException {
|
public void addAnalogOutput(int channel, double value) throws CayenneException {
|
||||||
CayenneItem item = new CayenneItem(channel, ECayenneItem.ANALOG_OUTPUT, value);
|
CayenneItem item = new CayenneItem(channel, ECayenneItem.ANALOG_OUTPUT, value);
|
||||||
addItem(item);
|
addItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addIlluminance(int channel, double lux) throws CayenneException {
|
public void addIlluminance(int channel, double lux) throws CayenneException {
|
||||||
CayenneItem item = new CayenneItem(channel, ECayenneItem.ILLUMINANCE, lux);
|
CayenneItem item = new CayenneItem(channel, ECayenneItem.ILLUMINANCE, lux);
|
||||||
addItem(item);
|
addItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPresence(int channel, int value) throws CayenneException {
|
public void addPresence(int channel, int value) throws CayenneException {
|
||||||
CayenneItem item = new CayenneItem(channel, ECayenneItem.PRESENCE, value);
|
CayenneItem item = new CayenneItem(channel, ECayenneItem.PRESENCE, value);
|
||||||
addItem(item);
|
addItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTemperature(int channel, double temperature) throws CayenneException {
|
public void addTemperature(int channel, double temperature) throws CayenneException {
|
||||||
CayenneItem item = new CayenneItem(channel, ECayenneItem.TEMPERATURE, temperature);
|
CayenneItem item = new CayenneItem(channel, ECayenneItem.TEMPERATURE, temperature);
|
||||||
addItem(item);
|
addItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addRelativeHumidity(int channel, double humidity) throws CayenneException {
|
public void addRelativeHumidity(int channel, double humidity) throws CayenneException {
|
||||||
CayenneItem item = new CayenneItem(channel, ECayenneItem.HUMIDITY, humidity);
|
CayenneItem item = new CayenneItem(channel, ECayenneItem.HUMIDITY, humidity);
|
||||||
addItem(item);
|
addItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addAccelerometer(int channel, double x, double y, double z) throws CayenneException {
|
public void addAccelerometer(int channel, double x, double y, double z) throws CayenneException {
|
||||||
CayenneItem item = new CayenneItem(channel, ECayenneItem.ACCELEROMETER, new Double[] {x, y, z});
|
CayenneItem item = new CayenneItem(channel, ECayenneItem.ACCELEROMETER, new Double[] { x, y, z });
|
||||||
addItem(item);
|
addItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,41 +66,42 @@ public final class SimpleCayenne {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addGyrometer(int channel, double x, double y, double z) throws CayenneException {
|
public void addGyrometer(int channel, double x, double y, double z) throws CayenneException {
|
||||||
CayenneItem item = new CayenneItem(channel, ECayenneItem.GYROMETER, new Double[] {x, y, z});
|
CayenneItem item = new CayenneItem(channel, ECayenneItem.GYROMETER, new Double[] { x, y, z });
|
||||||
addItem(item);
|
addItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addGps(int channel, double latitude, double longitude, double altitude) throws CayenneException {
|
public void addGps(int channel, double latitude, double longitude, double altitude) throws CayenneException {
|
||||||
CayenneItem item =
|
CayenneItem item = new CayenneItem(channel, ECayenneItem.GPS_LOCATION,
|
||||||
new CayenneItem(channel, ECayenneItem.GPS_LOCATION, new Double[] {latitude, longitude, altitude});
|
new Double[] { latitude, longitude, altitude });
|
||||||
addItem(item);
|
addItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addItem(CayenneItem item) throws CayenneException {
|
private void addItem(CayenneItem item) throws CayenneException {
|
||||||
// verify that channel is unique
|
// verify that channel is unique
|
||||||
int channel = item.getChannel();
|
int channel = item.getChannel();
|
||||||
if (channels.contains(channel)) {
|
if (channels.contains(channel)) {
|
||||||
throw new CayenneException("Channel id " + channel + " need to be unique!");
|
throw new CayenneException("Channel id " + channel + " need to be unique!");
|
||||||
}
|
}
|
||||||
// add the item
|
// add the item
|
||||||
channels.add(channel);
|
channels.add(channel);
|
||||||
message.add(item);
|
message.add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes the data into the supplied buffer.
|
* Encodes the data into the supplied buffer.
|
||||||
*
|
*
|
||||||
* @param maxSize maximum size of the message
|
* @param maxSize maximum size of the message
|
||||||
* @return the length of data encoded
|
* @return the length of data encoded
|
||||||
* @throws CayenneException in case something went wrong during encoding (e.g. message too big)
|
* @throws CayenneException in case something went wrong during encoding (e.g.
|
||||||
|
* message too big)
|
||||||
*/
|
*/
|
||||||
public byte[] encode(int maxSize) throws CayenneException {
|
public byte[] encode(int maxSize) throws CayenneException {
|
||||||
return message.encode(maxSize);
|
return message.encode(maxSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return message.toString();
|
return message.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ public abstract class BaseFormatter implements IFormatter {
|
||||||
/**
|
/**
|
||||||
* Gets an integer value from the byte buffer.
|
* Gets an integer value from the byte buffer.
|
||||||
*
|
*
|
||||||
* @param bb the byte buffer
|
* @param bb the byte buffer
|
||||||
* @param n the number of bytes to get
|
* @param n the number of bytes to get
|
||||||
* @param signed whether it should be interpreted as signed value or not
|
* @param signed whether it should be interpreted as signed value or not
|
||||||
* @return the value
|
* @return the value
|
||||||
*/
|
*/
|
||||||
|
@ -24,12 +24,12 @@ public abstract class BaseFormatter implements IFormatter {
|
||||||
}
|
}
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts an integer value into a byte buffer
|
* Puts an integer value into a byte buffer
|
||||||
*
|
*
|
||||||
* @param bb the byte buffer
|
* @param bb the byte buffer
|
||||||
* @param n the number of bytes to put
|
* @param n the number of bytes to put
|
||||||
* @param value the value to encode
|
* @param value the value to encode
|
||||||
*/
|
*/
|
||||||
protected void putValue(ByteBuffer bb, int n, int value) {
|
protected void putValue(ByteBuffer bb, int n, int value) {
|
||||||
|
@ -40,5 +40,5 @@ public abstract class BaseFormatter implements IFormatter {
|
||||||
shift -= 8;
|
shift -= 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import java.util.Locale;
|
||||||
* Formatter for cayenne items which represent a GPS position.
|
* Formatter for cayenne items which represent a GPS position.
|
||||||
*/
|
*/
|
||||||
public final class GpsFormatter extends BaseFormatter {
|
public final class GpsFormatter extends BaseFormatter {
|
||||||
|
|
||||||
private static final double LAT_LON_SCALE = 1E-4;
|
private static final double LAT_LON_SCALE = 1E-4;
|
||||||
private static final double ALT_SCALE = 1E-2;
|
private static final double ALT_SCALE = 1E-2;
|
||||||
|
|
||||||
|
@ -16,23 +16,20 @@ public final class GpsFormatter extends BaseFormatter {
|
||||||
double lat = LAT_LON_SCALE * getValue(bb, 3, true);
|
double lat = LAT_LON_SCALE * getValue(bb, 3, true);
|
||||||
double lon = LAT_LON_SCALE * getValue(bb, 3, true);
|
double lon = LAT_LON_SCALE * getValue(bb, 3, true);
|
||||||
double alt = ALT_SCALE * getValue(bb, 3, true);
|
double alt = ALT_SCALE * getValue(bb, 3, true);
|
||||||
return new Double[] {lat, lon, alt};
|
return new Double[] { lat, lon, alt };
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] format(Number[] values) {
|
public String[] format(Number[] values) {
|
||||||
return new String[] {
|
return new String[] { String.format(Locale.ROOT, "%.4f", values[0]),
|
||||||
String.format(Locale.ROOT, "%.4f", values[0]),
|
String.format(Locale.ROOT, "%.4f", values[1]), String.format(Locale.ROOT, "%.2f", values[2]) };
|
||||||
String.format(Locale.ROOT, "%.4f", values[1]),
|
|
||||||
String.format(Locale.ROOT, "%.2f", values[2])
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void encode(ByteBuffer bb, Number[] values) {
|
public void encode(ByteBuffer bb, Number[] values) {
|
||||||
putValue(bb, 3, (int)Math.round(values[0].doubleValue() / LAT_LON_SCALE));
|
putValue(bb, 3, (int) Math.round(values[0].doubleValue() / LAT_LON_SCALE));
|
||||||
putValue(bb, 3, (int)Math.round(values[1].doubleValue() / LAT_LON_SCALE));
|
putValue(bb, 3, (int) Math.round(values[1].doubleValue() / LAT_LON_SCALE));
|
||||||
putValue(bb, 3, (int)Math.round(values[2].doubleValue() / ALT_SCALE));
|
putValue(bb, 3, (int) Math.round(values[2].doubleValue() / ALT_SCALE));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,8 @@ package nl.sikken.bertrik.cayenne.formatter;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for cayenne data structures that can be formatted as an array of strings.
|
* Interface for cayenne data structures that can be formatted as an array of
|
||||||
|
* strings.
|
||||||
*/
|
*/
|
||||||
public interface IFormatter {
|
public interface IFormatter {
|
||||||
|
|
||||||
|
@ -15,9 +16,9 @@ public interface IFormatter {
|
||||||
*/
|
*/
|
||||||
Number[] parse(ByteBuffer bb);
|
Number[] parse(ByteBuffer bb);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats the data into an array of strings.
|
* Formats the data into an array of strings. For example, for a GPS location it
|
||||||
* For example, for a GPS location it outputs: latitude in [0], longitude in [1], altitude in [2].
|
* outputs: latitude in [0], longitude in [1], altitude in [2].
|
||||||
*
|
*
|
||||||
* @param values the value as number array
|
* @param values the value as number array
|
||||||
* @return the string representation
|
* @return the string representation
|
||||||
|
@ -27,7 +28,7 @@ public interface IFormatter {
|
||||||
/**
|
/**
|
||||||
* Encodes the data into the byte buffer
|
* Encodes the data into the byte buffer
|
||||||
*
|
*
|
||||||
* @param bb the buffer to encode to
|
* @param bb the buffer to encode to
|
||||||
* @param values the values to encode
|
* @param values the values to encode
|
||||||
*/
|
*/
|
||||||
void encode(ByteBuffer bb, Number[] values);
|
void encode(ByteBuffer bb, Number[] values);
|
||||||
|
|
|
@ -6,14 +6,14 @@ import java.util.Locale;
|
||||||
public final class IntegerFormatter extends BaseFormatter {
|
public final class IntegerFormatter extends BaseFormatter {
|
||||||
|
|
||||||
private final int length;
|
private final int length;
|
||||||
private final int size;
|
private final int size;
|
||||||
private final boolean signed;
|
private final boolean signed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param length the number of elements
|
* @param length the number of elements
|
||||||
* @param size the size of each element
|
* @param size the size of each element
|
||||||
* @param signed if the element is signed
|
* @param signed if the element is signed
|
||||||
*/
|
*/
|
||||||
public IntegerFormatter(int length, int size, boolean signed) {
|
public IntegerFormatter(int length, int size, boolean signed) {
|
||||||
|
@ -22,29 +22,29 @@ public final class IntegerFormatter extends BaseFormatter {
|
||||||
this.signed = signed;
|
this.signed = signed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Number[] parse(ByteBuffer bb) {
|
public Number[] parse(ByteBuffer bb) {
|
||||||
Integer[] values = new Integer[length];
|
Integer[] values = new Integer[length];
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
values[i] = getValue(bb, size, signed);
|
values[i] = getValue(bb, size, signed);
|
||||||
}
|
}
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] format(Number[] values) {
|
public String[] format(Number[] values) {
|
||||||
String[] formatted = new String[length];
|
String[] formatted = new String[length];
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
formatted[i] = String.format(Locale.ROOT, "%d", values[i].intValue());
|
formatted[i] = String.format(Locale.ROOT, "%d", values[i].intValue());
|
||||||
}
|
}
|
||||||
return formatted;
|
return formatted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void encode(ByteBuffer bb, Number[] values) {
|
public void encode(ByteBuffer bb, Number[] values) {
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
putValue(bb, size, values[i].intValue());
|
putValue(bb, size, values[i].intValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,145 +11,162 @@ import org.slf4j.LoggerFactory;
|
||||||
* Unit tests for CayenneMessage.
|
* Unit tests for CayenneMessage.
|
||||||
*/
|
*/
|
||||||
public final class CayenneMessageTest {
|
public final class CayenneMessageTest {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(CayenneMessageTest.class);
|
private static final Logger LOG = LoggerFactory.getLogger(CayenneMessageTest.class);
|
||||||
|
|
||||||
private final int MAX_BUF_SIZE = 500;
|
private final int MAX_BUF_SIZE = 500;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies example from specification.
|
* Verifies example from specification.
|
||||||
|
*
|
||||||
* @throws CayenneException in case of a parsing exception
|
* @throws CayenneException in case of a parsing exception
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testTwoTemperatureSensors() throws CayenneException {
|
public void testTwoTemperatureSensors() throws CayenneException {
|
||||||
final byte[] data = {0x03, 0x67, 0x01, 0x10, 0x05, 0x67, 0x00, (byte) 0xFF};
|
byte[] data = { 0x03, 0x67, 0x01, 0x10, 0x05, 0x67, 0x00, (byte) 0xFF };
|
||||||
final CayenneMessage payload = CayenneMessage.parse(data);
|
CayenneMessage payload = new CayenneMessage();
|
||||||
|
payload.parse(data);
|
||||||
LOG.info("payload: {}", payload);
|
LOG.info("payload: {}", payload);
|
||||||
|
|
||||||
Assert.assertArrayEquals(new String[] {"27.2"}, payload.ofChannel(3).format());
|
Assert.assertArrayEquals(new String[] { "27.2" }, payload.ofChannel(3).format());
|
||||||
Assert.assertArrayEquals(new String[] {"25.5"}, payload.ofChannel(5).format());
|
Assert.assertArrayEquals(new String[] { "25.5" }, payload.ofChannel(5).format());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies example from specification.
|
* Verifies example from specification.
|
||||||
|
*
|
||||||
* @throws CayenneException in case of a parsing exception
|
* @throws CayenneException in case of a parsing exception
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testTemperaturePlusAccel() throws CayenneException {
|
public void testTemperaturePlusAccel() throws CayenneException {
|
||||||
final byte[] data =
|
byte[] data = { 0x01, 0x67, (byte) 0xFF, (byte) 0xD7, 0x06, 0x71, 0x04, (byte) 0xD2, (byte) 0xFB, 0x2E, 0x00,
|
||||||
{0x01, 0x67, (byte) 0xFF, (byte) 0xD7, 0x06, 0x71, 0x04, (byte) 0xD2, (byte) 0xFB, 0x2E, 0x00, 0x00};
|
0x00 };
|
||||||
final CayenneMessage payload = CayenneMessage.parse(data);
|
CayenneMessage payload = new CayenneMessage();
|
||||||
|
payload.parse(data);
|
||||||
|
|
||||||
Assert.assertArrayEquals(new String[] {"-4.1"}, payload.ofChannel(1).format());
|
Assert.assertArrayEquals(new String[] { "-4.1" }, payload.ofChannel(1).format());
|
||||||
Assert.assertArrayEquals(new String[] {"1.234", "-1.234", "0.000"}, payload.ofChannel(6).format());
|
Assert.assertArrayEquals(new String[] { "1.234", "-1.234", "0.000" }, payload.ofChannel(6).format());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies example from specification.
|
* Verifies example from specification.
|
||||||
|
*
|
||||||
* @throws CayenneException in case of a parsing exception
|
* @throws CayenneException in case of a parsing exception
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testGps() throws CayenneException {
|
public void testGps() throws CayenneException {
|
||||||
final byte[] data =
|
byte[] data = { 0x01, (byte) 0x88, 0x06, 0x076, 0x5f, (byte) 0xf2, (byte) 0x96, 0x0a, 0x00, 0x03, (byte) 0xe8 };
|
||||||
{0x01, (byte) 0x88, 0x06, 0x076, 0x5f, (byte) 0xf2, (byte) 0x96, 0x0a, 0x00, 0x03, (byte) 0xe8};
|
CayenneMessage payload = new CayenneMessage();
|
||||||
final CayenneMessage payload = CayenneMessage.parse(data);
|
payload.parse(data);
|
||||||
|
|
||||||
Assert.assertArrayEquals(new String[] {"42.3519", "-87.9094", "10.00"}, payload.ofChannel(1).format());
|
Assert.assertArrayEquals(new String[] { "42.3519", "-87.9094", "10.00" }, payload.ofChannel(1).format());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies parsing of humidity value.
|
* Verifies parsing of humidity value.
|
||||||
|
*
|
||||||
* @throws CayenneException in case of a parsing exception
|
* @throws CayenneException in case of a parsing exception
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testHumidity() throws CayenneException {
|
public void testHumidity() throws CayenneException {
|
||||||
final byte[] data = {1, 104, 100};
|
byte[] data = { 1, 104, 100 };
|
||||||
final CayenneMessage payload = CayenneMessage.parse(data);
|
CayenneMessage payload = new CayenneMessage();
|
||||||
|
payload.parse(data);
|
||||||
Assert.assertArrayEquals(new String[] {"50.0"}, payload.ofChannel(1).format());
|
|
||||||
|
Assert.assertArrayEquals(new String[] { "50.0" }, payload.ofChannel(1).format());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies parsing of some actual data from a sodaq one.
|
* Verifies parsing of some actual data from a sodaq one.
|
||||||
|
*
|
||||||
* @throws CayenneException in case of a parsing exception
|
* @throws CayenneException in case of a parsing exception
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testActualData() throws CayenneException {
|
public void testActualData() throws CayenneException {
|
||||||
final String base64 = "AYgH8CEAt1D//zgCAmDQA2cBDg==";
|
String base64 = "AYgH8CEAt1D//zgCAmDQA2cBDg==";
|
||||||
final byte[] data = Base64.getDecoder().decode(base64);
|
byte[] data = Base64.getDecoder().decode(base64);
|
||||||
final CayenneMessage payload = CayenneMessage.parse(data);
|
CayenneMessage payload = new CayenneMessage();
|
||||||
|
payload.parse(data);
|
||||||
|
|
||||||
Assert.assertArrayEquals(new String[] {"52.0225", "4.6928", "-2.00"}, payload.ofChannel(1).format());
|
Assert.assertArrayEquals(new String[] { "52.0225", "4.6928", "-2.00" }, payload.ofChannel(1).format());
|
||||||
Assert.assertArrayEquals(new String[] {"247.84"}, payload.ofChannel(2).format());
|
Assert.assertArrayEquals(new String[] { "247.84" }, payload.ofChannel(2).format());
|
||||||
Assert.assertArrayEquals(new String[] {"27.0"}, payload.ofChannel(3).format());
|
Assert.assertArrayEquals(new String[] { "27.0" }, payload.ofChannel(3).format());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies parsing of some actual data from a sodaq one, with a fix applied to the voltage value.
|
* Verifies parsing of some actual data from a sodaq one, with a fix applied to
|
||||||
|
* the voltage value.
|
||||||
|
*
|
||||||
* @throws CayenneException in case of a parsing exception
|
* @throws CayenneException in case of a parsing exception
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testActualData2() throws CayenneException {
|
public void testActualData2() throws CayenneException {
|
||||||
final String base64 = "AYgH8CEAt03/+VwCAgGfA2cA8A==";
|
String base64 = "AYgH8CEAt03/+VwCAgGfA2cA8A==";
|
||||||
final byte[] data = Base64.getDecoder().decode(base64);
|
byte[] data = Base64.getDecoder().decode(base64);
|
||||||
final CayenneMessage payload = CayenneMessage.parse(data);
|
CayenneMessage payload = new CayenneMessage();
|
||||||
|
payload.parse(data);
|
||||||
|
|
||||||
// verify we can get at the data by channel
|
// verify we can get at the data by channel
|
||||||
Assert.assertArrayEquals(new String[] {"52.0225", "4.6925", "-17.00"}, payload.ofChannel(1).format());
|
Assert.assertArrayEquals(new String[] { "52.0225", "4.6925", "-17.00" }, payload.ofChannel(1).format());
|
||||||
Assert.assertArrayEquals(new String[] {"4.15"}, payload.ofChannel(2).format());
|
Assert.assertArrayEquals(new String[] { "4.15" }, payload.ofChannel(2).format());
|
||||||
Assert.assertArrayEquals(new String[] {"24.0"}, payload.ofChannel(3).format());
|
Assert.assertArrayEquals(new String[] { "24.0" }, payload.ofChannel(3).format());
|
||||||
|
|
||||||
// verify we can also get data by type
|
// verify we can also get data by type
|
||||||
Assert.assertArrayEquals(new String[] {"52.0225", "4.6925", "-17.00"},
|
Assert.assertArrayEquals(new String[] { "52.0225", "4.6925", "-17.00" },
|
||||||
payload.ofType(ECayenneItem.GPS_LOCATION).format());
|
payload.ofType(ECayenneItem.GPS_LOCATION).format());
|
||||||
Assert.assertArrayEquals(new String[] {"4.15"}, payload.ofType(ECayenneItem.ANALOG_INPUT).format());
|
Assert.assertArrayEquals(new String[] { "4.15" }, payload.ofType(ECayenneItem.ANALOG_INPUT).format());
|
||||||
Assert.assertArrayEquals(new String[] {"24.0"}, payload.ofType(ECayenneItem.TEMPERATURE).format());
|
Assert.assertArrayEquals(new String[] { "24.0" }, payload.ofType(ECayenneItem.TEMPERATURE).format());
|
||||||
|
|
||||||
// verify non-existing channel and type
|
// verify non-existing channel and type
|
||||||
Assert.assertNull(payload.ofChannel(0));
|
Assert.assertNull(payload.ofChannel(0));
|
||||||
Assert.assertNull(payload.ofType(ECayenneItem.BAROMETER));
|
Assert.assertNull(payload.ofType(ECayenneItem.BAROMETER));
|
||||||
|
|
||||||
// verify toString method
|
// verify toString method
|
||||||
Assert.assertNotNull(payload.toString());
|
Assert.assertNotNull(payload.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies parsing an empty string.
|
* Verifies parsing an empty string.
|
||||||
|
*
|
||||||
* @throws CayenneException in case of a parsing exception
|
* @throws CayenneException in case of a parsing exception
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testParseEmpty() throws CayenneException {
|
public void testParseEmpty() throws CayenneException {
|
||||||
final CayenneMessage payload = CayenneMessage.parse(new byte[0]);
|
CayenneMessage payload = new CayenneMessage();
|
||||||
|
payload.parse(new byte[0]);
|
||||||
Assert.assertTrue(payload.getItems().isEmpty());
|
Assert.assertTrue(payload.getItems().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies parsing a short buffer
|
* Verifies parsing a short buffer
|
||||||
|
*
|
||||||
* @throws CayenneException in case of a parsing exception
|
* @throws CayenneException in case of a parsing exception
|
||||||
*/
|
*/
|
||||||
@Test(expected = CayenneException.class)
|
@Test(expected = CayenneException.class)
|
||||||
public void testShortBuffer() throws CayenneException {
|
public void testShortBuffer() throws CayenneException {
|
||||||
CayenneMessage.parse(new byte[] {0});
|
new CayenneMessage().parse(new byte[] { 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies parsing of a buffer containing a non-supported data type.
|
* Verifies parsing of a buffer containing a non-supported data type.
|
||||||
|
*
|
||||||
* @throws CayenneException in case of a parsing exception
|
* @throws CayenneException in case of a parsing exception
|
||||||
*/
|
*/
|
||||||
@Test(expected = CayenneException.class)
|
@Test(expected = CayenneException.class)
|
||||||
public void testInvalidType() throws CayenneException {
|
public void testInvalidType() throws CayenneException {
|
||||||
CayenneMessage.parse(new byte[] {0, 100});
|
new CayenneMessage().parse(new byte[] { 0, 100 });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies parsing of a buffer containing insufficient data during parsing.
|
* Verifies parsing of a buffer containing insufficient data during parsing.
|
||||||
|
*
|
||||||
* @throws CayenneException in case of a parsing exception
|
* @throws CayenneException in case of a parsing exception
|
||||||
*/
|
*/
|
||||||
@Test(expected = CayenneException.class)
|
@Test(expected = CayenneException.class)
|
||||||
public void testShortData() throws CayenneException {
|
public void testShortData() throws CayenneException {
|
||||||
CayenneMessage.parse(new byte[] {2, 1});
|
new CayenneMessage().parse(new byte[] { 2, 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies encoding of a float value.
|
* Verifies encoding of a float value.
|
||||||
*
|
*
|
||||||
|
@ -157,15 +174,16 @@ public final class CayenneMessageTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void encodeFloat() throws CayenneException {
|
public void encodeFloat() throws CayenneException {
|
||||||
final CayenneMessage message = new CayenneMessage();
|
CayenneMessage message = new CayenneMessage();
|
||||||
message.add(new CayenneItem(1, ECayenneItem.ANALOG_INPUT, -12.34));
|
message.add(new CayenneItem(1, ECayenneItem.ANALOG_INPUT, -12.34));
|
||||||
|
|
||||||
final byte[] encoded = message.encode(MAX_BUF_SIZE);
|
byte[] encoded = message.encode(MAX_BUF_SIZE);
|
||||||
|
|
||||||
final CayenneMessage decoded = CayenneMessage.parse(encoded);
|
CayenneMessage decoded = new CayenneMessage();
|
||||||
|
decoded.parse(encoded);
|
||||||
Assert.assertEquals(-12.34, decoded.getItems().get(0).getValues()[0].doubleValue(), 0.01);
|
Assert.assertEquals(-12.34, decoded.getItems().get(0).getValues()[0].doubleValue(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies encoding of a humidity value.
|
* Verifies encoding of a humidity value.
|
||||||
*
|
*
|
||||||
|
@ -173,18 +191,19 @@ public final class CayenneMessageTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void encodeHumidity() throws CayenneException {
|
public void encodeHumidity() throws CayenneException {
|
||||||
final CayenneMessage message = new CayenneMessage();
|
CayenneMessage message = new CayenneMessage();
|
||||||
message.add(new CayenneItem(1, ECayenneItem.HUMIDITY, 35.5));
|
message.add(new CayenneItem(1, ECayenneItem.HUMIDITY, 35.5));
|
||||||
|
|
||||||
final byte[] encoded = message.encode(MAX_BUF_SIZE);
|
|
||||||
final CayenneMessage decoded = CayenneMessage.parse(encoded);
|
|
||||||
|
|
||||||
final CayenneItem item = decoded.getItems().get(0);
|
byte[] encoded = message.encode(MAX_BUF_SIZE);
|
||||||
|
CayenneMessage decoded = new CayenneMessage();
|
||||||
|
decoded.parse(encoded);
|
||||||
|
|
||||||
|
CayenneItem item = decoded.getItems().get(0);
|
||||||
Assert.assertEquals(ECayenneItem.HUMIDITY, item.getType());
|
Assert.assertEquals(ECayenneItem.HUMIDITY, item.getType());
|
||||||
Assert.assertEquals(35.5, item.getValues()[0].doubleValue(), 0.1);
|
Assert.assertEquals(35.5, item.getValues()[0].doubleValue(), 0.1);
|
||||||
Assert.assertEquals("35.5", item.format()[0]);
|
Assert.assertEquals("35.5", item.format()[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies encoding of a digital input.
|
* Verifies encoding of a digital input.
|
||||||
*
|
*
|
||||||
|
@ -192,17 +211,18 @@ public final class CayenneMessageTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testDigitalInput() throws CayenneException {
|
public void testDigitalInput() throws CayenneException {
|
||||||
final CayenneMessage message = new CayenneMessage();
|
CayenneMessage message = new CayenneMessage();
|
||||||
message.add(new CayenneItem(1, ECayenneItem.DIGITAL_INPUT, 1));
|
message.add(new CayenneItem(1, ECayenneItem.DIGITAL_INPUT, 1));
|
||||||
|
|
||||||
final byte[] encoded = message.encode(MAX_BUF_SIZE);
|
|
||||||
final CayenneMessage decoded = CayenneMessage.parse(encoded);
|
|
||||||
|
|
||||||
final CayenneItem item = decoded.getItems().get(0);
|
byte[] encoded = message.encode(MAX_BUF_SIZE);
|
||||||
|
CayenneMessage decoded = new CayenneMessage();
|
||||||
|
decoded.parse(encoded);
|
||||||
|
|
||||||
|
CayenneItem item = decoded.getItems().get(0);
|
||||||
Assert.assertEquals(ECayenneItem.DIGITAL_INPUT, item.getType());
|
Assert.assertEquals(ECayenneItem.DIGITAL_INPUT, item.getType());
|
||||||
Assert.assertEquals(1, item.getValues()[0].intValue());
|
Assert.assertEquals(1, item.getValues()[0].intValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies encoding/decoding of a presence value (e.g. number of satellites)
|
* Verifies encoding/decoding of a presence value (e.g. number of satellites)
|
||||||
*
|
*
|
||||||
|
@ -210,17 +230,30 @@ public final class CayenneMessageTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testPresence() throws CayenneException {
|
public void testPresence() throws CayenneException {
|
||||||
final CayenneMessage message = new CayenneMessage();
|
CayenneMessage message = new CayenneMessage();
|
||||||
message.add(new CayenneItem(1, ECayenneItem.PRESENCE, 7));
|
message.add(new CayenneItem(1, ECayenneItem.PRESENCE, 7));
|
||||||
|
|
||||||
final byte[] encoded = message.encode(MAX_BUF_SIZE);
|
|
||||||
final CayenneMessage decoded = CayenneMessage.parse(encoded);
|
|
||||||
|
|
||||||
final CayenneItem item = decoded.getItems().get(0);
|
byte[] encoded = message.encode(MAX_BUF_SIZE);
|
||||||
|
CayenneMessage decoded = new CayenneMessage();
|
||||||
|
decoded.parse(encoded);
|
||||||
|
|
||||||
|
CayenneItem item = decoded.getItems().get(0);
|
||||||
Assert.assertEquals(ECayenneItem.PRESENCE, item.getType());
|
Assert.assertEquals(ECayenneItem.PRESENCE, item.getType());
|
||||||
Assert.assertEquals(7, item.getValues()[0].intValue());
|
Assert.assertEquals(7, item.getValues()[0].intValue());
|
||||||
Assert.assertEquals("7", item.format()[0]);
|
Assert.assertEquals("7", item.format()[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies decoding of packed Cayenne format
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testPackedFormat() throws CayenneException {
|
||||||
|
CayenneMessage message = new CayenneMessage(ECayennePayloadFormat.PACKED_SENSOR_PAYLOAD);
|
||||||
|
byte[] data = { 0x67, 0x01, 0x10, 0x67, 0x00, (byte) 0xFF };
|
||||||
|
message.parse(data);
|
||||||
|
|
||||||
|
Assert.assertEquals(27.2, message.ofChannel(0).getValue().doubleValue(), 0.01);
|
||||||
|
Assert.assertEquals(25.5, message.ofChannel(1).getValue().doubleValue(), 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package nl.sikken.bertrik.cayenne;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See https://community.mydevices.com/t/cayenne-lpp-2-0/7510
|
||||||
|
*/
|
||||||
|
public final class CayennePayloadFormatTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPort() {
|
||||||
|
Assert.assertEquals(ECayennePayloadFormat.DYNAMIC_SENSOR_PAYLOAD, ECayennePayloadFormat.fromPort(1));
|
||||||
|
Assert.assertEquals(ECayennePayloadFormat.PACKED_SENSOR_PAYLOAD, ECayennePayloadFormat.fromPort(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -9,11 +9,13 @@ import org.slf4j.LoggerFactory;
|
||||||
* Unit test for SimpleCayenne.
|
* Unit test for SimpleCayenne.
|
||||||
*/
|
*/
|
||||||
public final class SimpleCayenneTest {
|
public final class SimpleCayenneTest {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(SimpleCayenneTest.class);
|
private static final Logger LOG = LoggerFactory.getLogger(SimpleCayenneTest.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies basic functionality by adding some items and encoding it into a message
|
* Verifies basic functionality by adding some items and encoding it into a
|
||||||
|
* message
|
||||||
|
*
|
||||||
* @throws CayenneException in case of a problem encoding/decoding
|
* @throws CayenneException in case of a problem encoding/decoding
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
|
@ -32,13 +34,14 @@ public final class SimpleCayenneTest {
|
||||||
cayenne.addRelativeHumidity(11, 50.0);
|
cayenne.addRelativeHumidity(11, 50.0);
|
||||||
cayenne.addTemperature(12, 19.0);
|
cayenne.addTemperature(12, 19.0);
|
||||||
LOG.info("Encoded message: {}", cayenne);
|
LOG.info("Encoded message: {}", cayenne);
|
||||||
|
|
||||||
// encode it
|
// encode it
|
||||||
byte[] data = cayenne.encode(500);
|
byte[] data = cayenne.encode(500);
|
||||||
Assert.assertNotNull(data);
|
Assert.assertNotNull(data);
|
||||||
|
|
||||||
// decode it
|
// decode it
|
||||||
CayenneMessage message = CayenneMessage.parse(data);
|
CayenneMessage message = new CayenneMessage();
|
||||||
|
message.parse(data);
|
||||||
Assert.assertEquals(12, message.getItems().size());
|
Assert.assertEquals(12, message.getItems().size());
|
||||||
Assert.assertEquals(3.82, message.ofType(ECayenneItem.ANALOG_INPUT).getValue().doubleValue(), 0.01);
|
Assert.assertEquals(3.82, message.ofType(ECayenneItem.ANALOG_INPUT).getValue().doubleValue(), 0.01);
|
||||||
Assert.assertEquals(55, message.ofType(ECayenneItem.DIGITAL_INPUT).getValue().intValue());
|
Assert.assertEquals(55, message.ofType(ECayenneItem.DIGITAL_INPUT).getValue().intValue());
|
||||||
|
@ -47,7 +50,7 @@ public final class SimpleCayenneTest {
|
||||||
Assert.assertEquals(42, message.ofType(ECayenneItem.PRESENCE).getValue().intValue(), 42);
|
Assert.assertEquals(42, message.ofType(ECayenneItem.PRESENCE).getValue().intValue(), 42);
|
||||||
Assert.assertEquals(19.0, message.ofType(ECayenneItem.TEMPERATURE).getValue().doubleValue(), 0.1);
|
Assert.assertEquals(19.0, message.ofType(ECayenneItem.TEMPERATURE).getValue().doubleValue(), 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies that a simple cayenne message with non-unique channels is rejected.
|
* Verifies that a simple cayenne message with non-unique channels is rejected.
|
||||||
*
|
*
|
||||||
|
|
|
@ -7,21 +7,21 @@ import org.junit.Test;
|
||||||
|
|
||||||
public final class GpsFormatterTest {
|
public final class GpsFormatterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeDecode() {
|
public void testEncodeDecode() {
|
||||||
GpsFormatter formatter = new GpsFormatter();
|
GpsFormatter formatter = new GpsFormatter();
|
||||||
Double[] coords = new Double[] {52.0, 4.1, -3.5};
|
Double[] coords = new Double[] { 52.0, 4.1, -3.5 };
|
||||||
|
|
||||||
// encode
|
// encode
|
||||||
ByteBuffer bb = ByteBuffer.allocate(100);
|
ByteBuffer bb = ByteBuffer.allocate(100);
|
||||||
formatter.encode(bb, coords);
|
formatter.encode(bb, coords);
|
||||||
|
|
||||||
|
// decode
|
||||||
|
bb.flip();
|
||||||
|
Double[] parsed = formatter.parse(bb);
|
||||||
|
Assert.assertEquals(coords[0], parsed[0], 0.01);
|
||||||
|
Assert.assertEquals(coords[1], parsed[1], 0.01);
|
||||||
|
Assert.assertEquals(coords[2], parsed[2], 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
// decode
|
|
||||||
bb.flip();
|
|
||||||
Double[] parsed = formatter.parse(bb);
|
|
||||||
Assert.assertEquals(coords[0], parsed[0], 0.01);
|
|
||||||
Assert.assertEquals(coords[1], parsed[1], 0.01);
|
|
||||||
Assert.assertEquals(coords[2], parsed[2], 0.01);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import nl.sikken.bertrik.cayenne.CayenneException;
|
import nl.sikken.bertrik.cayenne.CayenneException;
|
||||||
import nl.sikken.bertrik.cayenne.CayenneItem;
|
import nl.sikken.bertrik.cayenne.CayenneItem;
|
||||||
import nl.sikken.bertrik.cayenne.CayenneMessage;
|
import nl.sikken.bertrik.cayenne.CayenneMessage;
|
||||||
|
import nl.sikken.bertrik.cayenne.ECayennePayloadFormat;
|
||||||
import nl.sikken.bertrik.hab.ttn.TtnMessage;
|
import nl.sikken.bertrik.hab.ttn.TtnMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -148,7 +149,9 @@ public final class PayloadDecoder {
|
||||||
try {
|
try {
|
||||||
Instant time = message.getMetaData().getTime();
|
Instant time = message.getMetaData().getTime();
|
||||||
Sentence sentence = new Sentence(callSign, counter, time);
|
Sentence sentence = new Sentence(callSign, counter, time);
|
||||||
CayenneMessage cayenne = CayenneMessage.parse(message.getPayloadRaw());
|
ECayennePayloadFormat cayenneFormat = ECayennePayloadFormat.fromPort(message.getPort());
|
||||||
|
CayenneMessage cayenne = new CayenneMessage(cayenneFormat);
|
||||||
|
cayenne.parse(message.getPayloadRaw());
|
||||||
|
|
||||||
// add all items, in the order they appear in the cayenne message
|
// add all items, in the order they appear in the cayenne message
|
||||||
for (CayenneItem item : cayenne.getItems()) {
|
for (CayenneItem item : cayenne.getItems()) {
|
||||||
|
|
Ładowanie…
Reference in New Issue