學習open62541 --- [25] 使用建模工具SiOME

之前寫了一篇文章講述如何使用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差不多。

說實在的,這種教程寫起來非常累,希望看過的同學能給個贊。

如果有寫的不對的地方,希望能留言指正,謝謝閱讀。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章