Using the content of my previous blog posts, I can use Python to write the simplest OPC UA /ModbusTCP gateway.
From this program it can be seen that:
- It is not difficult to apply OPC UA, and now we can apply it to engineering applications, even DIY projects. There is no need to use complex tool software.
- It is a good choice to use Python to build prototype programs in the field of industrial automation.
OPCUA_modbus gateway
import sys
sys.path.insert(0, "..")
import time
from opcua import Server
from pyModbusTCP.client import ModbusClient # Modbus TCP Client
from pyModbusTCP import utils
if __name__ == "__main__":
# setup our server
server = Server()
server.set_endpoint("opc.tcp://127.0.0.1:48400/freeopcua/server/")
# setup our own namespace, not really necessary but should as spec
uri = "http://examples.freeopcua.github.io"
idx = server.register_namespace(uri)
# get Objects node, this is where we should put our nodes
objects = server.get_objects_node()
# populating our address space
myobj = objects.add_object(idx, "MyObject")
myvar = myobj.add_variable(idx, "MyVariable", 6.7)
myvar.set_writable() # Set MyVariable to be writable by clients
ModbusInterface = ModbusClient(host="localhost", port=502, unit_id=1, auto_open=True, auto_close=False)
# starting!
server.start()
try:
count = 0
while True:
time.sleep(1)
reg_l=ModbusInterface.read_input_registers(0,2)
val=utils.word_list_to_long(reg_l)
print(utils.decode_ieee(val[0],False))
myvar.set_value(utils.decode_ieee(val[0],False))
finally:
#close connection, remove subcsriptions, etc
server.stop()
modbusTCP server program
import argparse
from pyModbusTCP.server import ModbusServer, DataBank
from pyModbusTCP import utils
from datetime import datetime
import numpy as np
Fs = 8000
f = 50
x=0
coil_state=True
class MyDataBank(DataBank):
"""A custom ModbusServerDataBank for override get_holding_registers method."""
def __init__(self):
# turn off allocation of memory for standard modbus object types
# only "holding registers" space will be replaced by dynamic build values.
super().__init__(virtual_mode=True)
def get_coils(self, address, number=1, srv_info=None):
global coil_state
coil_state=not coil_state
return coil_state
def get_holding_registers(self, address, number=1, srv_info=None):
"""Get virtual holding registers."""
# populate virtual registers dict with current datetime values
now = datetime.now()
return now.second
def get_input_registers(self, address, number=1, srv_info=None):
global x
wave=np.sin(2 * np.pi * f * x / Fs)*10
x=x+1
b32_l=[utils.encode_ieee(wave,False)]
b16_l = utils.long_list_to_word(b32_l)
print(b16_l)
return b16_l
if __name__ == '__main__':
# parse args
parser = argparse.ArgumentParser()
parser.add_argument('-H', '--host', type=str, default='localhost', help='Host (default: localhost)')
parser.add_argument('-p', '--port', type=int, default=502, help='TCP port (default: 502)')
args = parser.parse_args()
# init modbus server and start it
server = ModbusServer(host=args.host, port=args.port, data_bank=MyDataBank())
server.start()
Finally, access the OPCUA Server through the uaExpert program. You can see that the myVar variable is changing.
OPCUA client
import sys
sys.path.insert(0, "..")
import numpy as np
import matplotlib.pyplot as plt
from opcua import Client
x = np.arange(0,1000,1,dtype=np.int16)
y=np.arange(-10,10,0.02,dtype=np.float32)
if __name__ == "__main__":
client = Client("opc.tcp://localhost:48400/freeopcua/server/")
# client = Client("opc.tcp://admin@localhost:4840/freeopcua/server/") #connect using a user
try:
client.connect()
# Client has a few methods to get proxy to UA nodes that should always be in address space such as Root or Objects
root = client.get_root_node()
print("Objects node is: ", root)
# Node objects have methods to read and write node attributes as well as browse or populate address space
print("Children of root are: ", root.get_children())
while True:
myvar = root.get_child(["0:Objects", "2:MyObject", "2:MyVariable"])
obj = root.get_child(["0:Objects", "2:MyObject"])
print("myvar is: ", myvar.get_value())
y=np.append(y,myvar.get_value())
y=np.delete(y, 0, axis=0)
plt.clf()
plt.plot(x, y, ls="-", lw=2, label="plot figure")
plt.legend()
plt.show()
plt.pause(0.1)
# Stacked myvar access
# print("myvar is: ", root.get_children()[0].get_children()[1].get_variables()[0].get_value())
finally:
client.disconnect()
You can see that these programs are short and neat. Writing programs is the best way to learn computer network protocols.