Skip to content

Commit

Permalink
Add LwM2m Test Object (Id:3441) to Leshan demos.
Browse files Browse the repository at this point in the history
  • Loading branch information
sbernard31 committed Oct 18, 2021
1 parent 0232b4b commit 7ef2ded
Show file tree
Hide file tree
Showing 7 changed files with 587 additions and 19 deletions.
@@ -0,0 +1,237 @@
/*******************************************************************************
* Copyright (c) 2021 Sierra Wireless and others.
*
* 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:
* Sierra Wireless - initial API and implementation
*******************************************************************************/
package org.eclipse.leshan.client.object;

import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import org.eclipse.leshan.client.resource.SimpleInstanceEnabler;
import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.node.LwM2mResourceInstance;
import org.eclipse.leshan.core.node.ObjectLink;
import org.eclipse.leshan.core.response.ExecuteResponse;
import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.core.util.RandomStringUtils;

/**
* This aims to implements LWM2M Test Object(Id:3441) from LWM2M registry.
*/
public class LwM2mTestObject extends SimpleInstanceEnabler {

private Random random = new Random(System.currentTimeMillis());

public LwM2mTestObject() {
super();
initialValues = new HashMap<>();

initialValues.put(4, Collections.EMPTY_MAP);

// single
initialValues.put(110, "initial value");
initialValues.put(120, 64l);
initialValues.put(130, 3.14159d);
initialValues.put(140, true);
initialValues.put(150, Hex.decodeHex("0123456789ABCDEF".toCharArray()));
initialValues.put(160, new Date(946684800000l));
initialValues.put(170, new ObjectLink(3, 0));

// multi
initialValues.put(1110, LwM2mResourceInstance.newStringInstance(0, "initial value"));
initialValues.put(1120, LwM2mResourceInstance.newIntegerInstance(0, 64l));
initialValues.put(1130, LwM2mResourceInstance.newFloatInstance(0, 3.14159d));
initialValues.put(1140, LwM2mResourceInstance.newBooleanInstance(0, true));
initialValues.put(1150,
LwM2mResourceInstance.newBinaryInstance(0, Hex.decodeHex("0123456789ABCDEF".toCharArray())));
initialValues.put(1160, LwM2mResourceInstance.newDateInstance(0, new Date(946684800000l)));
initialValues.put(1170, LwM2mResourceInstance.newObjectLinkInstance(0, new ObjectLink(3, 0)));
}

private void clearValues() {
Map<Integer, Object> clearedValues = new HashMap<>();

clearedValues.put(4, Collections.EMPTY_MAP);

// single
clearedValues.put(110, "");
clearedValues.put(120, 0l);
clearedValues.put(130, 0.0d);
clearedValues.put(140, false);
clearedValues.put(150, new byte[0]);
clearedValues.put(160, new Date(0l));
clearedValues.put(170, new ObjectLink());

// multi
clearedValues.put(1110, Collections.EMPTY_MAP);
clearedValues.put(1120, Collections.EMPTY_MAP);
clearedValues.put(1130, Collections.EMPTY_MAP);
clearedValues.put(1140, Collections.EMPTY_MAP);
clearedValues.put(1150, Collections.EMPTY_MAP);
clearedValues.put(1160, Collections.EMPTY_MAP);
clearedValues.put(1170, Collections.EMPTY_MAP);

fireResourcesChange(applyValues(clearedValues));
}

private void resetValues() {
fireResourcesChange(applyValues(initialValues));
}

private void randomValues() {

Map<Integer, Object> randomValues = new HashMap<>();
randomValues.put(4, generateResourceInstances(new StringGenerator()));

// single
randomValues.put(110, new StringGenerator().generate());
randomValues.put(120, new LongGenerator().generate());
randomValues.put(130, new DoubleGenerator().generate());
randomValues.put(140, new BooleanGenerator().generate());
randomValues.put(150, new BytesGenerator().generate());
randomValues.put(160, new DateGenerator().generate());
randomValues.put(170, new ObjectLinkGenerator().generate());

// multi
randomValues.put(1110, generateResourceInstances(new StringGenerator()));
randomValues.put(1120, generateResourceInstances(new LongGenerator()));
randomValues.put(1130, generateResourceInstances(new DoubleGenerator()));
randomValues.put(1140, generateResourceInstances(new BooleanGenerator()));
randomValues.put(1150, generateResourceInstances(new BytesGenerator()));
randomValues.put(1160, generateResourceInstances(new DateGenerator()));
randomValues.put(1170, generateResourceInstances(new ObjectLinkGenerator()));

fireResourcesChange(applyValues(randomValues));
}

private void storeParams(String params) {
// TODO add a real Execute Argument Parser
// https://github.com/eclipse/leshan/issues/1097
Map<Integer, String> arguments = new HashMap<>();
if (params != null && !params.isEmpty()) {
String[] args = params.split(",");

for (String arg : args) {
String[] part = arg.split("=");
Integer id = Integer.parseInt(part[0]);
if (part.length > 1) {
arguments.put(id, part[1].substring(1, part[1].length() - 1));
} else {
arguments.put(id, "");
}
}
}

Map<Integer, Object> newParams = new HashMap<>();
newParams.put(4, arguments);
fireResourcesChange(applyValues(newParams));
}

private Map<Integer, ?> generateResourceInstances(ValueGenerator<?> generator) {
HashMap<Integer, Object> instances = new HashMap<>();
for (int i = 0; i < random.nextInt(10); i++) {
instances.put(i, generator.generate());
}
return instances;
}

@Override
public ExecuteResponse execute(ServerIdentity identity, int resourceid, String params) {
switch (resourceid) {
case 0:
resetValues();
return ExecuteResponse.success();
case 1:
randomValues();
return ExecuteResponse.success();
case 2:
clearValues();
return ExecuteResponse.success();
case 3:
storeParams(params);
return ExecuteResponse.success();
default:
return super.execute(identity, resourceid, params);
}
}

/* ***************** Random Generator ****************** */

static interface ValueGenerator<T> {
T generate();
}

class StringGenerator implements ValueGenerator<String> {
@Override
public String generate() {
return RandomStringUtils.randomAlphanumeric(random.nextInt(20));
}
}

class LongGenerator implements ValueGenerator<Long> {
@Override
public Long generate() {
return random.nextLong();
}
}

class DoubleGenerator implements ValueGenerator<Double> {
@Override
public Double generate() {
return random.nextDouble();
}
}

class BooleanGenerator implements ValueGenerator<Boolean> {
@Override
public Boolean generate() {
return random.nextInt(2) == 1;
}
}

class BytesGenerator implements ValueGenerator<byte[]> {
@Override
public byte[] generate() {
byte[] bytes = new byte[random.nextInt(20)];
random.nextBytes(bytes);
return bytes;
}
}

class DateGenerator implements ValueGenerator<Date> {
@Override
public Date generate() {
// try to generate random date which is not so out of date.
long rd = System.currentTimeMillis();

// remove year randomly
rd -= (random.nextInt(20) - 10) * 31557600000l;

// add some variance in the year
rd += random.nextInt(3155760) * 1000l;

return new Date(rd);
}
}

class ObjectLinkGenerator implements ValueGenerator<ObjectLink> {
@Override
public ObjectLink generate() {
return new ObjectLink(random.nextInt(ObjectLink.MAXID - 1), random.nextInt(ObjectLink.MAXID - 1));
}
}
}
Expand Up @@ -131,6 +131,14 @@ protected LwM2mPath getResourcePath(int resourceId) {
return new LwM2mPath(getModel().id, getId(), resourceId);
}

protected LwM2mPath[] getResourcePaths(int... resourceIds) {
LwM2mPath[] paths = new LwM2mPath[resourceIds.length];
for (int i = 0; i < paths.length; i++) {
paths[i] = getResourcePath(resourceIds[i]);
}
return paths;
}

protected LwM2mPath getResourceInstancePath(int resourceId, int resourceInstanceId) {
return new LwM2mPath(getModel().id, getId(), resourceId, resourceInstanceId);
}
Expand Down
Expand Up @@ -22,9 +22,11 @@
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.model.ObjectModel;
Expand Down Expand Up @@ -143,6 +145,53 @@ public void setModel(ObjectModel objectModel) {
}
}

protected LwM2mPath[] applyValues(Map<Integer, Object> values) {
Set<LwM2mPath> changingResources = new HashSet<>();

for (ResourceModel resourceModel : getModel().resources.values()) {
if (resourceModel.operations.isReadable()) {
Object value = values.get(resourceModel.id);
// create the resource
LwM2mResource newResource = null;
if (value != null) {
if (resourceModel.multiple) {
// handle multi instances
if (value instanceof LwM2mResourceInstance) {
newResource = new LwM2mMultipleResource(resourceModel.id, resourceModel.type,
(LwM2mResourceInstance) value);
changingResources.add(getResourcePath(resourceModel.id));
} else if (value instanceof Map) {
@SuppressWarnings("unchecked")
Map<Integer, ?> val = (Map<Integer, ?>) value;
newResource = LwM2mMultipleResource.newResource(resourceModel.id, val, resourceModel.type);
}
} else {
// handle single instances
newResource = LwM2mSingleResource.newResource(resourceModel.id, value, resourceModel.type);
}
}
// add the resource
if (newResource != null) {
if (newResource.isMultiInstances()) {
for (Integer instanceID : newResource.getInstances().keySet()) {
changingResources.add(getResourceInstancePath(newResource.getId(), instanceID));
}
} else {
changingResources.add(getResourcePath(newResource.getId()));
}
LwM2mResource previous = resources.put(newResource.getId(), newResource);
if (previous.isMultiInstances()) {
for (Integer instanceID : previous.getInstances().keySet()) {
changingResources.add(getResourceInstancePath(previous.getId(), instanceID));
}
}
}
}
}

return changingResources.toArray(new LwM2mPath[changingResources.size()]);
}

protected LwM2mResource initializeResource(ObjectModel objectModel, ResourceModel resourceModel) {
if (!resourceModel.multiple) {
return initializeSingleResource(objectModel, resourceModel);
Expand Down Expand Up @@ -186,11 +235,19 @@ protected LwM2mSingleResource initializeSingleResource(ObjectModel objectModel,

protected LwM2mMultipleResource initializeMultipleResource(ObjectModel objectModel, ResourceModel resourceModel) {
if (initialValues != null) {
@SuppressWarnings("unchecked")
Map<Integer, ?> initialValue = (Map<Integer, ?>) initialValues.get(resourceModel.id);
Object initialValue = initialValues.get(resourceModel.id);
if (initialValue == null)
return null;
return LwM2mMultipleResource.newResource(resourceModel.id, initialValue, resourceModel.type);

if (initialValue instanceof LwM2mResourceInstance) {
return new LwM2mMultipleResource(resourceModel.id, resourceModel.type,
(LwM2mResourceInstance) initialValue);
} else if (initialValue instanceof Map) {
@SuppressWarnings("unchecked")
Map<Integer, ?> val = (Map<Integer, ?>) initialValue;
return LwM2mMultipleResource.newResource(resourceModel.id, val, resourceModel.type);
}
return null;
} else {
// no default value
Map<Integer, ?> emptyMap = Collections.emptyMap();
Expand Down
Expand Up @@ -42,6 +42,7 @@
import org.eclipse.leshan.client.demo.cli.interactive.InteractiveCommands;
import org.eclipse.leshan.client.engine.DefaultRegistrationEngineFactory;
import org.eclipse.leshan.client.object.Server;
import org.eclipse.leshan.client.object.LwM2mTestObject;
import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
import org.eclipse.leshan.client.resource.ObjectsInitializer;
import org.eclipse.leshan.client.resource.listener.ObjectsListenerAdapter;
Expand Down Expand Up @@ -71,6 +72,7 @@ public class LeshanClientDemo {

private static final Logger LOG = LoggerFactory.getLogger(LeshanClientDemo.class);
private static final int OBJECT_ID_TEMPERATURE_SENSOR = 3303;
private static final int OBJECT_ID_LWM2M_TEST_OBJECT = 3441;

public static void main(String[] args) {

Expand Down Expand Up @@ -182,6 +184,8 @@ public static LeshanClient createClient(LeshanClientDemoCLI cli, LwM2mModelRepos
initializer.setInstancesForObject(DEVICE, new MyDevice());
initializer.setInstancesForObject(LOCATION, locationInstance);
initializer.setInstancesForObject(OBJECT_ID_TEMPERATURE_SENSOR, new RandomTemperatureSensor());
initializer.setInstancesForObject(OBJECT_ID_LWM2M_TEST_OBJECT, new LwM2mTestObject());

List<LwM2mObjectEnabler> enablers = initializer.createAll();

// Create CoAP Config
Expand Down

0 comments on commit 7ef2ded

Please sign in to comment.