Configuring SCADA Integration Without a Native Plugin

Mark Townsend5 min read
Application NoteHMI / SCADAOther Manufacturer
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

You see integration friction rather than a panel fault: your Python utility reads SCADA data files, publishes BACnet values through a Modbus slave, and routes commands back through configured channels. The first check is whether the deployed SCADA version exposes a supported API or protocol interface; replacing Python or building a native plugin before checking that wastes time.

Stop relying on file access as the integration contract

Reading local .dat files can be fast and useful for a controlled utility, but it couples your program to the SCADA application's internal storage format. A successful read today does not guarantee the file layout or meaning will remain stable across versions. The discussion specifically notes that the database model changes between versions 5 and 6.

  • Parsing local XML: It can expose configuration properties such as channel names, communication lines, objects, and units, but it remains a dependency on local configuration structure rather than a formal integration interface.
  • Exporting XML or DAT into an RDBMS: This can serve reporting or downstream data needs, but it creates an export and synchronization task; it does not by itself provide a live command path.
  • Embedding Python inside C#: This adds a Python installation and runtime dependency to the SCADA host. It is usually more operationally complex than keeping Python as a separate process.

Keep the file-based approach only when you control the SCADA version, can test every upgrade, and can tolerate maintaining the parser. Do not treat a locally readable file as a stable public API.

Separate data exchange from driver execution

There are two distinct jobs: ingesting field-device data and exchanging values or commands with SCADA. A native driver is part of the SCADA runtime; an external integration service communicates across a defined interface. Mixing those jobs leads to unnecessary plugin work when the platform already has a usable protocol or API boundary.

In the described platform, Communicator and Server load drivers and modules as .NET applications, so native extensions must use a .NET language. That does not mean the BACnet logic itself must be rewritten in C#: a separate Python service can own BACnet communication and exchange values with SCADA through an interface both sides support.

The current Modbus bridge is a workable pattern: Python reads BACnet and publishes values to a Modbus slave, then the SCADA communicator reads that slave. For commands, SCADA writes designated channels and Python interprets those values before issuing device commands. Treat each mapped value as an explicit contract: document its meaning, data type, valid range, update direction, and behavior when communications fail.

Choose the narrowest supported interface

Option Use it for Trade-off
Existing Modbus bridge Exchanging mapped values when SCADA already reads the Modbus slave Adds a server and tag mapping, but keeps Python outside the SCADA runtime
KpHttpNotif.dll Issuing HTTP GET or POST requests on command Useful for command-triggered requests; confirm it fits the required data flow and response handling
REST API or gRPC A defined application-to-application integration Use only where the deployed SCADA side actually exposes or supports the required endpoint
Native .NET driver or module Integration that must run inside Communicator or Server Requires .NET development and alignment with the host application's extension model

For a continuously updated BACnet data path, retain the Modbus arrangement if it is reliable and the added mapping is manageable. For command-triggered HTTP exchanges, inspect the HTTP notification driver. Do not assume that a REST or gRPC recommendation means the installed SCADA version already provides an API server.

Build and test the bridge in a controlled sequence

  1. Record the SCADA product version and identify the supported interfaces in that installation. Confirm whether the communicator can read the chosen protocol and whether an HTTP or application API endpoint exists.
  2. Define the tag map before coding: identify each measurement and command, its type and units, its owner, and which direction it travels. Resolve duplicate ownership so the SCADA client and Python service do not compete to write the same value.
  3. Test BACnet acquisition independently. Verify that the Python process receives the expected device values before introducing SCADA mapping.
  4. Test the Modbus or HTTP handoff independently. Confirm that SCADA reads changing values and that a command written by SCADA reaches Python with the intended value.
  5. Connect the full path, then test loss and restoration of each communication segment. Specify what the application does with stale values and whether it blocks, retries, or rejects commands during an outage.
  6. Package the utility with its runtime dependencies and configuration. Keep protocol credentials and endpoint settings out of hard-coded source where practical.

Verify version compatibility and command behavior

Verify the complete path in both directions, not just a successful connection. Change a known BACnet value and confirm the corresponding SCADA point updates; issue a controlled SCADA command and confirm the Python service sees it and transmits the intended device action. Check units, scaling, and command polarity at each boundary.

For relational data such as channel names, communication lines, objects, and units, determine whether the installed version exposes a supported query or export mechanism. If you must parse XML or DAT, compare the configuration and database structures across the versions you intend to support. Version 5 and version 6 are specifically noted as having a changed database model, so do not reuse assumptions across that boundary without validation.

An OPC UA server was described as planned for version 6, not as a confirmed capability of every installation. Check the actual release and configuration before designing around it. Likewise, validate that an HTTP notification mechanism handles the required request/response pattern; an HTTP GET/POST action on command is not automatically a general-purpose streaming API.

FAQ: SCADA integration choices

How do I connect a Python BACnet program to SCADA?

Keep Python as a separate service and use an interface the installed SCADA version supports. The described arrangement publishes BACnet values through a Modbus slave and has the SCADA communicator read them.

How do I send commands from SCADA to Python?

Map SCADA command values to designated channels or protocol points, then have Python read and validate those values before issuing BACnet commands. Test the command path separately from measurement updates.

How do I read SCADA channel names and units?

Check for a supported query or export interface in the installed version. XML parsing is a possible fallback, but validate the structure across upgrades because the version 5-to-6 database model changed.

Stop and escalate to the SCADA product's official support channel when the installed version's interface availability or driver contract is unclear, or when a version change breaks file parsing or mapping. Provide the product version, the failing interface, and a minimal read/write test so support can identify the supported integration path.

Back to blog