基於open62541的opc ua 服務器開發實現(1)


關於opcua的介紹這里就不多說了,相信大家大都有了一些了解,open62541是一個開源C(C99)的opc-ua實現,開源代碼可在官網或github上下載。

話不多說,首先搭建一個opcua服務器實例

 1 #include <signal.h>
 2 #include "open62541.h"
 3    UA_Boolean running = true;
 4    static void stopHandler(int sig)
{
5 UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "received ctrl-c"); 6 running = false; 7 } 8 int main(void) { 9 signal(SIGINT, stopHandler); 10 signal(SIGTERM, stopHandler); 11 UA_ServerConfig *config = UA_ServerConfig_new_default(); 12 UA_Server *server = UA_Server_new(config); 13 UA_StatusCode retval = UA_Server_run(server, &running); 14 UA_Server_delete(server); 15 UA_ServerConfig_delete(config); 16 return (int)retval; 17 }

以下代碼演示如何使用數據類型以及如何向服務器添加變量節點。首先,我們向服務器添加一個新變量。查看UA變量屬性結構的定義,以查看為變量節點定義的所有屬性的列表。請注意,默認設置將變量值的訪問級別設置為只讀。有關使變量可寫的信息,請參見下文。

 1 #include <signal.h>
 2 #include <stdio.h>
 3 #include "open62541.h"
 4 static void
 5 addVariable(UA_Server *server) {
 6    /* Define the attribute of the myInteger variable node */
 7    UA_VariableAttributes attr = UA_VariableAttributes_default;
 8    UA_Int32 myInteger = 42;
 9    UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
10    attr.description = UA_LOCALIZEDTEXT("en-US","the answer");
11    attr.displayName = UA_LOCALIZEDTEXT("en-US","the answer");
12    attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
13    attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
14    /* Add the variable node to the information model */
15    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
16    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
17    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
18    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
19    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
20    parentReferenceNodeId, myIntegerName,
21    UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),attr, NULL, NULL);
22    }

現在我們用 寫服務 更改節點值。也可以通過網絡由OPC UA的客戶端訪問來修改值 。

 

 1 static void
 2 writeVariable(UA_Server *server) {
 3     UA_NodeId myIntegerNodeId = 
 4     UA_NODEID_STRING(1,"the.answer");
 5 
 6     /* Write a different integer value */
 7     UA_Int32 myInteger = 43;
 8     UA_Variant myVar;
 9     UA_Variant_init(&myVar);
10     UA_Variant_setScalar(&myVar, &myInteger, 
11     &UA_TYPES[UA_TYPES_INT32]);
12     UA_Server_writeValue(server, myIntegerNodeId, myVar);
13 
14     /* Set the status code of the value to an error code. The 
15     function
16     * UA_Server_write provides access to the raw service. The 
17     above
18     * UA_Server_writeValue is syntactic sugar for writing a specific 
19     node
20     * attribute with the write service. */
21     UA_WriteValue wv;
22     UA_WriteValue_init(&wv);
23     wv.nodeId = myIntegerNodeId;
24     wv.attributeId = UA_ATTRIBUTEID_VALUE;
25     wv.value.status = UA_STATUSCODE_BADNOTCONNECTED;
26     wv.value.hasStatus = true;
27     UA_Server_write(server, &wv);
28 
29     /* Reset the variable to a good statuscode with a value */
30     wv.value.hasStatus = false;
31     wv.value.value = myVar;
32     wv.value.hasValue = true;
33     UA_Server_write(server, &wv);
34     }

 

注意我們最初如何將變量節點的data type屬性設置為int32數據類型的nodeid。這禁止寫入非Int32的值。下面的代碼顯示了如何對每次寫入執行一致性檢查。

 1 static void
 2 writeWrongVariable(UA_Server *server) {
 3    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
 4 
 5    /* Write a string */
 6    UA_String myString = UA_STRING("test");
 7    UA_Variant myVar;
 8    UA_Variant_init(&myVar);
 9    UA_Variant_setScalar(&myVar, &myString, 
10    &UA_TYPES[UA_TYPES_STRING]);
11    UA_StatusCode retval = UA_Server_writeValue(server, 
12    myIntegerNodeId, myVar);
13    printf("Writing a string returned statuscode %s\n", 
14    UA_StatusCode_name(retval));
15    }

它遵循主服務器代碼,使用上述定義。

 1 UA_Boolean running = true;
 2 static void stopHandler(int sign) {
 3 UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
 4 running = false;
 5 }
 6 
 7 int main(void) {
 8    signal(SIGINT, stopHandler);
 9    signal(SIGTERM, stopHandler);
10  
11    UA_ServerConfig *config = UA_ServerConfig_new_default();
12    UA_Server *server = UA_Server_new(config);
13 
14    addVariable(server);
15    writeVariable(server);
16    writeWrongVariable(server);
17 
18    UA_StatusCode retval = UA_Server_run(server, &running);
19    UA_Server_delete(server);
20    UA_ServerConfig_delete(config);
21    return (int)retval;
22    }

在基於OPC UA的體系結構中,服務器通常位於信息源附近。在工業環境中,這意味着服務器接近物理過程,客戶機在運行時使用數據。在前面的教程中,我們看到了如何向OPC UA信息模型添加變量。本實例演示如何將變量連接到運行時信息,例如從物理過程的測量中連接。為簡單起見,我們以系統時鍾為基礎的“過程”。以下代碼段分別與運行時更新變量值的不同方式有關。總之,代碼片段定義了一個可編譯的源文件。

作為起點,假設已在服務器中為datetime類型的值創建了一個標識符為“ns=1,s=current time”的變量。假設當一個新的值從底層進程到達時,我們的應用程序被觸發,我們可以直接寫入變量。

 1 #include <signal.h>
 2 #include "open62541.h"
 3 #pragma comment(lib, "WS2_32")
 4 static void
 5 updateCurrentTime(UA_Server *server) {
 6     UA_DateTime now = UA_DateTime_now();
 7     UA_Variant value;
 8     UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
 9     UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
10     UA_Server_writeValue(server, currentNodeId, value);
11 }
12 static void
13 addCurrentTimeVariable(UA_Server *server) {
14     UA_DateTime now = 0;
15     UA_VariableAttributes attr = UA_VariableAttributes_default;
16     attr.displayName = UA_LOCALIZEDTEXT("en-US", "Current time");
17     attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
18     UA_Variant_setScalar(&attr.value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
19     UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
20     UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time");
21     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
22     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
23     UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
24     UA_Server_addVariableNode(server, currentNodeId, parentNodeId,
25         parentReferenceNodeId, currentName,
26         variableTypeNodeId, attr, NULL, NULL);
27     updateCurrentTime(server);
28 }

2.值回調

當一個值不斷變化時,例如系統時間,在一個緊密的循環中更新該值將占用大量的資源。值回調允許將變量值與外部表示同步。它們將回調附加到每次讀操作之前和每次寫操作之后執行的變量。

 1 static void
 2 beforeReadTime(UA_Server *server,
 3     const UA_NodeId *sessionId, void *sessionContext,
 4     const UA_NodeId *nodeid, void *nodeContext,
 5     const UA_NumericRange *range, const UA_DataValue *data) {
 6     UA_DateTime now = UA_DateTime_now();
 7     UA_Variant value;
 8     UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
 9     UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
10     UA_Server_writeValue(server, currentNodeId, value);
11 }
12 static void
13 afterWriteTime(UA_Server *server,
14     const UA_NodeId *sessionId, void *sessionContext,
15     const UA_NodeId *nodeId, void *nodeContext,
16     const UA_NumericRange *range, const UA_DataValue *data) {
17     UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
18         "The variable was updated");
19 }
20 static void
21 addValueCallbackToCurrentTimeVariable(UA_Server *server) {
22     UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
23     UA_ValueCallback callback;
24     callback.onRead = beforeReadTime;
25     callback.onWrite = afterWriteTime;
26     UA_Server_setVariableNode_valueCallback(server, currentNodeId, callback);
27 }

3.變量數據源

    對於值回調,該值仍存儲在變量節點中。所謂的數據源更進一步。服務器將每個讀寫請求重定向到回調函數。在讀取時,回調提供當前值的副本。在內部,數據源需要實現自己的內存管理。

 1 static UA_StatusCode
 2 readCurrentTime(UA_Server *server,
 3     const UA_NodeId *sessionId, void *sessionContext,
 4     const UA_NodeId *nodeId, void *nodeContext,
 5     UA_Boolean sourceTimeStamp, const UA_NumericRange *range,
 6     UA_DataValue *dataValue) {
 7     UA_DateTime now = UA_DateTime_now();
 8     UA_Variant_setScalarCopy(&dataValue->value, &now,
 9         &UA_TYPES[UA_TYPES_DATETIME]);
10     dataValue->hasValue = true;
11     return UA_STATUSCODE_GOOD;
12 }
13 
14 static UA_StatusCode
15 writeCurrentTime(UA_Server *server,
16     const UA_NodeId *sessionId, void *sessionContext,
17     const UA_NodeId *nodeId, void *nodeContext,
18     const UA_NumericRange *range, const UA_DataValue *data) {
19     UA_LOG_INFO(UA_Log_out, UA_LOGCATEGORY_USERLAND,
20         "Changing the system time is not implemented");
21     return UA_STATUSCODE_GOOD;
22 }
23 
24 static void
25 addCurrentTimeDataSourceVariable(UA_Server *server) {
26     UA_VariableAttributes attr = UA_VariableAttributes_default;
27     attr.displayName = UA_LOCALIZEDTEXT("en-US", "Current time - data source");
28     attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
29     UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-datasource");
30     UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time-datasource");
31     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
32     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
33     UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
34     UA_DataSource timeDataSource;
35     timeDataSource.read = readCurrentTime;
36     timeDataSource.write = writeCurrentTime;
37     UA_Server_addDataSourceVariableNode(server, currentNodeId, parentNodeId,
38         parentReferenceNodeId, currentName,
39         variableTypeNodeId, attr,
40         timeDataSource, NULL, NULL);
41 }

以下代碼遵循主服務器代碼,使用上述定義。

 1 UA_Boolean running = true;
 2 static void stopHandler(int sign) {
 3     UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
 4     running = false;
 5 }
 6 int main(void) {
 7     signal(SIGINT, stopHandler);
 8     signal(SIGTERM, stopHandler);
 9     UA_ServerConfig *config = UA_ServerConfig_new_default();
10     UA_Server *server = UA_Server_new(config);
11     addCurrentTimeVariable(server);
12     addValueCallbackToCurrentTimeVariable(server);
13     addCurrentTimeDataSourceVariable(server);
14     UA_StatusCode retval = UA_Server_run(server, &running);
15     UA_Server_delete(server);
16     UA_ServerConfig_delete(config);
17     return (int)retval;
18 }
View Code

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM