Connecting OPC Servers Using Python

Burger Wu
6 min readNov 22, 2022

OPC (Open Platform Communication) is the interoperability standard for the secure and reliable exchange of data in the industrial automation space and in other industries(From OPC Foundation). It was first built on the demand of integrating complex vendor specific protocol(ex: Modbus, Profibus and etc) into a standard one.

OPC was once called OLE (Object Linking and Embedding) for process control, and it highly depends on Microsoft’s OLE, COM(Component Object Model) and DCOM(Distributed Component Object Model) technlogy. This early development of OPC was also called OPC classic with three major division: OPC DA(Data Access), AE(Alarms and Events) and HDA(Historical Data Access).

However, OPC classic comes with many intrinsic problems:

  1. Depend on Microsoft technology, making it platform restricted. Also, the support from Microsoft seems to be weakening.
  2. COM and DCOM technology is free to use any port between 1024 to 65525, and server abd client will negotiate dynamic ports after initial contact. This makes OPC application firewall-unfriendly, and apparently it’s against the trend of cybersecurity.
  3. Configuration of COM/DCOM could be really troublesome.

That’s when OPC UA(Unified Architecture) comes into play! OPC UA was released in 2008, it integrated the functionality and specifications of OPC Classic into an extensible framework (From OPC Foundation). OPC UA not only works as reliable as OPC DA does, it also eliminates the platform restriction, comes with stronger authentication and encryption mechanism. What is the most important is, it saves time on configuration because OPC UA no longer relies on COM/DCOM.

Some more details about OPC can be found below:

  1. Wikipedia-OPC
  2. OPC Classic Specifications-OPC Foundation
  3. OPC UA Refrence-OPC Foundation
Different communication intermediate of OPC DA and UA. Source:https://www.opc-router.com/what-is-opc-ua/

Alright, time to get back to our topic. Though there are a lot of state-of-the-art software for OPC connections. Some independent developer or company may still have the need to apply one of the most popular programming language, Python for opc connection. In the rest of this article, I am going to show you how to implement OPC connection using Python package and code.

Connecting OPC DA Servers

In order for Python connectors to work on OPC DA servers, it is recommended that you use 32bit Python. If you are using Jupyter Notebook, you may add a 32 bit Python kernel. Here is another article about creating new kernels with desired version of Python.

Matrikon OPC Server for Simulation

In this project, we use free OPC simulation software provided by Matrikon as OPC data source. You may download the software here.

OpenOPC-Python3X

The main package for OPC DA connection in Python is OpenOPC. You may install it using pip install OpenOPC-Python3X for Python3. Other requried packages may include pywin32 and Pyro4.

Sample Code for Making Connection

# Import packages
import OpenOPC
import pywintypes

# Create OPC DA client object
pywintypes.datetime = pywintypes.TimeType
opc = OpenOPC.client()

# Search for OPC DA servers
servers = opc.servers()
servers

# Connection to OPC DA Server
opc.connect('Matrikon.OPC.Simulation.1')
aliases = opc.list()

# Use list() to explore available items
groups = opc.list('Simulation Items.Random')
groups
  1. The first step is to initiate a OPC client object by using OpenOPC.client().
  2. You can then search for available servers using server() method of OPC client object
  3. Choose servers to connect to and apply connect() method
  4. By using list() method, you can explore the hierachy of OPC DA Server.

Sample Code for Reading and Writing Values

One of the most important feature of OPC DA is collecting real-time data. Thus, we demonstrate sample code that reads and writes value to OPC DA server from Python OPC client.

# Read() method return a tuple with value, quality and timestamp of that tag
opc.read(‘Random.Int32’)

# Locate desired variable and apply write method to write value
opc.write(('Bucket Brigade.Int4',5))

# Define tag group list and write value tuple
tag_group = ['Bucket Brigade.Int4','Bucket Brigade.Int2']
tag_group_write = [('Bucket Brigade.Int4',10),('Bucket Brigade.Int2',8)]

# Pass on tag group to read and write method
opc.write(tag_group_write)
opc.read(tag_group)

By using read() and write () method, you are able to read value from and write value back(as long as the tag is writable) to OPC DA server. Besides, you can also use list of tag strings to apply read and write method to multiple tags.

You can find the sample Jupyter Notebook in this Github Repo with matplotlib real-time plotting example.

Things You May Need to Do If OpenOPC Doesn’t Work Well

Setting up Python connection for OPC DA may not always as smooth as expected. Here are some hints you may try to make OpenOPC work.

Some common error you may meet are

  • NameError: name ‘pythoncom’ is not defined
  • OPCError: Dispatch: Invalid class string”
  • Program crash during reading value error.

When you encounter these problems, there are some suggested potential solutions you may try out.

  1. Updating pywin32 module and then run postinstall script. Some solutions suggest you install through here instead of standard pip install. Then locate to Scripts folder under Python installation directory and open up command line to run below command.
python pywin32_postinstall -install 

2. Registering OPC DA auto wrapper.

  • Download the dll file from this website
  • Put the file in C:\Windows\System32 and start a administrative cmd
  • Run regsvr32 gbda_aut.dll in cmd

Here are some posts for reference to these solutions

DLL for opc communication crashes for python3.5+ on windows

NameError: name ‘pythoncom’ is not defined #8

invalid class string

Connecting OPC UA Servers

Connection of OPC UA server is a lot easier than OPC DA server. That is also another reason for the emergence of OPC UA. A schematic illustration of OPC UA address space is shown below.

Prosys OPC UA Simulation Server

In this part of experiment, we use Prosys OPC UA simulation as data source. You can download free version of the software here.

OPCUA Package

Basically, running pip install opcua is all you need for the connection to work.

pip install opcua

Sample Code for Making Connection

#First, import the opcua package
from opcua import ua,Client
import socket

#Find out the connection string of OPC UA Server and initiate client object, replace {} with OPC UA Server hostname
url =”opc.tcp://{}.localdomain:53530/OPCUA/SimulationServer”.format(socket.gethostname())
client = Client(url)

#Connection to the OPC UA Server
client.connect()

Simple paste OPC UA connection string to a client object and apply connection method can initiate the connection.

Sample Code for Exploring Hierachy

# Call get_root_node method to locate node at the top of hierachy
root_node = client.get_root_node()

# Use get_children to drill down the hierachy, get_browse_name method allows you to view node browse name instead of nodeID
[ x.get_browse_name() for x in root_node.get_children()]

# Locate object node where our variables are stored
objects = root_node.get_children()[0]

# Or you can use get_objects_node to directly locate objects node
objects = client.get_objects_node()

#Browse for the children nodes of the objects node
[ x.get_browse_name() for x in objects.get_children()]

# Use get_children method to explore children of the object node
# Use index to choose children node of desire
sim_data_node = objects.get_children()[2]

#Apply get_browse_name to get node name instead of node id
[ {"NodeID":x ,"Node Name": x.get_browse_name()} for x in sim_data_node.get_children()]

You can use get_children() method to drill down the hierachy. The method will return available children in a list for so that you can index the target node. Get_browse_name() method allows you to read the browse name instead of node id.

If you already know the target nodeId, you may use get_node() method instead to directly reach that node.

# You may specify NodeId using NodeId object or string
# The two method below reaches the same node
from opcua.ua import NodeId
client.get_node(NodeId(1004,3)).get_browse_name()

# Get to specific node by specifying node index using get_node method
client.get_node('ns=3;i=1004').get_browse_name()

Sample Code for Reading and Writing Values

#Index desired variable and apply get_value method to read value
sim_data_node_sin = client.get_node('ns=3;i=1004')
sim_data_node_sin.get_value()

# Index desired variable and apply set_value method to write value
sim_data_node_test = client.get_node('ns=3;i=1007')
sim_data_node_test.set_value(4.0)

The concept is really straightforward. Get_value() method allows you to read value from node whereas set_value() method allows you to write value back to OPC UA server.

You can find the sample Jupyter Notebook in this Github Repo with matplotlib real-time plotting example.

Conclusion

In this article, we demonstrate basic functions of OpenOPC-Python3X and opcua so that users can create connections to OPC server no matter what their application is for. Real-time plotting from the repo of this project proves the reliability and feasibility of the connection with OPC server. However, the setting of OpenOPC to OPC DA server is relatively troublesome and requires much more trial and error. Based on this result, it is highly recommended that you go for OPC UA if possible since OPC UA is considered to be the standard connection specification of state-of-the-art industrial automation.

--

--

Burger Wu

Data Science Enthusiast from Taiwan, especially interested in application in Energy Industry & Industry Automation