Getting Started with OPC UA In Docker
Simulating OPC environment in Docker
In this article, we will explore Open Platform Communication (OPC) used in industrial automation using Python.
We will learn how to set up a mock server to generate 2 server nodes, pressure & temperature.
Setup
Let’s assume we have PLC sensors that send data to the OPC server and we wanted to grab the data for analysis using a data visualization tool. The end goal of the architecture will be shown below:
Prerequisite
- Python
- Docker
- OPC UA
Im using Docker Windows for this setup.
FreeOpcUa with docker-compose
services:
data_generator:
build:
dockerfile: ./container/datagenerator/Dockerfile
container_name: opc_data_generator
restart: on-failure
ports:
- "4840:4840"
Here we define a service that builds the docker file and named the container opc_data_generator. Next, the service will be restarted automatically on failure and map the host machine port with 4840 with container port 4840.
Create Python Application
The code below is from FreeOpcUa/opcua-asyncio examples in git. We are using examples/server-minimal.py as an example with some modifications.
import asyncio
import logging
import random
from asyncua import Server, ua
async def main():
_logger = logging.getLogger("asyncua")
# setup our server
server = Server()
await server.init()
server.set_endpoint("opc.tcp://0.0.0.0:4840/freeopcua/server/")
# setup our own namespace, not really necessary but should as spec
uri = "http://examples.freeopcua.github.io"
idx = await server.register_namespace(uri)
ns = "ns=2;s=freeopcua.Tags.pressure"
ns2 = "ns=2;s=freeopcua.Tags.temperature"
min_val = -0.5
max_val = 0.6
# populating our address space
# server.nodes, contains links to very common nodes like objects and root
myobj = await server.nodes.objects.add_object(idx, "MyObject")
pressure = await myobj.add_variable(ns, "MyVariable", 10.5)
temperature = await myobj.add_variable(ns2, "MyVariable", 26.7)
# Set MyVariable to be writable by clients
await pressure.set_writable()
await temperature.set_writable()
opcs = [ pressure, temperature ]
_logger.info("Starting server!")
async with server:
while True:
await asyncio.sleep(1)
random_counter = random.uniform(min_val, max_val)
for opc in opcs:
new_val = await opc.get_value() + random_counter
if(new_val>100.0):
new_val=100.0
elif(new_val<0.0):
new_val=0.0
_logger.info("Set value of %s to %.1f", opc, new_val)
await opc.write_value(new_val)
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
asyncio.run(main(), debug=True)
Create Dockerfile
Based on the above directory, we will create Dockerfile as per below:
FROM python:3.7-slim
RUN pip install \
asyncua==1.0.2
WORKDIR /opt/opc_mockup/datagenerator
COPY ../../datagenerator /opt/opc_mockup/datagenerator
CMD [ "python" ,"./opc_data_generator.py"]
We are using images from Python 3.7 slim version to reduce the size of our Docker image. Next line we install FreeOpcUa using pip to be able to use their library.
Lastly, we will run our Python file by setting the working directory and copying it to that directory.
Start OPC Server using Docker Compose
docker compose up --build -d
We can check our application logs in Docker Desktop
This is only the beginning part of the project. The next part will include the data streaming using kafka.
References link
Tap the 👏 button if you found this article useful!