OPC Server and modbus

2021-08-19  本文已影响0人  gaoshine

OPC Server and modbus

近期做一个企业内的危险源数据上传的项目,除了需要从传感器采集数据还有从modbus协议的设备采集数据,偶尔也有需要从OPC server取数的需求.

1.OPC Server

简单来说,OPC是一套标准,其目的是把PLC特定的协议(如Modbus,Profibus等)抽象成为标准化的接口,作为“中间人”的角色把通用的OPC“读写”请求转换成具体的设备协议来与HMI/SCADA系统直接对接 基于这个标准.

我们采用python的opcua的库构建自己的 opc server.

from opcua import ua, Server
# 设置我们的opc服务器
server = Server()
server.set_endpoint("opc.tcp://0.0.0.0:4840/freeopcua/server/")

# 设置我们的命名空间(namespace), 当然也可以不用设置
uri = "http://ai.kingstars.cn"
idx = server.register_namespace(uri)

# 获取对象节点(node)
objects = server.get_objects_node()

# 定义好我们的地址空间
myobj = objects.add_object(idx, "MyObject")
myvar = myobj.add_variable(idx, "MyVariable", 6.7)
myvar.set_writable()    # 设置 MyVariable 可写

# 启动服务!
server.start()

下一步,我们模拟一个不断变化的数值

try:
    count = 0
    while True:
        time.sleep(1)
        count += 0.1
        myvar.set_value(count)
finally:
    #close connection, remove subcsriptions, etc
    server.stop()

这样一个OPC server就构建完成了.

2. opc to modbus and modbus tcp server

下一步构建一个读取OPC服务的 client,并且以 modbus tcp server把 opc的数值发布出去,从而实现OPC2modbus的功能.

from pymodbus.server.asynchronous import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer
from opcua import Client

# 配置 Modbus server
# Host address of the OPC Server
opc_host = "opc.tcp://localhost:4840/freeopcua/server/"

# Modbus Server的bind ip及端口
modbus_host = "0.0.0.0"
modbus_port = 5020

# Modbus server 信息标识
identity = ModbusDeviceIdentification()
identity.VendorName = 'kingstars'
identity.ProductCode = 'WEIOT'
identity.VendorUrl = 'http://ai.kingstars.cn'
identity.ProductName = 'OPC to Modbus'
identity.ModelName = 'O2M0.1'
identity.MajorMinorRevision = '0.0.1'

# 更新频率
update_inteval = 1.5

下一步更新 opc_clinet及对应的函数

opc_client: Client


def initiate_client():
    global opc_client
    opc_client = Client(opc_host)
    opc_client.connect()
    return opc_client


def shutdown_client():
    global opc_client
    opc_client.disconnect()


def updating_writer(context):

    root = opc_client.get_root_node()

    # 从 OPC Client 获取数据
    myvar = root.get_child(["0:Objects", "2:MyObject", "2:MyVariable"])
    
    # 更新 Modbus 数据
    register = 3
    slave_id = 0x00  # 从站id
    address = 0x00
    value = myvar.get_value()
    context[slave_id].setValues(register, address, [int(value)])


def initiate_server(identity, inteval, host, port):
    # 初始化 modbus server及初始数据 
    store = ModbusSlaveContext(
        di=ModbusSequentialDataBlock.create(),
        co=ModbusSequentialDataBlock.create(),
        hr=ModbusSequentialDataBlock.create(),
        ir=ModbusSequentialDataBlock.create(),)

    context = ModbusServerContext(slaves=store, single=True)

    # 开始循环及回调
    loop = LoopingCall(f=updating_writer, context=context)
    loop.start(inteval, now=False)

    # 启动 Modbus server
    print("Modbus Server started. Press Cntl + C to stop...")
    StartTcpServer(context, identity=identity, address=(host, port))

以上代码具备OPC client,负责从opc server获取数据,然后启动 modbus server.
外部设备通过modbus tcp即可获取opc server数据.

主代码如下:

if __name__ == "__main__":
    try:
        print("Initializing...")
        print("Starting OPC Client.")
        initiate_client()
        print(f"Client listening at: {opc_host}")
        print("-"*70 + "\n")
        print("Starting Modbus server.")
        print(f"Server hosted at: {modbus_host}:{modbus_port}")
        initiate_server(identity, update_inteval, modbus_host, modbus_port)
    finally:
        print("Sutting down...")
        shutdown_client()
        

3. 测试

上一篇 下一篇

猜你喜欢

热点阅读