学习open62541 --- [25] 使用建模工具SiOME

本文详述了使用SiOME进行OPC-UA建模的过程,包括创建对象类型、变量、方法及设置规则,并介绍了如何生成XML文件及代码,最后整合至OPC-UA Server。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前写了一篇文章讲述如何使用UaModeler,后来经一个读者朋友提醒,才发现它是商业软件,不交钱只能创建有限数量的节点…

后来本人去查找了一下相关的免费软件,找到2款:一个是freeOPCUA下的opcua-modeler,网址是https://github.com/FreeOpcUa/opcua-modeler;另外一款是西门子公司的SiOME,网址是这里

这2款软件都是只生成XML文件,UaModeler不仅可以生成XML文件,也可以自动生成代码(不过我们不需要生成的代码),但是要收费。经对比使用之后,它们的操作基本相同,但是感觉SiOME更胜一筹,界面也更加简洁美观。

下面就讲述如何使用SiOME


一 下载SiOME

打开SiOME的下载网址,点击下图中黄色标记的地方进行下载
在这里插入图片描述
如果没有注册,就需要注册一下,后面就可以下载了。
在这里插入图片描述
下载好了之后,直接解压,里面有个exe文件,直接点击就可以使用
在这里插入图片描述
双击打开后如下,
在这里插入图片描述


二 使用

这里创建一个对象类型,这个对象类型里有2个变量和1个方法,该方法有2个输入参数和一个输出参数。下面是操作步骤

1. 添加新的Namespace

首先,要添加新的Namespace,有了自定义的Namespace后,才能在这个Namespace下添加对象类型。

在顶部Namespaces栏点击右边黄色标记处,
在这里插入图片描述
会弹出Add New Namespace,点击之,
在这里插入图片描述
弹出如下界面,在Namespace URI下填入值就可以了,然后点击OK
在这里插入图片描述
可能会问:填什么呢?
可以参考已有的Namespace,SiOME打开时,就已经有了一个Namespace,我们点击如下黄色标记地方,
在这里插入图片描述
就能看到已有的Namespace,如下,这是个基础的Namespace
在这里插入图片描述
所以就可以模仿着写,如:http://myexample.org/UA/

2. 添加自定义对象类型

在左侧的Information model窗口中找到BaseObjectType,因为任意自定义对象类型都要继承BaseObjectType,如下,
在这里插入图片描述
右击BaseObjectType,选择Add New ObjectType,
在这里插入图片描述
在弹出的窗口中的Name栏里填入myObjectType,注意NodeClass是ObjectType,Namespace也是我们新添加的那个,然后点击OK
在这里插入图片描述
这样,我们添加的对象类型就已经出现了,
在这里插入图片描述

3. 添加变量

这里给myObjectType添加2个变量var1和var2,右击myObjectType,弹出界面如下,选择Add Child
在这里插入图片描述
弹出如下界面,注意默认NodeClass是Object
在这里插入图片描述
修改后如下,注意黄色标记的地方
在这里插入图片描述
ReferenceType选的是HasComponent,点击右侧倒三角后选择路径如下,
在这里插入图片描述
DataType选择Int32,点击右侧倒三角后选择路径如下,
在这里插入图片描述
同样的操作,添加变量var2,添加完成后如下,
在这里插入图片描述

4. 添加方法

添加一个方法叫func,同样右击myObjectType选择Add Child,然后在弹出界面里进行设置,如下,
在这里插入图片描述
点击OK,然后展开添加的方法,如下,
在这里插入图片描述
func下有2个属性,InputArguments和OutputArguments,即输入参数和输出参数,这也可以在右侧Reference窗口里看到它们的关系,
在这里插入图片描述
右击InputArguments,选择Add New Argument,
在这里插入图片描述
添加后如下,
在这里插入图片描述
选中Arg1后在右侧Argument Attributes里修改一下,如下黄色标记,
在这里插入图片描述
同样添加另外一个输入参数叫Data2和输出参数returnVal,它们属性如下
在这里插入图片描述
在这里插入图片描述
这样函数func就添加完毕了,如下所示,
在这里插入图片描述

5. 设置ModellingRule

ModellingRule是个Reference,决定了使用对象类型创建对象时如何生成其child,点击myObjectType,在右侧References窗口中展开Hierarchical References,下图红圈
在这里插入图片描述
如下
在这里插入图片描述
在ModellingRule栏下勾选框中进行勾选,如下,
在这里插入图片描述
这样var1,var2和func的ModellingRule就设置为Mandatory了,在创建对象时一定会生成这些child。

6. 生成XML

这个很简单,点击工具栏的Save as按钮
在这里插入图片描述
或者直接按ctrl+s,就会弹出保存对话框,保存为example.xml就可以了。

如果以后需要修改之前创建的Namespace,可以点击工具栏的open按钮去打开保存的xml文件就ok了。


三 生成代码及使用

生成代码及使用步骤请参照这篇文章的第三节,不再赘述,最终整体代码如下,

/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
 * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */

#include <signal.h>
#include <stdio.h>

#include "open62541.h"

/* Files myNS.h and myNS.c are created from example.xml */
#include "myNS.h"

UA_Boolean running = true;

static void stopHandler(int sign) {
    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
    running = false;
}


static UA_StatusCode helloWorldMethodCallback(UA_Server *server,
                         			const UA_NodeId *sessionId, void *sessionHandle,
                         			const UA_NodeId *methodId, void *methodContext,
                         			const UA_NodeId *objectId, void *objectContext,
                         			size_t inputSize, const UA_Variant *input,
                         			size_t outputSize, UA_Variant *output) 
{
    UA_Int32 value = 0;
    for (size_t i = 0; i < inputSize; ++i)
    {
    	UA_Int32 * ptr = (UA_Int32 *)input[i].data;
    	value += (*ptr);
    }

    UA_Variant_setScalarCopy(output, &value, &UA_TYPES[UA_TYPES_INT32]);
    
    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Hello World was called");
    
    return UA_STATUSCODE_GOOD;
}


int main(int argc, char **argv) 
{
    signal(SIGINT, stopHandler);
    signal(SIGTERM, stopHandler);

    UA_Server *server = UA_Server_new();
    UA_ServerConfig_setDefault(UA_Server_getConfig(server));

    UA_StatusCode retval;
    /* create nodes from nodeset */
    if (myNS(server) != UA_STATUSCODE_GOOD) {
        UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Could not add the example nodeset. "
            "Check previous output for any error.");
        retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
    } else {
        UA_UInt16 myNSIndex = UA_Server_addNamespace(server, "http://myexample.org/UA/");
        
        // 方法节点的NodeId是UA_NODEID_NUMERIC(myNSIndex, 1012)
        UA_Server_setMethodNode_callback(server, UA_NODEID_NUMERIC(myNSIndex, 1012), &helloWorldMethodCallback);
        
        
        UA_NodeId createdNodeId;
        UA_ObjectAttributes object_attr = UA_ObjectAttributes_default;

        object_attr.description = UA_LOCALIZEDTEXT("en-US", "myNSObject");
        object_attr.displayName = UA_LOCALIZEDTEXT("en-US", "myNSObject");
        
        // myObjectType的NodeId是UA_NODEID_NUMERIC(myNSIndex, 1009)
        UA_Server_addObjectNode(server, UA_NODEID_NULL,
                                UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
                                UA_QUALIFIEDNAME(1, "myNSObject"),
                                UA_NODEID_NUMERIC(myNSIndex, 1009),
                                object_attr, NULL, &createdNodeId);
        
        
        
        UA_NodeId createdNodeId2;
        UA_ObjectAttributes object_attr2 = UA_ObjectAttributes_default;

        object_attr2.description = UA_LOCALIZEDTEXT("en-US", "myNSObject2");
        object_attr2.displayName = UA_LOCALIZEDTEXT("en-US", "myNSObject2");
        
        // myObjectType的NodeId是UA_NODEID_NUMERIC(myNSIndex, 1009)
        UA_Server_addObjectNode(server, UA_NODEID_NULL,
                                UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
                                UA_QUALIFIEDNAME(1, "myNSObject2"),
                                UA_NODEID_NUMERIC(myNSIndex, 1009),
                                object_attr2, NULL, &createdNodeId2);
        
        
        retval = UA_Server_run(server, &running);
    }

    UA_Server_delete(server);
    
    return (int) retval;
}

代码关键点

  1. 使用UA_Server_addNamespace()获取我们新加的Namespace index值,这个很重要,因为这个值不一定是使用SiOME创建Namespace时生成的那个值,使用SiOME创建时是1,但是运行server后变成了2
  2. 对象类型节点和方法节点的NodeId在SiOME创建时会自动生成

关于对象类型节点和方法节点的NodeId,推荐使用路径搜索去查找,这个是最靠谱的方法,可以查看这篇文章

最终编译OK后,使用UaExpert进行连接,成功后如下
在这里插入图片描述
关于为什么新建的Namespace的index值变成了2,可以使用UaExpert看出来,点击Address Space下的No Highlight右侧的倒三角进行展开,如下,
在这里插入图片描述
可以看到1被urn:open62541.server.application给用了。


四 总结

本文主要讲述如何使用SiOME来进行建模,以及后续生成对应代码并集成到OPC UA Server里来,操作上和UaModeler以及opcua-modeler差不多。

说实在的,这种教程写起来非常累,希望看过的同学能给个赞。

如果有写的不对的地方,希望能留言指正,谢谢阅读。

评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值