Skip to content

Commit

Permalink
More work about write attribute support.
Browse files Browse the repository at this point in the history
  • Loading branch information
sbernard31 committed Jan 25, 2024
1 parent f1b0023 commit a4c7318
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,11 @@ public synchronized void initRelation(LwM2mServer server, ObserveRequest request
LOG.debug("Handle observe relation for {} / {}", server, request);

// Store needed data for this observe relation.
attributes = strategy.selectNotificationsAttributes(request.getPath(), attributes);
updateNotificationData(server, request, attributes, node, sender);
}

protected NotificationAttributeTree getAttributes(LwM2mServer server, ObserveRequest request) {
// TODO use objectTree to get attribute;
// for testing, we return hardcoded value for resource 6/0/1

LwM2mObjectEnabler objectEnabler = objectTree.getObjectEnabler(request.getPath().getObjectId());
if (objectEnabler == null)
Expand All @@ -115,12 +114,13 @@ public synchronized void notificationTriggered(LwM2mServer server, ObserveReques

// ELSE handle Notification Attributes.
NotificationAttributeTree attributes = notificationData.getAttributes();
ObserveResponse candidateNotificationToSend = null;

// if there is criteria based on value
if (notificationData.hasCriteriaBasedOnValue()) {
ObserveResponse response = createResponse(server, request);
if (response.isSuccess()) {
LwM2mChildNode newValue = response.getContent();
candidateNotificationToSend = createResponse(server, request);
if (candidateNotificationToSend.isSuccess()) {
LwM2mChildNode newValue = candidateNotificationToSend.getContent();

// if criteria doesn't match do not raise any event.
if (!strategy.shouldTriggerNotificationBasedOnValueChange(request.getPath(), attributes,
Expand Down Expand Up @@ -149,7 +149,7 @@ public synchronized void notificationTriggered(LwM2mServer server, ObserveReques
ScheduledFuture<Void> pminTask = executor.schedule(new Callable<Void>() {
@Override
public Void call() throws Exception {
sendNotification(server, request, attributes, sender);
sendNotification(server, request, null, attributes, sender);
return null;
}
}, pmin - timeSinceLastNotification, TimeUnit.SECONDS);
Expand All @@ -159,7 +159,7 @@ public Void call() throws Exception {
}
}

sendNotification(server, request, attributes, sender);
sendNotification(server, request, candidateNotificationToSend, attributes, sender);
}

// TODO an optimization could be to synchronize by observe relation (identify by server / request)
Expand Down Expand Up @@ -204,7 +204,7 @@ protected synchronized void updateNotificationData(LwM2mServer server, ObserveRe
pmaxTask = executor.schedule(new Callable<Void>() {
@Override
public Void call() throws Exception {
sendNotification(server, request, attributes, sender);
sendNotification(server, request, null, attributes, sender);
return null;
}
}, strategy.getPmax(path, attributes), TimeUnit.SECONDS);
Expand All @@ -215,15 +215,17 @@ public Void call() throws Exception {
new NotificationData(attributes, lastSendingTime, lastValue, pmaxTask));
}

protected void sendNotification(LwM2mServer server, ObserveRequest request, NotificationAttributeTree attributes,
NotificationSender sender) {
ObserveResponse observeResponse = createResponse(server, request);
protected void sendNotification(LwM2mServer server, ObserveRequest request, ObserveResponse observeResponse,
NotificationAttributeTree attributes, NotificationSender sender) {
if (observeResponse == null) {
observeResponse = createResponse(server, request);
}
if (!sender.sendNotification(observeResponse)) {
// remove data as relation doesn't exist anymore
clear(server, request);
} else {
if (observeResponse.isFailure()) {
// remove data as relation must removed on failure
// remove data as relation must be removed on failure
clear(server, request);
} else {
updateNotificationData(server, request, attributes, observeResponse.getContent(), sender);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,47 @@
*******************************************************************************/
package org.eclipse.leshan.client.notification;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.leshan.client.notification.checker.CriteriaBasedOnValueChecker;
import org.eclipse.leshan.client.notification.checker.FloatChecker;
import org.eclipse.leshan.client.notification.checker.IntegerChecker;
import org.eclipse.leshan.client.notification.checker.UnsignedIntegerChecker;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttribute;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributeSet;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes;
import org.eclipse.leshan.core.link.lwm2m.attributes.NotificationAttributeTree;
import org.eclipse.leshan.core.model.ResourceModel.Type;
import org.eclipse.leshan.core.node.LwM2mChildNode;
import org.eclipse.leshan.core.node.LwM2mNode;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mResourceInstance;
import org.eclipse.leshan.core.node.LwM2mSingleResource;

// TODO The way write attribute should be handle is not clear in LWM2M specification (-_-!) ... which is not an ideal situation ...
// See : https://github.com/OpenMobileAlliance/OMA_LwM2M_for_Developers/issues/478
// So we create a dedicated class about how this attributes should be handled.
// The idea to then create a Interface + Default implement and let users implements their own strategy depending of their understanding or needs.
public class NotificationStrategy {

// TODO this first implementaiton is a very over simple one. Just for validate design
protected Map<Type, CriteriaBasedOnValueChecker> checkers = new HashMap<>();

public NotificationStrategy() {
checkers.put(Type.FLOAT, new FloatChecker());
checkers.put(Type.INTEGER, new IntegerChecker());
checkers.put(Type.UNSIGNED_INTEGER, new UnsignedIntegerChecker());
}

public NotificationAttributeTree selectNotificationsAttributes(LwM2mPath path,
NotificationAttributeTree attributes) {

LwM2mAttributeSet set = attributes.getWithInheritance(path);
NotificationAttributeTree result = new NotificationAttributeTree();
result.put(path, set);

return result;
}

public boolean hasPmin(NotificationAttributeTree attributes, LwM2mPath path) {
return getPmin(path, attributes) != null;
Expand Down Expand Up @@ -66,15 +92,40 @@ public Long getPmax(LwM2mPath path, NotificationAttributeTree attributes) {
}

public boolean hasCriteriaBasedOnValue(LwM2mPath path, NotificationAttributeTree attributes) {
// attributes.contains(LwM2mAttributes.GREATER_THAN) || attributes.contains(LwM2mAttributes.LESSER_THAN)
// || attributes.contains(LwM2mAttributes.STEP
// TODO not implemented yet
return false;
LwM2mAttributeSet set = attributes.get(path);
return set != null && (set.contains(LwM2mAttributes.GREATER_THAN) || set.contains(LwM2mAttributes.LESSER_THAN)
|| set.contains(LwM2mAttributes.STEP));
}

public boolean shouldTriggerNotificationBasedOnValueChange(LwM2mPath path, NotificationAttributeTree attributes,
LwM2mNode lastSentValue, LwM2mChildNode newValue) {
// TODO not implemented yet
return false;
LwM2mNode lastSentNode, LwM2mChildNode newNode) {

// Get Previous and New Values
Object lastSentValue;
Object newValue;
Type resourceType;
if (lastSentNode instanceof LwM2mSingleResource && newNode instanceof LwM2mSingleResource) {
lastSentValue = ((LwM2mSingleResource) lastSentNode).getValue();
newValue = ((LwM2mSingleResource) newNode).getValue();
resourceType = ((LwM2mSingleResource) newNode).getType();
} else if (lastSentNode instanceof LwM2mResourceInstance && newNode instanceof LwM2mResourceInstance) {
lastSentValue = ((LwM2mResourceInstance) lastSentNode).getValue();
newValue = ((LwM2mResourceInstance) newNode).getValue();
resourceType = ((LwM2mResourceInstance) newNode).getType();
} else {
throw new IllegalArgumentException(String.format(
"Unexpected nodes (last send node %s new value %s) for check about value changed, only LwM2mSingleResource or LwM2mResourceInstance are supported",
lastSentNode.getClass().getSimpleName(), newNode.getClass().getSimpleName()));
}

// Check criteria
CriteriaBasedOnValueChecker checker = checkers.get(resourceType);
if (checker == null) {
throw new IllegalArgumentException(
String.format("Unexpected resource type : %s is not supported", resourceType));
}
LwM2mAttributeSet set = attributes.get(path);
return checker.shouldTriggerNotificationBasedOnValueChange(set, lastSentValue, newValue);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*******************************************************************************
* Copyright (c) 2024 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.notification.checker;

import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributeSet;

public interface CriteriaBasedOnValueChecker {
boolean shouldTriggerNotificationBasedOnValueChange(LwM2mAttributeSet attributes, Object lastSentValue,
Object newValue);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*******************************************************************************
* Copyright (c) 2024 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.notification.checker;

import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributeSet;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes;

public class FloatChecker implements CriteriaBasedOnValueChecker {

@Override
public boolean shouldTriggerNotificationBasedOnValueChange(LwM2mAttributeSet attributes, Object lastSentValue,
Object newValue) {
Double lastSentDouble = (Double) lastSentValue;
Double newDouble = (Double) newValue;

if (attributes.contains(LwM2mAttributes.STEP)) {
return Math.abs(lastSentDouble - newDouble) >= attributes.get(LwM2mAttributes.STEP).getValue();
} else if (attributes.contains(LwM2mAttributes.LESSER_THAN)) {
Double lessThan = attributes.get(LwM2mAttributes.LESSER_THAN).getValue();
return lastSentDouble >= lessThan && newDouble < lessThan;
} else if (attributes.contains(LwM2mAttributes.GREATER_THAN)) {
Double greaterThan = attributes.get(LwM2mAttributes.LESSER_THAN).getValue();
return lastSentDouble <= greaterThan && newDouble < greaterThan;
}
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*******************************************************************************
* Copyright (c) 2024 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.notification.checker;

import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributeSet;
import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes;

public class IntegerChecker implements CriteriaBasedOnValueChecker {

@Override
public boolean shouldTriggerNotificationBasedOnValueChange(LwM2mAttributeSet attributes, Object lastSentValue,
Object newValue) {
Long lastSentLong = (Long) lastSentValue;
Long newLong = (Long) newValue;

if (attributes.contains(LwM2mAttributes.STEP)) {
return Math.abs(lastSentLong - newLong) >= attributes.get(LwM2mAttributes.STEP).getValue();
} else if (attributes.contains(LwM2mAttributes.LESSER_THAN)) {
Double lessThan = attributes.get(LwM2mAttributes.LESSER_THAN).getValue();
return lastSentLong >= lessThan && newLong < lessThan;
} else if (attributes.contains(LwM2mAttributes.GREATER_THAN)) {
Double greaterThan = attributes.get(LwM2mAttributes.LESSER_THAN).getValue();
return lastSentLong <= greaterThan && newLong < greaterThan;
}
return true;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*******************************************************************************
* Copyright (c) 2024 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.notification.checker;

import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributeSet;

public class UnsignedIntegerChecker implements CriteriaBasedOnValueChecker {

@Override
public boolean shouldTriggerNotificationBasedOnValueChange(LwM2mAttributeSet attributes, Object lastSentValue,
Object newValue) {
// ULong lastSentULong = (ULong) lastSentValue;
// ULong newULong = (ULong) newValue;

throw new IllegalStateException("Not implemented yet");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,29 @@ public void remove(LwM2mPath path) {
public LwM2mAttributeSet get(LwM2mPath path) {
return internalTree.get(path);
}

/**
* @return {@link LwM2mAttributeSet} attached to given level merged with value inherited from higher level.
*
* @See https://www.openmobilealliance.org/release/LightweightM2M/V1_2_1-20221209-A/HTML-Version/OMA-TS-LightweightM2M_Core-V1_2_1-20221209-A.html#7-3-2-0-732-lessNOTIFICATIONgreater-Class-Attributes
*/
public LwM2mAttributeSet getWithInheritance(LwM2mPath path) {
// For Root Path no need to "flatten" hierarchy
if (path.isRoot()) {
return get(path);
}

// For not root path
// Create Attribute Set taking inherited value into account.
LwM2mAttributeSet result = get(path);
LwM2mPath parentPath = path.toParenPath();
while (!parentPath.isRoot()) {
LwM2mAttributeSet parentAttributes = get(parentPath);
if (parentAttributes != null) {
result = parentAttributes.merge(result);
}
parentPath = parentPath.toParenPath();
}
return result;
}
}

0 comments on commit a4c7318

Please sign in to comment.