Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encoder support for TimestampedLwM2mNodes #1246

Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 [%s] ", format);
sbernard31 marked this conversation as resolved.
Show resolved Hide resolved
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*******************************************************************************
* 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 DefaultLwM2mDecoder
*/
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 @@ -30,11 +30,13 @@
import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.node.LwM2mResourceInstance;
import org.eclipse.leshan.core.node.ObjectLink;
import org.eclipse.leshan.core.node.TimestampedLwM2mNodes;
import org.eclipse.leshan.core.node.TimestampedLwM2mNode;
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.TimestampedNodeEncoder;
import org.eclipse.leshan.core.node.codec.TimestampedMultiNodeEncoder;
import org.eclipse.leshan.core.util.Validate;
sbernard31 marked this conversation as resolved.
Show resolved Hide resolved
import org.eclipse.leshan.senml.SenMLEncoder;
import org.eclipse.leshan.senml.SenMLException;
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,38 @@ 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);

internalEncoder.records.get(0).setBaseTime(timestamp);
pack.addRecords(internalEncoder.records);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very cosmetic but maybe cleaner/easier to read to put internalEncoder result in a variable, then change the base time then use this variable to put in in pack ?

}
}
}

try {
return encoder.toSenML(pack);
} catch (SenMLException e) {
throw new CodecException(e, "Unable to encode timestamped nodes[timestamps: %s] : %s", timestampedNodes.getTimestamps(), timestampedNodes.getNodes());
sbernard31 marked this conversation as resolved.
Show resolved Hide resolved
}
}

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 @@ -380,7 +381,40 @@ public void senml_encode_timestamped_resources() throws CodecException {
}

@Test
public void senml_json_encode_resources() {
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, 0, 1), LwM2mSingleResource.newBooleanResource(1, true))
.put(500_000_003L, new LwM2mPath(0, 0, 2), 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/0/1\",\"bt\":500000002,\"vb\":true},")
.append("{\"bn\":\"/0/0/2\",\"bt\":500000003,\"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
Map<LwM2mPath, LwM2mNode> nodes = new LinkedHashMap<>();
nodes.put(new LwM2mPath("3/0/0"), LwM2mSingleResource.newStringResource(0, "Open Mobile Alliance"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public void createClient(Map<String, String> additionalAttributes) {
initializer.setInstancesForObject(LwM2mId.DEVICE, new TestDevice("Eclipse Leshan", MODEL_NUMBER, "12345"));
initializer.setClassForObject(LwM2mId.ACCESS_CONTROL, DummyInstanceEnabler.class);
initializer.setInstancesForObject(TEST_OBJECT_ID, new DummyInstanceEnabler(0),
new SimpleInstanceEnabler(1, FLOAT_RESOURCE_ID, 12345d));
new SimpleInstanceEnabler(1, FLOAT_RESOURCE_ID, 123456d));
sbernard31 marked this conversation as resolved.
Show resolved Hide resolved
List<LwM2mObjectEnabler> objects = initializer.createAll();

// Build Client
Expand All @@ -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