0

I got a stranger error although I follow guideline in ditto example.

Octopus can published messages to MQTT. I can see them use MQTT client. WebApp shows connection established and send send event works. I can change values through "my.test.octopus"panel. But when I query it using API, I can only my values from webapp, never got vales from octopus.

I checked connectivity logs, seems mapping problem...I used the following to create mapping when creating connection:

"incomingScript": "function mapToDittoProtocolMsg(
                              headers, 
                              textPayload, 
                              bytePayload, 
                              contentType) {

    const jsonString = String.fromCharCode.apply(null, new Uint8Array(bytePayload));
    const jsonData = JSON.parse(jsonString);
    const thingId = jsonData.thingId;
    const value = { 
      temp_sensor: { 
        properties: { 
          value: jsonData.temp 
        }
      },
      altitude: { 
        properties: { 
          value: jsonData.alt 
        }
      }
    };

    return Ditto.buildDittoProtocolMsg('my.test', thingId, 'things', 'twin', 'commands', 'modify', '/features', headers, value);
}"

Thanks for help

update

The error manifests in the following log line:

See the following log statement:

"The message mapper configuration failed due to: unterminated regular expression literal (incomingScript#1) - in line/column #1/472," -- ""incomingScript": "function mapToDittoProtocolMsg(headers, textPayload, bytePayload, contentType) {var jsonData = JSON.parse(textPayload);const thingId = jsonData.thingId;const value = {temp_sensor: { properties: { value: jsonData.temp } }, altitude: { properties: { value: jsonData.alt } } }; return Ditto.buildDittoProtocolMsg('my.test', thingId, 'things', 'twin', 'commands', 'modify', '/features', headers, value); }"
Yannic Bürgmann
  • 6,301
  • 5
  • 43
  • 77
BobOntario
  • 11
  • 3
  • What I don't get is that the mapping script is 402 characters long and in the log we see an error in column 472. Did you copy paste the script from somewhere else and possibly added some hidden characters? – Yannic Bürgmann Apr 02 '19 at 07:02
  • something wrong with the terminal, I guess. Now it appears works. Thanks – BobOntario Apr 09 '19 at 02:04

1 Answers1

1

Your mapping script seems to work correctly. I created a unit test for it using the payload mapping testing from ditto-examples.

This test looks like the following:

 @Test
 public void incomingBytePayloadMapping() throws IOException {
     final Resource incomingMappingFunction = new Resource("incomingScript.js");
     final PayloadMappingFunction underTest = PayloadMappingFunction.fromJavaScript(incomingMappingFunction.getContent());

     final Map<String, String> headers = new HashMap<>();
     headers.put("content-type", ContentTypes.APPLICATION_OCTET_STREAM.toString());
     headers.put("device_id", "the-thing-id");

     final byte[] bytePayload = "{\"thingId\":\"my.test.thing\",\"temp\":25.6,\"alt\":11}".getBytes();
     final ExternalMessage message = ExternalMessageFactory.newExternalMessageBuilder(headers)
             .withBytes(bytePayload)
             .build();

     final Resource expectedAdaptableJsonResource = new Resource("expectedAdaptable.json");
     final JsonObject expectedAdaptableJson = JsonFactory.newObject(expectedAdaptableJsonResource.getContent());
     final Adaptable expectedAdaptable = ProtocolFactory
             .jsonifiableAdaptableFromJson(expectedAdaptableJson)
             .setDittoHeaders(DittoHeaders.of(headers));

     PayloadMappingTestCase.assertThat(message)
             .mappedByJavascriptPayloadMappingFunction(underTest)
             .isEqualTo(expectedAdaptable)
             .verify();
 }

incomingScript.js

function mapToDittoProtocolMsg(
    headers,
    textPayload,
    bytePayload,
    contentType) {

    const jsonString = String.fromCharCode.apply(null, new Uint8Array(bytePayload));
    const jsonData = JSON.parse(jsonString);
    const thingId = jsonData.thingId;
    const value = {
        temp_sensor: {
            properties: {
                value: jsonData.temp
            }
        },
        altitude: {
            properties: {
                value: jsonData.alt
            }
        }
    };

    return Ditto.buildDittoProtocolMsg('my.test', thingId, 'things', 'twin', 'commands', 'modify', '/features', headers,
                                       value);
}

expectedAdaptable.json

{
  "topic": "my.test/my.test.thing/things/twin/commands/modify",
  "headers": {},
  "path": "/features",
  "value": {
    "temp_sensor": {
      "properties": {
        "value": 25.6
      }
    },
    "altitude": {
      "properties": {
        "value": 11
      }
    }
  }
}

So far this seems to work, but in this test I assume the following incoming bytePayload:

final byte[] bytePayload = "{\"thingId\":\"my.test.thing\",\"temp\":25.6,\"alt\":11}".getBytes();

Could you somehow verify, that the byte payload your octopus is sending is looking correctly? Is the octopus really sending byte payload or is it text payload (application/json)?

update

According to the comment of Bob Su the octopus is sending text payload. In order to map this payload, you actually have to use the text payload instead of byte payload. In the following you'll see the updated incomingScript.

incomingScript.js

function mapToDittoProtocolMsg(
    headers,
    textPayload,
    bytePayload,
    contentType) {

    var jsonData = JSON.parse(textPayload);
    const thingId = jsonData.thingId;
    const value = {
        temp_sensor: {
            properties: {
                value: jsonData.temp
            }
        },
        altitude: {
            properties: {
                value: jsonData.alt
            }
        }
    };

    return Ditto.buildDittoProtocolMsg('my.test', thingId, 'things', 'twin', 'commands', 'modify', '/features', headers,
                                       value);
}

The test can be adapted to:

@Test
public void incomingTextPayloadMapping() throws IOException {
    final Resource incomingMappingFunction = new Resource("incomingScript.js");
    final PayloadMappingFunction underTest = PayloadMappingFunction.fromJavaScript(incomingMappingFunction.getContent());

    final Map<String, String> headers = new HashMap<>();
    headers.put("content-type", ContentTypes.APPLICATION_JSON.toString());
    headers.put("device_id", "the-thing-id");

    final ExternalMessage message = ExternalMessageFactory.newExternalMessageBuilder(headers)
            .withText("{\"thingId\":\"my.test.thing\",\"temp\":25.6,\"alt\":11}")
            .build();

    final Resource expectedAdaptableJsonResource = new Resource("expectedAdaptable.json");
    final JsonObject expectedAdaptableJson = JsonFactory.newObject(expectedAdaptableJsonResource.getContent());
    final Adaptable expectedAdaptable = ProtocolFactory
            .jsonifiableAdaptableFromJson(expectedAdaptableJson)
            .setDittoHeaders(DittoHeaders.of(headers));

    PayloadMappingTestCase.assertThat(message)
            .mappedByJavascriptPayloadMappingFunction(underTest)
            .isEqualTo(expectedAdaptable)
            .verify();
}
Yannic Bürgmann
  • 6,301
  • 5
  • 43
  • 77
  • Yes, our octopus sending is correct. It is sending text payload . – BobOntario Apr 01 '19 at 13:24
  • If it's sending text payload you should adapt your payload mapping script in order to use the textPayload instead of byte payload. I'll update my answer – Yannic Bürgmann Apr 01 '19 at 13:28
  • not yet. Still have the same problem: "The message mapper configuration failed due to: unterminated regular expression literal (incomingScript#1) - in line/column #1/472," -- ""incomingScript": "function mapToDittoProtocolMsg(headers, textPayload, bytePayload, contentType) {var jsonData = JSON.parse(textPayload);const thingId = jsonData.thingId;const value = {temp_sensor: { properties: { value: jsonData.temp } }, altitude: { properties: { value: jsonData.alt } } }; return Ditto.buildDittoProtocolMsg('my.test', thingId, 'things', 'twin', 'commands', 'modify', '/features', headers, value); }" – BobOntario Apr 02 '19 at 01:27
  • Problem in the between 'twin' and 'commands'. can you send me a good curl command of createConnection + mapping ? many thanks – BobOntario Apr 02 '19 at 01:32
  • If i used Postman to post message, partially works. Publish from webapp is successfully. But receiving data still have problem. we need some time to try and will post you updates. – BobOntario Apr 02 '19 at 02:41