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

Leshan: Observe Objects with CompositeObserve #38

Merged
merged 1 commit into from
Jul 4, 2024
Merged
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 @@ -15,7 +15,7 @@ class InstanceSerializer(serializers.Serializer):
id = serializers.IntegerField(help_text="Instance Counter")


class ValueSerializer(serializers.Serializer):
class ObjectSerializer(serializers.Serializer):
instances = InstanceSerializer(many=True, required=False)
KIND_CHOICES = [
'obj',
Expand All @@ -24,6 +24,29 @@ class ValueSerializer(serializers.Serializer):
id = serializers.IntegerField(help_text="Object ID")


class ValueSerializer(serializers.Serializer):
# Annotation for Django Rest Framework for documentation generation
objects = serializers.ListField(
child=ObjectSerializer(),
help_text="List of LwM2M objects"
)

def to_representation(self, instance):
ret = {}
for key, value in instance.items():
serializer = ObjectSerializer(value, context=self.context)
ret[key] = serializer.data
return ret

def to_internal_value(self, data):
ret = {}
for key, value in data.items():
serializer = ObjectSerializer(data=value, context=self.context)
serializer.is_valid(raise_exception=True)
ret[key] = serializer.validated_data
return ret


class CompositeResourceSerializer(HandleResourceMixin, serializers.Serializer):
ep = serializers.CharField(max_length=255, help_text="Unique LwM2M Endpoint")
val = ValueSerializer()
Expand All @@ -34,19 +57,15 @@ def create(self, validated_data):

endpoint, _ = Endpoint.objects.get_or_create(endpoint=ep)

# Check if value is an object with instances (Composite resource)
if val['kind'] == 'obj':
obj_id = val.get('id')
for instance in val['instances']:
for _, obj in val.items():
obj_id = obj.get('id')
for instance in obj['instances']:
for resource in instance['resources']:
try:
self.handle_resource(endpoint, obj_id, resource)
except serializers.ValidationError as e:
# Re-raise the validation error to be handled by
# Django's validation system
raise e
else:
logger.error("Expected composite resource data.")
raise serializers.ValidationError("Expected composite resource data.")

return endpoint
54 changes: 48 additions & 6 deletions server/leshan/src/main/java/lwm2m/LeshanSvr.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
import org.eclipse.leshan.core.observation.CompositeObservation;
import org.eclipse.leshan.core.observation.SingleObservation;
import org.eclipse.leshan.core.request.ObserveRequest;
import org.eclipse.leshan.core.request.ObserveCompositeRequest;
import org.eclipse.leshan.core.response.ObserveCompositeResponse;
import org.eclipse.leshan.core.response.ObserveResponse;
import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.server.send.SendListener;
import org.eclipse.leshan.core.request.SendRequest;
import org.eclipse.leshan.core.request.ReadRequest;
Expand Down Expand Up @@ -189,10 +191,13 @@ private void onboardingDevice(Registration registration) {
try {
ReadResponse readResp = server.send(registration, new ReadRequest(3));
if (readResp.isSuccess()) {
/* Match the additional hierachiy of the observe response format */
mapper.enable(SerializationFeature.INDENT_OUTPUT);
ObjectNode node = mapper.createObjectNode();
node.put("ep", registration.getEndpoint());
node.set("val", mapper.valueToTree(readResp.getContent()));
ObjectNode valNode = mapper.createObjectNode();
valNode.set("/3", mapper.valueToTree(readResp.getContent()));
node.set("val", valNode);

dataSenderRest.sendData(ApiPath.COMPOSITE_RES, node);
} else {
Expand All @@ -204,12 +209,49 @@ private void onboardingDevice(Registration registration) {
e.printStackTrace();
}

/* Subscribe to individual objects */
int[][] objectLinks = {{3303, 0, 5700}, {3304, 0, 5701}, {3305, 0, 5702}};
for (int[] link : objectLinks) {
ObserveRequest observeRequest = new ObserveRequest(link[0], link[1], link[2]);
/* Subscribe to single resource instances as an example
* 3303: Temperature Sensor
*/
int[][] singleObjectLinks = {
{3303, 0, 5700}
};

for (int[] link : singleObjectLinks) {
try {
ObserveRequest singleRequest = new ObserveRequest(link[0], link[2]);

log.trace("Sending ObserveRequest: " + singleRequest);
server.send(registration, singleRequest);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

/* Subscribe to composite object instances as an example
* 10300: Custom Object Instance
*/
int[][] compositeObjectLinks = {
{10300}
};

for (int[] link : compositeObjectLinks) {
try {
server.send(registration, observeRequest);
List<LwM2mPath> paths = new ArrayList<>();
paths.add(new LwM2mPath(link[0]));

// Specify the content formats
ContentFormat requestContentFormat = ContentFormat.SENML_CBOR;
ContentFormat responseContentFormat = ContentFormat.SENML_CBOR;

// Create the ObserveCompositeRequest for the whole object instance
ObserveCompositeRequest observeCompositeRequest = new ObserveCompositeRequest(
requestContentFormat,
responseContentFormat,
paths
);

log.trace("Sending ObserveCompositeRequest: " + observeCompositeRequest);
server.send(registration, observeCompositeRequest);
} catch (InterruptedException e) {
e.printStackTrace();
}
Expand Down