Skip to content

Commit

Permalink
#1228: Add timestampedNodes support to SenML encoder.
Browse files Browse the repository at this point in the history
  • Loading branch information
adamsero authored and sbernard31 committed Apr 28, 2022
1 parent 389e60b commit a759c24
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@ public byte[] encodeTimestampedData(List<TimestampedLwM2mNode> timestampedNodes,
}

@Override
public byte[] encodeTimestampedNodes(TimestampedLwM2mNodes timestampedNodes, ContentFormat format,
LwM2mModel model) throws CodecException {
public byte[] encodeTimestampedNodes(TimestampedLwM2mNodes timestampedNodes, ContentFormat format, LwM2mModel model)
throws CodecException {
Validate.notNull(timestampedNodes);

if (format == null) {
Expand All @@ -213,8 +213,15 @@ public byte[] encodeTimestampedNodes(TimestampedLwM2mNodes timestampedNodes, Con
if (encoder == null) {
throw new CodecException("Content format %s is not supported", format);
}
// TODO implements timestamped nodes encoding with fall back to "not timestamped" multi node.
return encodeNodes(timestampedNodes.getNodes(), format, model);

if (encoder instanceof TimestampedMultiNodeEncoder) {
return ((TimestampedMultiNodeEncoder) encoder).encodeTimestampedNodes(timestampedNodes, model, converter);
} else if (encoder instanceof MultiNodeEncoder) {
return ((MultiNodeEncoder) encoder).encodeNodes(timestampedNodes.getNodes(), model, converter);
} else {
throw new CodecException("Encoder does not support multiple nodes encoding for this content format: %s",
format);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*******************************************************************************
* Copyright (c) 2022 Orange.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Orange
*******************************************************************************/
package org.eclipse.leshan.core.node.codec;

import org.eclipse.leshan.core.model.LwM2mModel;
import org.eclipse.leshan.core.node.TimestampedLwM2mNodes;

/**
* An encoder for {@link TimestampedLwM2mNodes}.
*
* @see DefaultLwM2mEncoder
*/
public interface TimestampedMultiNodeEncoder {
/**
* Serializes a {@link TimestampedLwM2mNodes} object into a byte array.
* <p>
*
* @param timestampedNodes timestamped nodes to be serialized
* @param model the collection of supported object models
* @param converter value converter for resources
* @return the serialized byte array
* @throws CodecException if encoding fails
*/
byte[] encodeTimestampedNodes(TimestampedLwM2mNodes timestampedNodes, LwM2mModel model,
LwM2mValueConverter converter) throws CodecException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
*
* Contributors:
* Boya Zhang - initial API and implementation
*******************************************************************************/
Expand All @@ -31,9 +31,11 @@
import org.eclipse.leshan.core.node.LwM2mResourceInstance;
import org.eclipse.leshan.core.node.ObjectLink;
import org.eclipse.leshan.core.node.TimestampedLwM2mNode;
import org.eclipse.leshan.core.node.TimestampedLwM2mNodes;
import org.eclipse.leshan.core.node.codec.CodecException;
import org.eclipse.leshan.core.node.codec.LwM2mValueConverter;
import org.eclipse.leshan.core.node.codec.MultiNodeEncoder;
import org.eclipse.leshan.core.node.codec.TimestampedMultiNodeEncoder;
import org.eclipse.leshan.core.node.codec.TimestampedNodeEncoder;
import org.eclipse.leshan.core.util.Validate;
import org.eclipse.leshan.senml.SenMLEncoder;
Expand All @@ -43,7 +45,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LwM2mNodeSenMLEncoder implements TimestampedNodeEncoder, MultiNodeEncoder {
public class LwM2mNodeSenMLEncoder implements TimestampedNodeEncoder, MultiNodeEncoder, TimestampedMultiNodeEncoder {
private static final Logger LOG = LoggerFactory.getLogger(LwM2mNodeSenMLEncoder.class);

private final SenMLEncoder encoder;
Expand Down Expand Up @@ -147,6 +149,42 @@ public byte[] encodeTimestampedData(List<TimestampedLwM2mNode> timestampedNodes,
}
}

@Override
public byte[] encodeTimestampedNodes(TimestampedLwM2mNodes timestampedNodes, LwM2mModel model,
LwM2mValueConverter converter) throws CodecException {
Validate.notEmpty(timestampedNodes.getTimestamps());

SenMLPack pack = new SenMLPack();
for (Long timestamp : timestampedNodes.getTimestamps()) {
Map<LwM2mPath, LwM2mNode> nodesAtTimestamp = timestampedNodes.getNodesAt(timestamp);
for (Entry<LwM2mPath, LwM2mNode> entry : nodesAtTimestamp.entrySet()) {
LwM2mPath path = entry.getKey();
InternalEncoder internalEncoder = new InternalEncoder();
internalEncoder.objectId = path.getObjectId();
internalEncoder.model = model;
internalEncoder.requestPath = path;
internalEncoder.converter = converter;
internalEncoder.records = new ArrayList<>();
LwM2mNode node = entry.getValue();
if (node != null) {
node.accept(internalEncoder);

List<SenMLRecord> records = internalEncoder.records;
if (!records.isEmpty()) {
records.get(0).setBaseTime(timestamp);
pack.addRecords(records);
}
}
}
}

try {
return encoder.toSenML(pack);
} catch (SenMLException e) {
throw new CodecException(e, "Unable to encode timestamped nodes: %s", timestampedNodes);
}
}

private static class InternalEncoder implements LwM2mNodeVisitor {
// visitor inputs
private int objectId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.eclipse.leshan.core.node.LwM2mResourceInstance;
import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.node.TimestampedLwM2mNode;
import org.eclipse.leshan.core.node.TimestampedLwM2mNodes;
import org.eclipse.leshan.core.node.codec.senml.LwM2mNodeSenMLEncoder;
import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.core.util.Hex;
Expand Down Expand Up @@ -379,6 +380,40 @@ public void senml_encode_timestamped_resources() throws CodecException {
Assert.assertEquals(expected, new String(encoded));
}

@Test
public void senml_json_encode_timestamped_nodes() throws CodecException {
TimestampedLwM2mNodes timestampedLwM2mNodes = TimestampedLwM2mNodes.builder()
.put(500_000_001L, new LwM2mPath(0, 0, 0), LwM2mSingleResource.newStringResource(0, "TestString"))
.put(500_000_002L, new LwM2mPath(0, 1),
new LwM2mObjectInstance(1, Arrays.asList(LwM2mSingleResource.newBooleanResource(1, true),
LwM2mSingleResource.newIntegerResource(2, 123))))
.build();

byte[] encoded = encoder.encodeTimestampedNodes(timestampedLwM2mNodes, ContentFormat.SENML_JSON, model);

String expectedString = new StringBuilder()
.append("[{\"bn\":\"/0/0/0\",\"bt\":500000001,\"vs\":\"TestString\"},") //
.append("{\"bn\":\"/0/1/\",\"bt\":500000002,\"n\":\"1\",\"vb\":true},") //
.append("{\"n\":\"2\",\"v\":123}]") //
.toString();

Assert.assertEquals(expectedString, new String(encoded));
}

@Test
public void senml_cbor_encode_timestamped_nodes() throws CodecException {
TimestampedLwM2mNodes timestampedLwM2mNodes = TimestampedLwM2mNodes.builder()
.put(500_000_004L, new LwM2mPath(0, 0, 0), LwM2mSingleResource.newStringResource(0, "SampleString"))
.put(500_000_005L, new LwM2mPath(0, 0, 1), LwM2mSingleResource.newBooleanResource(1, false))
.put(500_000_006L, new LwM2mPath(0, 0, 2), LwM2mSingleResource.newIntegerResource(2, 456)).build();

byte[] encoded = encoder.encodeTimestampedNodes(timestampedLwM2mNodes, ContentFormat.SENML_CBOR, model);

String expectedString = "83a321662f302f302f30221a1dcd6504036c53616d706c65537472696e67a321662f302f302f31221a1dcd650504f4a321662f302f302f32221a1dcd6506021901c8";

Assert.assertEquals(expectedString, Hex.encodeHexString(encoded));
}

@Test
public void senml_json_encode_resources() {
// Nodes to encode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ public FakeEncoder() {
}

@Override
public byte[] encodeNodes(Map<LwM2mPath, LwM2mNode> nodes, ContentFormat format, LwM2mModel model) {
public byte[] encodeTimestampedNodes(TimestampedLwM2mNodes timestampedNodes, ContentFormat format,
LwM2mModel model) {
return ("[{\"bn\":\"/2000/1/\",\"n\":\"3\",\"v\":12345,\"t\":268435456},"
+ "{\"n\":\"3\",\"v\":67890,\"t\":268435457}]").getBytes(StandardCharsets.UTF_8);
}
Expand Down

0 comments on commit a759c24

Please sign in to comment.