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 all 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 @@ -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 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 @@ -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,40 @@ 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);

SenMLRecord record = internalEncoder.records.get(0);
record.setBaseTime(timestamp);
pack.addRecord(record);
}
}
}

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,37 @@ 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, 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
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