Skip to main content
6-Contributor
July 6, 2026
Question

Python Edge ML solution for ThingWorx

  • July 6, 2026
  • 0 replies
  • 10 views

Running a machine-learning model right at the edge — next to the machine, without a round-trip to the cloud — is a common ask. This post shows a ready-made Python solution that reads live data from an OPC UA server, scores a PMML model locally, and pushes the predictions into ThingWorx as Thing properties. It builds on the thingworx-python wrapper, so the ThingWorx side is only a few lines of code and everything is driven by one config.json.

What it does

The service edge_inference.py wires up a small pipeline:

OPC UA (asyncua) → assemble a feature row → PMML model → ThingWorx properties

  • subscribes to / polls OPC UA nodes (e.g. temperature, pressure, humidity);
  • optionally reads extra inputs from ThingWorx properties (setpoints, limits);
  • runs the model when the trigger condition is met;
  • writes each prediction back to a ThingWorx property.

Where to get it

The Edge ML solution is on GitHub:
https://github.com/thingworx-field-work/Python-Edge-Machine-Learning

It builds on the ThingWorx Python SDK wrapper. See:

Installing the libraries

The solution needs three groups of dependencies: OPC UA, data/ML, and the ThingWorx wrapper. They are listed in requirements.txt:

# OPC UA client
asyncua

# data + PMML scoring
pandas
numpy
sklearn-pmml-model # pure-Python backend (recommended, no Java)
# pypmml # optional: full PMML 4.4, needs a Java 8+ runtime

# ThingWorx C SDK Python wrapper (installed from this repo)
# pip install -e "../ThingWorx Python wrapper"

Install them into a virtual environment:

pip install -r requirements.txt
pip install -e "../ThingWorx Python wrapper" # the thingworx package

The repo also ships helper scripts — install.ps1 / install.sh set up the venv and install everything in one step, and run.ps1 / run.sh launch the service.

Importing the libraries in Python

Inside edge_inference.py the key imports are:

import json
import pandas as pd
from asyncua import Client as OpcUaClient # OPC UA
from thingworx import ThingWorxClient, Thing, BaseType # ThingWorx wrapper

# The PMML backend (sklearn-pmml-model or pypmml) is imported internally,
# chosen by the "backend" field in config.json.

The ThingWorx wrapper loads the native C SDK (twCSdk.dll / libtwCSdk.so) lazily when the client is created — the same mechanism described in the Python SDK post. On Windows keep the OpenSSL DLLs (libcrypto-3-x64.dlllibssl-3-x64.dll) next to twCSdk.dll.

Configuring the app: config.json

You normally do not edit the Python at all — everything lives in config.json, which has four sections:

{
"thingworx": {
"host": "localhost", "port": 8016,
"app_key": "your-app-key", "thing_name": "EdgeMLThing",
"encryption": false, "self_signed_ok": true,

"input_properties": [ // read FROM ThingWorx, feed to model
{ "name": "OperatingMode", "type": "STRING",
"model_input_field": "operating_mode" }
],
"output_properties": [ // write predictions TO ThingWorx
{ "name": "ElectricalOutput", "type": "NUMBER",
"model_output_field": "ElectricalOutput", "push_type": "ALWAYS" }
]
},

"opcua": {
"server_url": "opc.tcp://your-kepware-host:49320",
"security": { "mode": "username",
"username": "OPCUAClient", "password": "secret" },
"nodes": [
{ "node_id": "ns=2;s=SIM_ML.Machine1.AmbientTemperature",
"name": "AmbientTemperature", "type": "NUMBER" }
]
},

"model": {
"pmml_file": "my_model.xml",
"backend": "auto", // auto | sklearn-pmml-model | pypmml
"trigger_on": "all", // run when all/any nodes reported
"input_mapping": { "AmbientTemperature": "AmbientTemperature" },
"output_mapping": { "ElectricalOutput": "ElectricalOutput" }
},

"logging": { "level": "INFO", "file": null }
}

The two mappings are the glue: input_mapping connects each OPC UA node name to a PMML input field, and output_mapping connects each PMML output field to a ThingWorx property. Use trigger_on: "all" to score only once every subscribed node has reported at least once, or "any" to score on every change.

Running the app

Point config.json at your OPC UA server, ThingWorx host, and PMML file, then start it:

# Windows
.\run.ps1
# or explicitly
python edge_inference.py --config config.json

# Linux / macOS
./run.sh

On start it connects to ThingWorx, binds the Thing, connects to the OPC UA server, and begins scoring. Predictions appear as properties on your Thing in Composer. Stop it with Ctrl+C.

Not sure which fields your model expects? Inspect it first — this lists the model's inputs and outputs without connecting to anything:

.\run.ps1 inspect my_model.xml         # or: ./run.sh inspect my_model.xml

Troubleshooting

  • PMML scoring fails only with pypmml — that backend needs a Java 8+ runtime. Use the default sklearn-pmml-model (pure Python) if you have no JVM.
  • Model field mismatch — run inspect and make the names in input_mapping / output_mapping match the PMML exactly.
  • OPC UA connect errors — check server_url and the security block (mode/username/password) against your server.
  • Failed to load ThingWorx C SDK library — make sure twCSdk.dll and the OpenSSL DLLs are in the run folder (the launcher sets TWCSDK_LIB_PATH for you).

Takeaways

With one config file you get a complete edge-ML loop: OPC UA in, PMML scoring on the device, ThingWorx out — no cloud round-trip and no custom transport code. Swap the pmml_file and the two mappings to deploy a different model against different tags.

Prerequisites: Python 3.11+ (for sklearn-pmml-model), a twCSdk build for your OS, a reachable OPC UA server, and a ThingWorx platform with an Application Key and a RemoteThing to bind to.