为了进一步研究单功能块应用,我编写了一个演示程序。在这个演示中,包含了两个单功能应用
ECycle_1 定时产生一个输出事件EO,触发FunctionBlock 的输入事件EI。触发内部的算法EI_Algorithem。 两个功能块通过Opcua 的Client/Server 协议交换数据和事件。
import sys
sys.path.insert(0, "..")
import time
from opcua import ua, Server
from ThreadClient import ThreadClient
class InputEventHandler(object):
def datachange_notification(self, node, val, data):
EventName=node.get_browse_name().Name
print(EventName+" Trigged")
if EventName=="EI":
self.EI_Algorithm()
def EI_Algorithm(self):
print("EI_Algorithm in pocessing")
if __name__ == "__main__":
# setup Aggregation server
server = Server()
server.set_endpoint("opc.tcp://localhost:48400/freeopcua/server/")
server.application_uri = "urn:freeopcua:python:server"
#server.import_xml("OpcUaModbus.NodeSet2.xml")
# Create aggregation information model
uri = "http://www.maxim.org/OpcUaAggregation/"
idx = server.register_namespace(uri)
objects = server.get_objects_node()
#Create
FunctionBlock=objects.add_folder(idx, "ECycle_1")
InputEvents=FunctionBlock.add_folder(idx,"InputEvents")
OutputEvents=FunctionBlock.add_folder(idx,"OutputEvents")
InputVars=FunctionBlock.add_folder(idx,"InputVars")
OutputVars=FunctionBlock.add_folder(idx,"OutputVars")
Event_EI=InputEvents.add_variable(idx,"EI",ua.Variant(False, ua.VariantType.Boolean))
Event_EO=OutputEvents.add_variable(idx,"EO",ua.Variant(False, ua.VariantType.Boolean))
server.start()
handler = InputEventHandler()
sub = server.create_subscription(100, handler)
sub.subscribe_data_change(Event_EI)
ThreadClient=ThreadClient("Client")
ThreadClient.start()
# main Loop
try:
Event_state = True
while True:
time.sleep(5)
Event_state=not Event_state
Event_EO.set_data_value(Event_state)
ThreadClient.Notify_Event("2:EI",Event_state)
finally:
#close connection, remove subcsriptions, etc
server.stop()
ThreadClient.stop()
import threading
from opcua import ua , Client
import time
#from Common import get_variables,get_Node_Path,get_Node_By_Name
class ThreadClient(threading.Thread):
def __init__(self,Name):
threading.Thread.__init__(self)
self._stopper = threading.Event()
self.Name=Name
self.client = Client("opc.tcp://localhost:48410/freeopcua/server/")
def stop(self):
self._stopper.set()
def stopped(self):
return self._stopper.isSet()
def Notify_Event(self,TargetEvent,val):
Path=["0:Objects","2:FunctionBlock","2:InputEvents",TargetEvent]
#print(Path)
root = self.client.get_root_node()
node=root.get_child(Path)
#print(node)
self.client.set_values([node],[val])
def run(self):
#Start
try:
self.client.connect()
root = self.client.get_root_node()
print("Objects node is: ", root)
while True:
#print("Client")
time.sleep(2)
finally:
self.client.disconnection()
import sys
sys.path.insert(0, "..")
import time
from opcua import ua, Server
from ThreadClient import ThreadClient
class InputEventHandler(object):
def datachange_notification(self, node, val, data):
EventName=node.get_browse_name().Name
print(EventName+" Trigged")
if EventName=="EI":
self.EI_Algorithm()
def EI_Algorithm(self):
print("EI_Algorithm in pocessing")
if __name__ == "__main__":
# setup Aggregation server
server = Server()
server.set_endpoint("opc.tcp://localhost:48410/freeopcua/server/")
server.application_uri = "urn:freeopcua:python:server"
#server.import_xml("OpcUaModbus.NodeSet2.xml")
# Create aggregation information model
uri = "http://www.maxim.org/OpcUaAggregation/"
idx = server.register_namespace(uri)
objects = server.get_objects_node()
#Create
FunctionBlock=objects.add_folder(idx, "FunctionBlock")
InputEvents=FunctionBlock.add_folder(idx,"InputEvents")
OutputEvents=FunctionBlock.add_folder(idx,"OutputEvents")
InputVars=FunctionBlock.add_folder(idx,"InputVars")
OutputVars=FunctionBlock.add_folder(idx,"OutputVars")
Event_EI=InputEvents.add_variable(idx,"EI",ua.Variant(False, ua.VariantType.Boolean))
Event_EO=OutputEvents.add_variable(idx,"EO",ua.Variant(False, ua.VariantType.Boolean))
Event_EI.set_writable()
Event_EO.set_writable()
server.start()
handler = InputEventHandler()
sub = server.create_subscription(100, handler)
sub.subscribe_data_change(Event_EI)
#ThreadClient=ThreadClient("Client")
#ThreadClient.start()
# main Loop
try:
Event_state = True
while True:
time.sleep(5)
#Event_state=not Event_state
#Event_EO.set_data_value(Event_state)
#ThreadClient.Notify_Event("EO",Event_state)
finally:
#close connection, remove subcsriptions, etc
server.stop()
#ThreadClient.stop()
上面的例子仅仅是一个演示程序,从它可以看出一个单一功能块如何在没有Runtime 的情形下独立运行。在云端,能够将该应用程序在Docker 中运行。由于不需要runtime,使编程变得异常简单。可以使用Golang,C#,C++,javascript 等各种语言简单实现。
进一步地完善包括
1 每个单功能块需要有一个JSON 格式的Configuration 配置文档。
2 当功能块网络生成时,要将EventConnection 和DataConnection 的信息添加到信息模型中,比较好的方法是将OutputEvent 中包含Target URI 信息,Client 能够将事件输出注入目标功能块。OutputData 是通过目标功能块的Client 来读取数据的。也可以采取源功能块注入的方法。
3 进一步设计多种语言的单应用程序的标准模板