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
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 :
Create an Application Key for your Connector (remember to increase the expiration date - to make it simple I bind it to Administrator)
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 ...
View full tip