PackML 学习笔记(2) OPCUA /PackML

        2020年11月11日,OPC 基金会发布了PackML 的配套规范(OPC 30050: PackML - Packaging Control)。意味着可以使用OPCUA 信息模型来构建PackML 模型了。

   如果写一篇技术简介往往是简单的,要去实现这门技术却很难。首先,OPC UA 的本文涉及的内容很多,而且不容易读懂。工业标准与IT 技术不同,网络上难以找到模板和编程技巧的介绍。多数情况下智能靠自己琢磨。也许个人的读书笔记对其他人是有帮助的。

PackML 的应用

     尽管PackML 最初是为包装行业制定的标准,但是目前已经远远地超出了PackML 的范畴,它俨然成为机器的通用接口标准:基于PackML 的应用系统如下图所示

  •  规范了HMI 操作界面
  • 利用PackTag 实现机器之间的通信
  • 利用PackTag 实现IT/OT 之间的通信 

当使用OPCUA 构建 PackML 模型之后,能够使用OPC UA 的通信机制实现PackML。

       应该指出,无论是PackML ,还是OPCUA 。它们都只是一种模型描述方法。它们规范了程序界面的标准化。具体的实现是需要PLC 或者机器控制器内部程序支持的。PLC 厂商提供了IEC61131-3 功能块库支持PackML状态的演变。作为工业软件的开发者,我们需要研究如何编写类似的功能块或者软件。

基于OPCUA /PackML ,我们可以开发:

  • 标准化的IT 远程操作面板
  • 机器操控面板
  • 机器与和机器之间通过PackTag 互操作。

       使用PackML 与采纳其它所有工业软件标准一样,唯一的目的是减少系统设计的工作量,实现代码复用和低代码。 

        机器与机器之间采用PackML 相互操作。未来还能够将PaclML 封装到AAS 资产管理壳的子管理壳中。

      实现数字化制造的重点是构建物理对象的信息模型,提出的方案很多,最终我们拿什么来呈现物理对象的数字化模型?目前尚无定论。从发展趋势看,工业4.0的AAS 呼声比较高。packML也是一种方式。与此同时,OPCUA ,PackML,AAS 等模型相互交织在一起,相互引用,分分合合。搞得非常复杂。

OPCUA/PackML 的主要模型

状态机

   状态机是PackML 中最重要的概念,OPC UA 模型中支持有限状态机模型,有限状态机包含了状态(state)和转移(transittion)。下图中的长方形代表状态,连线代表的是转移。转移分为两种,一种是通过外部客户端调用方法和Tag实现,另一种是内部状态完成时由内部程序完成(SC- State complete)。

   packML 被OPCUA 采纳之后,导致了各种状态机模型。主要包括如下三种

  • PackML基本状态机
  • PackML机器状态机 是一台设备的packML 的状态机
  • PackML执行状态机 是执行状态下的子状态。

PackML基本状态机

基本状态机对应packML图的Clear 阶段的状态演变(下图的灰色部分)

 

 运行部分的状态机在内部的PackML Machine StateMachine中。

PackML 机器状态机

     

PackML执行状态机

 在执行状态下,还可以由子状态机(subState),如果存在的话,将会在PackML执行状态机中建模。

从上面的几个状态机来看,PackML 的状态图是分层实现的:

在PackMLBaseStateMachine 中

只定义了有关Clear和Abort的状态:

  • Cleared
  • Aborting
  • Aborted

在PackMLMachineStateMachine 中设及Reset,Stop 的状态变化

  • Clearing
  • Running
  • Stopping
  • Stoped

在 ExecuteState中

  • Complete
  • Competing
  • Execute
  • Held
  • Holding
  • Resetting
  • Starting
  • Suspened
  • Suspending
  • Unholding
  • unsuspending

至于为什么这样嵌套式地定义各种状态和转移,不是特别明白其中的奥秘。

状态机的运行机制

       PackML 设计建立在OPC UA 有限自动机模型的基础之上的。OPC UA 有限自动机信息模型如下:

       有限自动机的两个最重要基本概念是状态和转移(state and Transition)。在上图中,MyMethod导致转换Transition1,Transition 1有两个引用(FromState 和ToState)。从状态1 转移到状态2.从同时产生MyEvent事件。

          模型是描述出来了,MyMethod 导致Transition,transition 触发状态转移和事件触发都需要Opc UA 服务器中的程序来实现。至少Open61541 协议栈中是不包含这些程序功能的。

packML基本对象

作为一个完整的规范,PackML标准中不仅仅是状态机,还包括其它信息模型,PackML BaseObjectType是可以用于任何机器或者对象的packML 类型:

管理功能

Admin 包含了PackML OPC UA服务器的管理功能。 

状态

标签

一组方法

SetParameter

SetInterLock

SetProduct

SetMatchSpeed

基本状态机

基本状态机内部包含了PackML 机器状态机(PackML Machine StateMachine)。而PackML 机器状态机内部可以包含执行状态机。 

标签 PackTag

基于packML 的互操作除了采取调用Method 的方法,还有一种是采用PackTag

           按文档的说法,PackTags 是命名数据元素,用于开放式架构、自动化机器中的可互操作数据交换,也可用于机器与更高级别的信息系统(如制造运营管理和企业信息系统)之间的数据交换。

PackTags 分为三组:命令(command)状态(status)管理(admin)

命令标签(command)和状态标签(status)包含机器和生产线控制之间的接口以进行协调或配方/参数下载所需的数据。

  • 命令标签:作为程序控制作为入口。
  • 状态标签:由设备产生并修改,表示设备当前状态。
  • 管理标签:包含由更高级别系统收集的用于机器性能分析或操作员信息的数据。

通常,信息数据是在基于以太网的通信网络上使用 OPC 传递的

 OPC UA 中,Tag 是字符串格式的变量。在OPCUA /PackML文档中没有对PackID 有过多的说明。个人觉得OPCUA/PackML 是通过写入TagId和Status 来交换PackID。想必也需要内部程序支持。

实验过程

        最好的学习方法是从做中学。为了充分理解packML OPCUA 的封装以及具体的应用,我构建了一台设备的PackML 模型。值得一提的是,OPCUA/PackML 只是一个信息模型,它背后需要相应的程序配合。国外的PLC 产品中具有功能块库支持PackML.作为工业软件的开发者,需要掌握packML 的程序设计方法。

  在本实验中,

1 使用西门子公司的SiOME 或者uaModeling 软件构建一个具有PackML的OPCUA 模型。

2 使用Python 编写一个OPCUA/PackML 服务器。

3 开发一个基于OPCUA/PackML 的HMI 操控程序。

建立模型

        为了充分研究PackML 的运作机理,我们建立了一个完整的PackMLBaseObjects。(我们使用的是西门子的SiOME 建模工具。

BaseStateMachine的模型

MachineState模型

ExecuteState 模型

python OPCUA/PackML Server 代码

PackML Server 要完成的主要工作包括

导入Opc.Ua.PackML_Nodeset2.XML

导入OpcUA。PackMLServer_NodeSet2.XML

设置CurrentState

实现 PackML 模型中的Method,通过Method 实现状态转移。

添加MethodCallback

     在OpcUa Server 中,从NodeSet2.xml 导入模型后,如果包含了Method 对象,那就需要将一个MethodCallback 链接到MethodNode 。

在python opcua 中 使用下列函数:

server.link_method(StartMethodId,func);

在open62541 中 使用下列函数:

 UA_Server_setMethodNode_callback(server, nodeId, method);

实例:

        下面的程序展示了PackML 的状态变化,在客户端依次调用 start,Hold 和Unhold 方法,PackML 的currentState 依次Idle ->Execute->Held->Execute 变化。

import sys
sys.path.insert(0, "..")
import time
from opcua import ua, Server

def StartCallback(parent,variant):
    #val=variant.Value
    #print(val[0].Value);
    # change Current State From Idle to Execute
    print("Start Callback")
    path=["2:PackMLObjects", "3:TheMachine","2:BaseStateMachine","2:MachineState","2:ExecuteState","2:Execute"]
    ExecuteState=objects.get_child(path)
    path=["2:PackMLObjects", "3:TheMachine","2:BaseStateMachine","0:CurrentState","0:Id"]
    currentStateId=objects.get_child(path)
    currentStateId.set_value(ExecuteState.nodeid, ua.VariantType.NodeId)
    return 
def  HoldCallback(parent):
    print("Hold Callback")
    path=["2:PackMLObjects", "3:TheMachine","2:BaseStateMachine","2:MachineState","2:ExecuteState","2:Held"]
    HeldState=objects.get_child(path)
    path=["2:PackMLObjects", "3:TheMachine","2:BaseStateMachine","0:CurrentState","0:Id"]
    currentStateId=objects.get_child(path)
    currentStateId.set_value(HeldState.nodeid, ua.VariantType.NodeId)
    return 
def  UnholdCallback(parent):
    print("Unhold Callback")
    path=["2:PackMLObjects", "3:TheMachine","2:BaseStateMachine","2:MachineState","2:ExecuteState","2:Execute"]
    ExecuteState=objects.get_child(path)
    path=["2:PackMLObjects", "3:TheMachine","2:BaseStateMachine","0:CurrentState","0:Id"]
    currentStateId=objects.get_child(path)
    currentStateId.set_value(ExecuteState.nodeid, ua.VariantType.NodeId)
    return 
    return 
if __name__ == "__main__":

    # setup our server
    server = Server()
    server.set_endpoint("opc.tcp://127.0.0.1:48400/freeopcua/server/")
    server.import_xml("Opc.Ua.PackML.NodeSet2.xml")
    server.import_xml("OpcUa.PackMLServer.NodeSet2.xml")
    # 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()
    #path=["2:PackMLObjects", "3:TheMachine","2:BaseStateMachine","2:MachineState","2:Stopped"]
    #StoppedState=objects.get_child(path)
    #print(StoppedState.nodeid.Identifier.numerator)
    path=["2:PackMLObjects", "3:TheMachine","2:BaseStateMachine","2:MachineState","2:ExecuteState","2:Idle"]
    IdleState=objects.get_child(path)
    print(IdleState.nodeid.Identifier.numerator)
    path=["2:PackMLObjects", "3:TheMachine","2:BaseStateMachine","0:CurrentState","0:Id"]
    currentStateId=objects.get_child(path)
    print(currentStateId.nodeid.Identifier.numerator)
    currentStateId.set_writable()
    currentStateId.set_value(IdleState.nodeid, ua.VariantType.NodeId)
    # Add Satrt Method Callback
    path=["2:PackMLObjects", "3:TheMachine","2:BaseStateMachine","2:MachineState","2:ExecuteState","2:Start"]
    StartMethodId=objects.get_child(path)
    server.link_method(StartMethodId,StartCallback)
    #Add Hold Method Callback
    path=["2:PackMLObjects", "3:TheMachine","2:BaseStateMachine","2:MachineState","2:ExecuteState","2:Hold"]
    HoldMethodId=objects.get_child(path)
    server.link_method(HoldMethodId,HoldCallback)
    #Add UnHold Method Callback
    path=["2:PackMLObjects", "3:TheMachine","2:BaseStateMachine","2:MachineState","2:ExecuteState","2:Unhold"]
    UnholdMethodId=objects.get_child(path)
    server.link_method(UnholdMethodId,UnholdCallback)
    server.load_type_definitions()
    # starting!
    server.start()
    
    try:
        count = 0
        while True:
            time.sleep(1)
        
    finally:
        #close connection, remove subcsriptions, etc
        server.stop()

Python OPC UA/PackML HMI 代码

(待续)

小结

走马看花般看标准云里雾里,一旦要写代码,就要将标准看烂了方可。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值