콘텐츠로 바로가기

now0930 일지

이런저런 생각

  • 홈
  • 비공개
  • 강좌
  • 잔여 작업 조회
  • 위치

open62541 server – client variable 전달

tutorial을 열심히 따라했지만 변수를 추가하다 막혀 못했다. 내가 OPC-UA 프로토콜을 제대로 이해하지 못한 탓이다. 아래를 참조하여 변수를 서버에 간단히 추가했다.

https://open62541.org/doc/current/tutorial_server_variabletype.html

클라이언트가 서버 변수를 읽으려 했으나, 쉽게 하지 못했다. tutorial이 현재 시각을 쉽게 보여줘서 너무 일찍 시작하였다. callback 함수까지 설정했어야 했는데, 설명하지 않았다. 너무 불친절하다. 다행히도 좋은 동영상을 보고 이해했다. 다음 장에 나온 Connecting a Variable with a Physical Process 항목까지 읽고 따라 했어야 했다.

https://open62541.org/doc/current/tutorial_server_datasource.html
#include "open62541.h"

#include <signal.h>
#include <stdlib.h>

static volatile UA_Boolean running = true;
static void stopHandler(int sig) {
    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "received ctrl-c");
    running = false;
}


static void
addVariable(UA_Server *server) {
    /* Define the attribute of the myInteger variable node */
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    UA_Int32 myInteger = 42;
    UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
    attr.description = UA_LOCALIZEDTEXT("en-US","the answer");
    attr.displayName = UA_LOCALIZEDTEXT("en-US","the answer");
    attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
    attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;

    /* Add the variable node to the information model */
    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
                              parentReferenceNodeId, myIntegerName,
                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NULL);
}

static void
writeVariable(UA_Server *server) {
    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");

    /* Write a different integer value */
    UA_Int32 myInteger = 43;
    UA_Variant myVar;
    UA_Variant_init(&myVar);
    UA_Variant_setScalar(&myVar, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
    UA_Server_writeValue(server, myIntegerNodeId, myVar);
	 /* Set the status code of the value to an error code. The function
     * UA_Server_write provides access to the raw service. The above
     * UA_Server_writeValue is syntactic sugar for writing a specific node
     * attribute with the write service. */
    UA_WriteValue wv;
    UA_WriteValue_init(&wv);
    wv.nodeId = myIntegerNodeId;
    wv.attributeId = UA_ATTRIBUTEID_VALUE;
    wv.value.status = UA_STATUSCODE_BADNOTCONNECTED;
    wv.value.hasStatus = true;
    UA_Server_write(server, &wv);

    /* Reset the variable to a good statuscode with a value */
    wv.value.hasStatus = false;
    wv.value.value = myVar;
    wv.value.hasValue = true;
    UA_Server_write(server, &wv);
}


static void
writeWrongVariable(UA_Server *server) {
    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");

    /* Write a string */
    UA_String myString = UA_STRING("test");
    UA_Variant myVar;
    UA_Variant_init(&myVar);
    UA_Variant_setScalar(&myVar, &myString, &UA_TYPES[UA_TYPES_STRING]);
    UA_StatusCode retval = UA_Server_writeValue(server, myIntegerNodeId, myVar);
    printf("Writing a string returned statuscode %s\n", UA_StatusCode_name(retval));
}

static void
beforeReadVal(UA_Server *server,
               const UA_NodeId *sessionId, void *sessionContext,
               const UA_NodeId *nodeid, void *nodeContext,
               const UA_NumericRange *range, const UA_DataValue *data) {
    //updateCurrentTime(server);
	int tmpVal = rand()%1000;
    UA_Variant value;
    UA_Variant_setScalar(&value, &tmpVal, &UA_TYPES[UA_TYPES_INT32]);
    UA_NodeId currentNodeId = UA_NODEID_STRING(1, "Test Var:Me");
    UA_Server_writeValue(server, currentNodeId, value);
	

}

int main(void) {
    signal(SIGINT, stopHandler);
    signal(SIGTERM, stopHandler);

    UA_Server *server = UA_Server_new();
    UA_ServerConfig_setDefault(UA_Server_getConfig(server));
	//UA_ServerConfig* config = UA_Server_getConfig(server);
	//config->verifyRequestTimestamp = UA_RULEHANDLING_ACCEPT;

	//addVariable(server);
	//writeVariable(server);
	//writeWrongVariable(server);


	//vendor, serial, variable 순으로 추가.

	//공통으로 사용할 부분 설정.
	UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
	UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
//
//	//vendor name 추가.
//	UA_VariableAttributes vnAttr = UA_VariableAttributes_default;
//	UA_String vendorName = UA_STRING("test vendor");
//    UA_Variant_setScalar(&vnAttr.value, &vendorName, &UA_TYPES[UA_TYPES_STRING]);
//    UA_NodeId vendorNodeId = UA_NODEID_STRING(1, "Test VendorName:Me");
//	UA_QualifiedName myVendorName = UA_QUALIFIEDNAME(1, "the answer");
//    UA_Server_addVariableNode(server, vendorNodeId, parentNodeId,
//                              parentReferenceNodeId, myVendorName,
//                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), vnAttr, NULL, NULL);
//
//
//	//serial no 추가.
//	UA_VariableAttributes serialAttr = UA_VariableAttributes_default;
//	UA_Int32 serialName = 123456;
//    UA_Variant_setScalar(&serialAttr.value, &serialName, &UA_TYPES[UA_TYPES_INT32]);
//    UA_NodeId serialNodeId = UA_NODEID_STRING(1, "Test serial:Me");
//	UA_QualifiedName mySerialName = UA_QUALIFIEDNAME(1, "serial No");
//    UA_Server_addVariableNode(server, serialNodeId, parentNodeId,
//                              parentReferenceNodeId, mySerialName,
//                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), serialAttr, NULL, NULL);
//
	//variable 추가.
	UA_VariableAttributes varAttr = UA_VariableAttributes_default;
	UA_Int32 varName = 10;
    UA_Variant_setScalar(&varAttr.value, &varName, &UA_TYPES[UA_TYPES_INT32]);
    UA_NodeId varNodeId = UA_NODEID_STRING(1, "Test Var:Me");
	UA_QualifiedName myVarName = UA_QUALIFIEDNAME(1, "variable");
    UA_Server_addVariableNode(server, varNodeId, parentNodeId,
                              parentReferenceNodeId, myVarName,
                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), varAttr, NULL, NULL);

	//add callback
    UA_ValueCallback callback ;
    callback.onRead = beforeReadVal;
    callback.onWrite = NULL;
	UA_NodeId currentNodeId = UA_NODEID_STRING(1, "Test Var:Me");
    UA_Server_setVariableNode_valueCallback(server, currentNodeId, callback);


	//서버 구동.
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Starting server...");
    UA_StatusCode retval = UA_Server_run(server, &running);
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Shutdown server...");


    UA_Server_delete(server);
    return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}
#include "open62541.h"

#include <stdlib.h>

int main(void) {
    UA_Client *client = UA_Client_new();
    UA_ClientConfig_setDefault(UA_Client_getConfig(client));
    UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
	printf("retval is %d\n",retval);
    if(retval != UA_STATUSCODE_GOOD) {
        UA_Client_delete(client);
        return (int)retval;
    }

    /* Read the value attribute of the node. UA_Client_readValueAttribute is a
     * wrapper for the raw read service available as UA_Client_Service_read. */


	
//    UA_Variant value; /* Variants can hold scalar values and arrays of any type */
//    UA_Variant_init(&value);
//
//    /* NodeId of the variable holding the current time */
//    const UA_NodeId nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
//	retval = UA_Client_readValueAttribute(client, nodeId, &value);
//	printf("test\n");
//    if(retval == UA_STATUSCODE_GOOD &&
//       UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_DATETIME])) {
//        UA_DateTime raw_date = *(UA_DateTime *) value.data;
//        UA_DateTimeStruct dts = UA_DateTime_toStruct(raw_date);
//        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "date is: %u-%u-%u %u:%u:%u.%03u\n",
//                    dts.day, dts.month, dts.year, dts.hour, dts.min, dts.sec, dts.milliSec);
//
//    }
//
	//const UA_NodeId nodeId2 = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
//	const UA_NodeId nodeId2 = UA_NODEID_STRING(1, "the.answer");

//	UA_Variant value2;
//	UA_Variant_init(&value2);
//	UA_Int32 node_value=0;
//
//	retval = UA_Client_readValueAttribute(client, nodeId2, &value2);
//	if(retval == UA_STATUSCODE_GOOD &&
//			//UA_Variant_hasScalarType(&value2, &UA_TYPES[UA_TYPES_INT32])) {
//			UA_Variant_isScalar(&value2)) {
//		UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"myData is ");
//
//	}
//
    /* Read attribute */
    UA_Int32 value2 = 0;
    printf("\nReading the value of node (1, \"Test Var:Me\"):\n");
    UA_Variant *val = UA_Variant_new();
	printf("retval is %d\n",retval);
    retval = UA_Client_readValueAttribute(client, UA_NODEID_STRING(1, "Test Var:Me"), val);
	printf("retval is %d\n",retval);
    if(retval == UA_STATUSCODE_GOOD && UA_Variant_isScalar(val) &&
			val->type == &UA_TYPES[UA_TYPES_INT32]) {
		value2 = *(UA_Int32*)val->data;
		//printf("the value is: %i\n", value2);
		UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"myData is %d",value2);

	}
	UA_Variant_delete(val);
	/* Clean up */
//	UA_Variant_clear(&value);
	UA_Variant_clear(&value2);

	UA_Client_delete(client); /* Disconnects the client internally */
	return EXIT_SUCCESS;
}

노드 ID, 데이터 타입 등 까다롭게 맞춰줘야 한다. 틀리면 못 읽는다.

이 글 공유하기:

  • Tweet
발행일 2020-09-06글쓴이 이대원
카테고리 생활코딩 태그 opcua, open62541, plc, raspberry

댓글 남기기응답 취소

이 사이트는 Akismet을 사용하여 스팸을 줄입니다. 댓글 데이터가 어떻게 처리되는지 알아보세요.

글 내비게이션

이전 글

raspberry pi + opc-ua + open62541 삽질기

다음 글

open62541 method call

2025 6월
일 월 화 수 목 금 토
1234567
891011121314
15161718192021
22232425262728
2930  
5월    

최신 글

  • 자기 회로 정리 2025-06-22
  • common mode, differential mode 2025-05-11
  • signal conditioner, 신호 처리기 2025-05-10
  • strain gage 2025-05-09
  • 칼만 필터 2025-05-01

카테고리

  • 산업계측제어기술사
  • 삶 자국
    • 책과 영화
    • 투자
  • 생활코딩
    • LEGO
    • ROS
    • tensorflow
  • 전기기사
  • 피아노 악보

메타

  • 로그인
  • 엔트리 피드
  • 댓글 피드
  • WordPress.org

페이지

  • 소개
  • 잔여 작업 조회
    • 작업 추가
    • 작업의 사진 조회
    • 작업 수정 페이지
  • 사진
    • GPS 입력된 사진
    • 사진 조회
  • 위치
    • 하기 휴가 방문지
    • 해외 출장

태그

android bash c docker driver FSM gps java kernel LEGO linux mysql network program opcua open62541 plc programmers python raspberry reinforcementLearning ros state space system program tensorflow transfer function 경제 미국 민수 삼국지 세계사 실기 에너지 역사 유전자 일본 임베디드 리눅스 전기기사 조선 중국 채윤 코딩 테스트 통계 한국사 한국어

팔로우하세요

  • Facebook
now0930 일지
WordPress로 제작.
 

댓글 로드중...