cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Showing results for 
Search instead for 
Did you mean: 

Protocol Adapter Toolkit - MQTT Sample Project hands-on (1.1.x)

Highlighted
Level 9

Protocol Adapter Toolkit - MQTT Sample Project hands-on (1.1.x)

The Protocol Adapter Toolkit (PAT) is an SDK that allows developers to write a custom Connector that enables edge devices to connect to and communicate with the ThingWorx Platform.

 

In this blog, I will be dabbling with the MQTT Sample Project that uses the MQTT Channel introduced in PAT 1.1.2.

 

Preamble

  • All the PAT sample projects are documented in detail in their respective README.md.
    This post is an illustrated Walk-thru for the MQTT Sample project, please review its README.md for in depth information.
  • More reading in Protocol Adapter Toolkit (PAT) overview
  • PAT 1.1.2 is supported with ThingWorx Platform 8.0 and 8.1 - not fully supported with 8.2 yet.

 

MQTT Connector features

The MQTT Sample project provides a Codec implementation that service MQTT requests and a command line MQTT client to test the Connector.

  • The sample MQTT Codec handles Edge initiated requests
    • read a property from the ThingWorx Platform
    • write a property to the ThingWorx Platform
    • execute a service on the ThingWorx Platform
    • send an event to the ThingWorx Platform (also uses a ServiceEntityNameMapper to map an edgeId to an entityName)

All these actions require a security token that will be validated by a Platform service via a InvokeServiceAuthenticator.

 

  • This Codec also handles Platform initiated requests (egress message)
    • write a property to the Edge device
    • execute a service without response on the Edge device

 Components and Terminology

 

 

 

  • Mqtt Messages originated from the Edge Device (inbound) are published to the sample MQTT topic
  • Mqtt Messages originated from the Connector (outbound) are published to the mqtt/outbound MQTT topic

 

Codec

A pluggable component that interprets Edge Messages and converts them to ThingWorx Platform Messages to enable interoperability between an Edge Device and the ThingWorx Platform.

Connector

A running instance of a Protocol Adapter created using the Protocol Adapter Toolkit.

Edge Device

The device that exists external to the Connector which may produce and/or consume Edge Messages.

(mqtt) Edge Message

A data structure used for communication defined by the Edge Protocol.  An Edge Message may include routing information for the Channel and a payload for Codec. Edge Messages originate from the Edge Device (inbound) as well as the Codec (outbound).

(mqtt) Channel

The specific mechanism used to transmit Edge Messages to and from Edge Devices. The Protocol Adapter Toolkit currently includes support for HTTP, WebSocket, MQTT, and custom Channels. A Channel takes the data off of the network and produces an Edge Message for consumption by the Codec and takes Edge Messages produced by the Codec and places the message payload data back onto the network.

Platform Connection The connection layer between a Connector and ThingWorx core

Platform Message

The abstract representation of a message destined for and coming from the ThingWorx Platform (e.g. WriteProperty, InvokeService). Platform Messages are produced by the Codec for incoming messages and provided to the Codec for outgoing messages/responses.

 

Installation and Build

 Protocol Adapter Toolkit installation

  • The media is available from PTC Software Downloads : ThingWorx Connection Server > Release 8.2 > ThingWorx Protocol Adapter Toolkit
  • Just unzip the media on your filesystem, there is no installer
  • The MQTT Sample Project is available in <protocol-adapter-toolkit>\samples\mqtt

Eclipse Project setup

  • Prerequisite :
    • Eclipse IDE (I'm using Neon.3 release)
    • Eclipse Gradle extension - for example the Gradle IDE Pack available in the Eclipse Marketplace
  • Import the MQTT Project :
    • File > Import > Gradle (STS) > Gradle (STS) Project
    • Browser to <protocol-adapter-toolkit>\samples\mqtt, then [Build Model] and select the mqtt project

 

gardle_import.png

 

Review the sample MQTT codec and test client

  • Connector : mqtt > src/main/java > com.thingworx.connector.sdk.samples.codec.MqttSampleCodec
    • decode : converts an MqttEdgeMessage to a PlatformRequest
    • encode (3 flavors) : converts a PlatformMessage or an InfoTable or a Throwable to a MqttEdgeMessage
    • Note that most of the conversion logic is common to all sample projects (websocket, rest, mqtt) and is done in an helper class : SampleProtocol
      • The SampleProtocol sources are available in the <protocol-adapter-toolkit>\samples\connector-api-sample-protocol project - it can be imported in eclipse the same way as the mqtt.
      • SampleTokenAuthenticator and SampleEntityNameMapper are also defined in the <protocol-adapter-toolkit>\samples\connector-api-sample-protocol project.

  • Client : mqtt > src/client/java > com.thingworx.connector.sdk.samples.MqttClient
    • Command Line MQTT client based on Eclipse Paho that allows to test edge initiated and platform initiated requests.

Build the sample MQTT Connector and test client

  • Select the mqtt project then RMB > Gradle (STS) > Task Quick Launcher > type Clean build +  [enter]
    • This creates a distributable archive (zip+tar) in <protocol-adapter-toolkit>\samples\mqtt\build\distributions that packages the sample mqtt connector, some startup scripts, an xml with sample entities to import on the platform and a sample connector.conf.
    • Note that I will test the connector and the client directly from Eclipse, and will not use this package.

Runtime configuration and setup

MQTT broker

  • I'm just using a Mosquitto broker Docker image from Docker Hub

 

docker run -d -p 1883:1883 --name mqtt ncarlier/mqtt

 ThingWorx Platform appKey and ConnectionServicesExtension

  • From the ThingWorx Composer :
    1. Create an Application Key for your Connector (remember to increase the expiration date - to make it simple I bind it to Administrator)
    2. Import the ConnectionServicesExtension-x.y.z.zip and pat-extension-x.y.z.zip extensions available in <protocol-adapter-toolkit>\requiredExtensions

 Connector configuration

  • Edit <protocol-adapter-toolkit>\samples\mqtt\src\main\dist\connector.conf
  • Update the highlighted entries below to match your configuration :

 

include "application"

cx-server {

  connector {

    active-channel = "mqtt"

    bind-on-first-communication = true

    channel.mqtt {

      broker-urls = [ "tcp://localhost:1883" ]

      // at least one subscription must be defined

      subscriptions {

       "sample": [ "com.thingworx.connector.sdk.samples.codec.MqttSampleCodec", 1 ]

      }

      outbound-codec-class = "com.thingworx.connector.sdk.samples.codec.MqttSampleCodec"

    }

  }

  transport.websockets {

    app-key = "00000000-0000-0000-0000-000000000000"

    platforms = "wss://thingWorxServer:8443/Thingworx/WS"

  }

  // Health check service default port (9009) was in used on my machine. Added the following block to change it.

  health-check {

     port = 9010

  }

}

 Start the Connector

  • Run the Connector directly from Eclipse using the Gradle Task
    • RMB > Run As ... > Gradle (STS) Build
  • (Alternate technique)  Debug as Java Application from Eclipse
    • Select the mqtt project, then Run > Debug Configurations ....
      • Name : mqtt-connector
      • Main class:  com.thingworx.connectionserver.ConnectionServer
      • On the argument tab add a VM argument : -Dconfig.file=<protocol-adapter-toolkit>\samples\mqtt\src\main\dist\connector.conf
      • Select [Debug]

 Verify connection to the Platform

  • From the ThingWorx Composer, Monitoring > Connection Servers
    • Verify that a Connection Server with name protocol-adapter-cxserver-<uuid> is listed

 Testing

 Import the ThingWorx Platform sample Things

  • From the ThingWorx Composer
    • Import/Export > From File : <protocol-adapter-toolkit>\samples\mqtt\src\main\dist\SampleEntities.xml
    • Verify that WeatherThing, EntityNameConverter and EdgeTokenAuthenticator have been imported.
      • WeatherThing : RemoteThing that is used to test our Connector
      • EdgeTokenAuthenticator : holds a sample service (ValidateToken) used to validate the security token provided by the Edge device
      • EntityNameConverter : holds a sample service (GetEntityName) used to map an edgeId to an entityName

 Start the test MQTT client

  • I will run the test client directly from Eclipse
  • Select the mqtt project, then Run > Run Configurations ....
    • Name : mqtt-client
    • Main class:  com.thingworx.connector.sdk.samples.MqttClient
    • On the argument tab add a Program argument : tcp://<mqtt_broker_host>:1883
    • Select [Run]
    • Type the client commands in the Eclipse Console

 Test Edge initiated requests

 

 

Read a property from the ThingWorx Platform

  • In the MQTT client console enter : readProperty WeatherThing temp

 

Sending message: {"propertyName":"temp","requestId":1,"authToken":"token1234","action":"readProperty","deviceId":"WeatherThing"} to topic: sample

Received message: {"temp":56.3,"requestId":1} from topic: mqtt/outbound

  • Notes :
    • An authToken is sent with the request, it is validated by a platform service using the SampleTokenAuthenticator (this authenticator is common to all the PAT samples and is defined in <protocol-adapter-toolkit>\samples\connector-api-sample-protocol)
    • EntityNameMapper is not used by readProperty (no special reason for that)
    • The PlatformRequest message built by the codec is ReadPropertyMessage

 

Write a property to the ThingWorx Platform

  • In the MQTT client console enter : writeProperty WeatherThing temp 20

 

Sending message: {"temp":"20","propertyName":"temp","requestId":2,"authToken":"token1234","action":"writeProperty","deviceId":"WeatherThing"} to topic: sample
  • Notes :
    • An authToken is sent with the request, it is validated by a platform service using the SampleTokenAuthenticator
    • EntityNameMapper is not used by writeProperty
    • The PlatformRequest message built by the codec is WritePropertyMessage
    • No Edge message is sent back to the device

 

Send an event to the ThingWorx Platform

  •   In the MQTT client console enter : fireEvent Weather WeatherEvent SomeDescription

 

Sending message: {"requestId":5,"authToken":"token1234","action":"fireEvent","eventName":"WeatherEvent","message":"Some description","deviceId":"Weather"} to topic: sample
  • Notes :
    • An authToken is sent with the request, it is validated by a platform service using the SampleTokenAuthenticator
    • fireEvent uses a EntityNameMapper (SampleEntityNameMapper) to map the deviceId (Weather) to a Thing name (WeatherThing), the mapping is done by a platform service
    • The PlatformRequest message built by the codec is FireEventMessage
    • No Edge message is sent back to the device

 

Execute a service on the ThingWorx Platform

    • ... can be tested with the GetAverageTemperature on WeatherThing ...

Test Platform initiated requests

 

 

Write a property to the Edge device

  • The MQTT Connector must be configured to bind the Thing with the Platform when the first message is received for the Thing.
    • This was done by setting the bind-on-first-communication=true in connector.conf
    • When a Thing is bound, the remote egress messages will be forwarded to the Connector
    • The Edge initiated requests above should have done the binding, but if the Connector was restarted since, just bind again with : readProperty WeatherThing isConnected
  • From the ThingWorx composer update the temp property value on WeatherThing to 30
  • An egress message is logged in the MQTT client console :

 

Received message: {"egressMessages":[{"propertyName":"temp","propertyValue":30,"type":"PROPERTY"}]} from topic: mqtt/outbound

 

Execute a service on the ThingWorx Platform

    • ... can be tested with the SetNtpService on WeatherThing ...