From 408ebf519647cc3699a07e7869f5cc83424b286b Mon Sep 17 00:00:00 2001 From: Jonas Remmert Date: Thu, 13 Jun 2024 22:56:23 +0200 Subject: [PATCH] leshan: observe objects instead of individual resources Subscribing to objects directly, includes all resources. This is still preliminary, as those oberve commands should be triggered later directly from Django. Django: Modify serializer to accomodate for the added hierarchical layer in the composite request data. Signed-off-by: Jonas Remmert --- .../composite_resource_serializer.py | 35 +++++++++--- .../leshan/src/main/java/lwm2m/LeshanSvr.java | 54 ++++++++++++++++--- 2 files changed, 75 insertions(+), 14 deletions(-) diff --git a/server/django/sensordata/serializers/composite_resource_serializer.py b/server/django/sensordata/serializers/composite_resource_serializer.py index 0f54b40b..932d8c64 100644 --- a/server/django/sensordata/serializers/composite_resource_serializer.py +++ b/server/django/sensordata/serializers/composite_resource_serializer.py @@ -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', @@ -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() @@ -34,10 +57,9 @@ 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) @@ -45,8 +67,5 @@ def create(self, validated_data): # 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 diff --git a/server/leshan/src/main/java/lwm2m/LeshanSvr.java b/server/leshan/src/main/java/lwm2m/LeshanSvr.java index 61b51da6..300b36fa 100644 --- a/server/leshan/src/main/java/lwm2m/LeshanSvr.java +++ b/server/leshan/src/main/java/lwm2m/LeshanSvr.java @@ -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; @@ -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 { @@ -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 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(); }