Question about Decoder Microservice and DecoderResult

Product/components used and version/fix level are you on:

Cumulocity 1015.0.309

Detailed explanation of the problem:

I have implemented a decoder microservice in Java that get called by an lwm2m custom action, queries an external API and then returns a DecoderResult object containing an Event with custom fragments. All seems to work fine, except that the custom fragments are missing in the created events. I used the following project as reference which suggests that custom fragments in events should be possible:

Is this a known issue or am i doing something wrong here?

Example

Decoder service log output showing DecoderResult:

{"alarms":null,"dataFragments":null,"message":null,"success":true,"alarmTypesToUpdate":null,"events":[{"source":{"id":"7660465"},"type":"c8y_LocationUpdate","time":"2023-05-24T08:39:09.683+02:00","text":"Location update","c8y_Position":{"alt":null,"lng":11.56999804,"uncertainty":2342,"fulfilledWith":"WIFI","lat":44.12599975}}],"measurements":null}

Resulting cumulocity event:

{
	"creationTime": "2023-05-24T06:39:09.794Z",
	"source": {
		"name": "My LwM2M Device",
		"self": "https://TENANT_URL/inventory/managedObjects/7660465",
		"id": "7660465"
	},
	"type": "c8y_LocationUpdate",
	"lastUpdated": "2023-05-24T06:39:09.794Z",
	"self": "https://TENANT_URL/event/events/9365743",
	"time": "2023-05-24T08:39:09.683+02:00",
	"id": "9365743",
	"text": "Location update"
}

As can be seen the “c8y_Position” custom fragment is missing in the created event. If i take the JSON of the event produced by my microservice and create the event manually via a POST to the “/event/events” endpoint the custom fragments are preserved.

Is your question related to the free trial, or to a production (customer) instance?

Production instance

Hi Daniel and warm welcome to the Tech Community Forums :slight_smile:

As you already noted with a manual API call the fragments are preserved.
Hence in general this works of course, as any custom fragments can be added to an event.

Could you share details of your microservice and how it is doing the API call to update the event?
Are you using Java and the Microservice SDK?

Another smaller remark:
As the “c8y_Position” fragment is used by Cumulocity I would recommend to put any real custom attributes - like uncertainty and fulfilledWith - into a separate fragment, e.g. “custom_Position”.
This would separate your real custom fragments to those known to Cumulocity and can prevent any confusion later on.

"c8y_Position":{"lng":11.56999804,"lat":44.12599975},
"custom_Position": {"uncertainty":2342,"fulfilledWith":"WIFI"}

Regards Kai

Hi Kai,

Thanks for the quick reply!

Yes i am using Java and the microservice SDK, i took the sample-lwm2m-custom-decoder as a reference. My current (working) solution is to not rely on the event creation by the platform from my DecoderResult “events” array but to POST the Event JSON (in the same format i wrote it to the DecoderResult before) directly to the /event/events endpoint, and just returning { “success”: true } as my DecoderResult.

I also tried to add a different custom fragment to my DecoderResult event (eg. “decoderCustomFragment”: true ), this also does not propagate to the resulting cumulocity event. Only the values of the properties “type”, “time” and “text” are present.

In the readme for the “sample-lwm2m-custom-decoder” under " Full decoder response sample" a custom fragment is shown as being part of a valid response, so i assumed this should work:

"events": [
{
    "source": {
        "id":"12345" },
    "type": "TestEvent 1",
    "text": "Data was decoded",
    "time": "2020-03-03T12:03:12.845Z",
    "myFragment": "my data"
},
...
]

Regards,
Daniel

Hi @dst ,

can you post a code snippet how you are adding the custom fragment to the eventRepresentation?
I think there is something wrong and we can correct that together when you post your code snippet.

Hi @Stefan_Witschel

Sure here is a code excerpt for the DecoderResult creation:

			C8yPosition c8y_Position = new C8yPosition();
			c8y_Position.setLat( groundFixResponse.getLat() );
			c8y_Position.setLng( groundFixResponse.getLon() );
			c8y_Position.setUncertainty( groundFixResponse.getUncertainty() );
			c8y_Position.setFulfilledWith( groundFixResponse.getFulfilledWith() );

	        var source = new ManagedObjectRepresentation();
	        source.setId( sourceDeviceId );
	        
	        var event = new EventRepresentation();
	        event.setSource(source);
	        event.setType("c8y_LocationUpdate");
	        event.setDateTime(DateTime.now());
	        event.setText("Location update by single cell positioning");
	        event.setProperty("c8y_Position", c8y_Position);
	        event.setProperty("decoderCustomFragment", true);
	        
			DecoderResult decoderResult = new DecoderResult();
			decoderResult.addEvent(event, false);
			decoderResult.setSuccess(true);
			return decoderResult;

Thanks in advance!

How is the DecoderResult handled afterwards?
Just creating the DecoderResult does not yet do any API call AFAIK.
You would need to handle the DecoderResult object in some way and take the event and send it to the platform.

Since it’s a decoder microservice my application creates a REST endpoint “/decode” and is marked as decoder in my cumulocity.json:

"isDecoder": {
    "name":"LocationDecoderService"
  }

This endpoint gets called by the platform via lwm2m “CustomActions” when a specific value is transmitted by our devices. The microservice returns the DecoderResult as response body for the “/decode” endpoint. The whole functionality is implemented as described here:

The expectation would then be as described here:

C8yPosition is a custom type I guess and cannot be deserialized to JSON on request.
When you are using standard types like “c8y.position” you can just do:

event.set(position)

for custom types you can use setProperty but have to make sure that the custom type extends svenson AbstractDynamicProperties.

Try to set a “String” as a customProperty for testing:

event.setProperty("decoderCustomFragment", "ThisIsAvalue");

So i made some adjustments and further testing unfortunately with the same results. What i changed:

  • Added extends AbstractDynamicProperties to my C8yPosition type and
  • Changed set event.setProperty to a String:
	        event.setProperty("LocationService", "ThisIsAvalue");

When testing locally the response body of the “/decode” call is now:

{
    "events": [
        {
            "id": null,
            "type": "c8y_LocationUpdate",
            "time": "2023-05-24T12:51:50.395+00:00",
            "creationTime": null,
            "text": "Location update by single cell positioning",
            "externalSource": null,
            "source": {
                "id": "7660465"
            },
            "dateTime": "2023-05-24T12:51:50.395Z",
            "creationDateTime": null,
            "lastUpdatedDateTime": null,
            "LocationService": "ThisIsAvalue",
            "c8y_Position": {
                "attrs": {},
                "lat": 48.25999975,
                "lng": 16.30999804,
                "alt": null,
                "uncertainty": 2342,
                "fulfilledWith": "WIFI"
            }
        }
    ],
    "success": true
}

So extending AbstractDynamicProperties seems to just have added the “attrs”: {} object, otherwise the JSON looks the same as before.

I now add the event via http POST request (as before) AND also return the DecoderResponse as response body for handling in Cumulocity. As expected two events are generated, the event generated by the platform is unfortunatly still missing all custom fragments:

Event generated by Cumulocity:

        {
            "creationTime": "2023-05-24T13:01:42.977Z",
            "source": {
                "name": "My LwM2M Device",
                "self": "https://TENANT_URL/inventory/managedObjects/7660465",
                "id": "7660465"
            },
            "type": "c8y_LocationUpdate",
            "lastUpdated": "2023-05-24T13:01:42.977Z",
            "self": "https://TENANT_URL/event/events/9374975",
            "time": "2023-05-24T15:01:42.970+02:00",
            "id": "9374975",
            "text": "Location update by single cell positioning"
        }

Event generated by POST request to “/event/events”:

        {
            "creationTime": "2023-05-24T13:01:42.932Z",
            "source": {
                "name": "My LwM2M Device",
                "self": "https://TENANT_URL/inventory/managedObjects/7660465",
                "id": "7660465"
            },
            "type": "c8y_LocationUpdate",
            "lastUpdated": "2023-05-24T13:01:42.932Z",
            "self": "https://TENANT_URL/event/events/9378650",
            "time": "2023-05-24T15:01:42.928+02:00",
            "id": "9378650",
            "text": "Location update by single cell positioning",
            "LocationService": "ThisIsAvalue",
            "c8y_Position": {
                "fullfilledWith": "WIFI",
                "lng": 11.56999804,
                "uncertainty": 2342,
                "lat": 44.12599975
            }
        }

I kind of expected this since i checked to logs of the old version and deserialization seemed to work fine before. Would you happen to have a working example of some decoder code that adds a custom fragment?

Thanks, Daniel

2 Likes

Thanks for your testing!
ok, the attrs property looks wrong in the c8y_Position fragment, so serialization to JSON doesn’t seem to be the issue here.

This pretty sounds like an issue with the lwm2m-agent not recognizing any custom fragments for decoded events when mapping to Cumulocity IoT.

Are you able to create a support incident for this so we can track this issue?

Ok thanks for the feedback, i will open a support ticket through our usual channels.

Regards,
Daniel