콘텐츠로 바로가기

now0930 일지

이런저런 생각

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

open62541 server+client 공정 동작 시간 측정

처음 설정한 목표를 드디어 수행했다. 서버를 대략 구현했으니 다음으로 클라이언트를 만들었다. 내가 쪼랩이라 tutorial 문서 그대로 사용했다. cilent는 UA_Client_Service_browse로 server가 어떤 데이터를 가지고 있는지 볼 수 있다. 그러나 역시 한 단계밖에 볼 수 없다. 하부 구조를 보려면 nodeId를 기억하여 다시 browse를 해야 한다. 더 좋은 방법이 있겠지만, 이것도 되니까 문제 안된다.

서버가 method를 가지고 있어 client에서 오는 콜을 받아 정리해서 보내 줄 수도 있지만, 시간을 많이 쓸 듯하여 쉽고 간단하게 갔다.

서버가 각 공정 동작 상황을 OPC UA nodeId에 기록한다. 5초마다 업데이트 했는데, 실재 PLC로 구현한다면 매 초마다 update하는 방식으로 해야 할 듯하다. 그리 많은 부하가 걸리지는 않을 듯 하다.

클라이언트는 매 3초마다 서버로 request하여 정보를 받아오는 방식으로 작성했다. timestamp는 server쪽 시각을 쓰지 않고, 클라이언트가 임의로 만든 시각을 기록했다.

데이터를 데이터 베이스로 바로 업데이트 한다던가, 파일로 기록할 수도 있다. 그러나 시간을 많이 쓰므로 표준 출력으로 나온 메세지를 file로 redirect하면 쉽게 할 수 있다. 중복 데이터는 sort로 쉽게 지울 수 있다. bash 만세!

#include "open62541.h"

#include <signal.h>
#include <stdlib.h>
#include <time.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
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, "SEQ");
	//UA_Server_writeValue(server, currentNodeId, value);
}

/* predefined identifier for later use */
UA_NodeId pumpTypeId = {1, UA_NODEIDTYPE_NUMERIC, {1001}};
UA_NodeId cylTypeId = {1, UA_NODEIDTYPE_NUMERIC, {2001}};
UA_NodeId robotTypeId = {1, UA_NODEIDTYPE_NUMERIC, {3001}};


static void
defineObjectTypes(UA_Server *server) {
	/* Define the object type for "Device" */
	UA_NodeId deviceTypeId; /* get the nodeid assigned by the server */
	UA_ObjectTypeAttributes dtAttr = UA_ObjectTypeAttributes_default;
	dtAttr.displayName = UA_LOCALIZEDTEXT("en-US", "DeviceType");
	UA_Server_addObjectTypeNode(server, UA_NODEID_NULL,
			UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
			UA_QUALIFIEDNAME(1, "DeviceType"), dtAttr,
			NULL, &deviceTypeId);

	UA_VariableAttributes mnAttr = UA_VariableAttributes_default;
	mnAttr.displayName = UA_LOCALIZEDTEXT("en-US", "ManufacturerName");
	UA_NodeId manufacturerNameId;
	UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
			UA_QUALIFIEDNAME(1, "ManufacturerName"),
			UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), mnAttr, NULL, &manufacturerNameId);
	/* Make the manufacturer name mandatory */
	UA_Server_addReference(server, manufacturerNameId,
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
			UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true);


	UA_VariableAttributes modelAttr = UA_VariableAttributes_default;
	modelAttr.displayName = UA_LOCALIZEDTEXT("en-US", "ModelName");
	UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
			UA_QUALIFIEDNAME(1, "ModelName"),
			UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), modelAttr, NULL, NULL);

	/* Define the object type for "Pump" */
	UA_ObjectTypeAttributes ptAttr = UA_ObjectTypeAttributes_default;
	ptAttr.displayName = UA_LOCALIZEDTEXT("en-US", "PumpType");
	UA_Server_addObjectTypeNode(server, pumpTypeId,
			deviceTypeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
			UA_QUALIFIEDNAME(1, "PumpType"), ptAttr,
			NULL, NULL);

	UA_VariableAttributes statusAttr = UA_VariableAttributes_default;
	statusAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Status");
	statusAttr.valueRank = UA_VALUERANK_SCALAR;
	UA_NodeId statusId;
	UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId,
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
			UA_QUALIFIEDNAME(1, "Status"),
			UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), statusAttr, NULL, &statusId);
	/* Make the status variable mandatory */
	UA_Server_addReference(server, statusId,
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
			UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true);

	UA_VariableAttributes rpmAttr = UA_VariableAttributes_default;
	rpmAttr.displayName = UA_LOCALIZEDTEXT("en-US", "MotorRPM");
	rpmAttr.valueRank = UA_VALUERANK_SCALAR;
	UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId,
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
			UA_QUALIFIEDNAME(1, "MotorRPMs"),
			UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), rpmAttr, NULL, NULL);
}

static void
defineCylTypes(UA_Server *server) {
	/* Define the object type for "Device" */
	UA_NodeId deviceTypeId; /* get the nodeid assigned by the server */
	UA_ObjectTypeAttributes dtAttr = UA_ObjectTypeAttributes_default;
	dtAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Cylinder Type");
	UA_Server_addObjectTypeNode(server, UA_NODEID_NULL,
			UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
			UA_QUALIFIEDNAME(1, "Cylinder Type"), dtAttr,
			NULL, &deviceTypeId);

	UA_VariableAttributes mnAttr = UA_VariableAttributes_default;
	mnAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Manufacturer Name");
	UA_NodeId manufacturerNameId;
	UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
			UA_QUALIFIEDNAME(1, "Cylinder"),
			UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), mnAttr, NULL, &manufacturerNameId);

	UA_VariableAttributes modelAttr = UA_VariableAttributes_default;
	UA_NodeId modelNameId;
	modelAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Cyl Model Name");
	UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
			UA_QUALIFIEDNAME(1, "Cyl Model Name"),
			UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), modelAttr, NULL, &modelNameId);

	UA_Server_addReference(server,modelNameId,
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
			UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true);

	/* Define the object type for "Pump" */
	UA_ObjectTypeAttributes ptAttr = UA_ObjectTypeAttributes_default;
	ptAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Running State");
	UA_Server_addObjectTypeNode(server, cylTypeId,
			deviceTypeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
			UA_QUALIFIEDNAME(1, "Running State"), ptAttr,
			NULL, NULL);


	UA_VariableAttributes statusAttr = UA_VariableAttributes_default;
	statusAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Status");
	statusAttr.valueRank = UA_VALUERANK_SCALAR;
	UA_NodeId statusId;
	UA_Server_addVariableNode(server, UA_NODEID_NULL, cylTypeId,
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
			UA_QUALIFIEDNAME(1, "Status"),
			UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), statusAttr, NULL, &statusId);
	/* Make the manufacturer name mandatory */
	// UA_Server_addReference()를 어떻게 하냐에 따라  statusId
	// instance 만들경우 표시
	UA_Server_addReference(server, statusId,
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
			UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true);
}



static void
defineRobotTypes(UA_Server *server) {
	/* Define the object type for "Device" */
	UA_NodeId deviceTypeId; /* get the nodeid assigned by the server */
	UA_ObjectTypeAttributes dtAttr = UA_ObjectTypeAttributes_default;
	dtAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Robot Type");
	UA_Server_addObjectTypeNode(server, UA_NODEID_NULL,
			UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
			UA_QUALIFIEDNAME(1, "Robot Type"), dtAttr,
			NULL, &deviceTypeId);

	UA_VariableAttributes mnAttr = UA_VariableAttributes_default;
	mnAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Manufacturer Name");
	UA_NodeId manufacturerNameId;
	UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
			UA_QUALIFIEDNAME(1, "Robot"),
			UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), mnAttr, NULL, &manufacturerNameId);

	UA_VariableAttributes modelAttr = UA_VariableAttributes_default;
	UA_NodeId modelNameId;
	modelAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Robot Model Name");
	UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
			UA_QUALIFIEDNAME(1, "Robot Model Name"),
			UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), modelAttr, NULL, &modelNameId);

	UA_Server_addReference(server,modelNameId,
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
			UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true);

	/* Define the object type for "Pump" */
	UA_ObjectTypeAttributes ptAttr = UA_ObjectTypeAttributes_default;
	ptAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Interlock");
	UA_Server_addObjectTypeNode(server, robotTypeId,
			deviceTypeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
			UA_QUALIFIEDNAME(1, "Interlock"), ptAttr,
			NULL, NULL);


	UA_VariableAttributes statusAttr = UA_VariableAttributes_default;
	statusAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Status");
	statusAttr.valueRank = UA_VALUERANK_SCALAR;
	UA_NodeId statusId;
	UA_Server_addVariableNode(server, UA_NODEID_NULL, robotTypeId,
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
			UA_QUALIFIEDNAME(1, "Status"),
			UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), statusAttr, NULL, &statusId);
	/* Make the manufacturer name mandatory */
	// UA_Server_addReference()를 어떻게 하냐에 따라  statusId
	// instance 만들경우 표시
	UA_Server_addReference(server, statusId,
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
			UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true);
}



static void
addPumpObjectInstance(UA_Server *server, char *name) {
	UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
	oAttr.displayName = UA_LOCALIZEDTEXT("en-US", name);
	UA_Server_addObjectNode(server, UA_NODEID_NULL,
			UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
			UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
			UA_QUALIFIEDNAME(1, name),
			pumpTypeId, /* this refers to the object type
						   identifier */
			oAttr, NULL, NULL);
}

static void
addCylObjectInstance(UA_Server *server, char *name) {
	UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
	oAttr.displayName = UA_LOCALIZEDTEXT("en-US", name);
	/*
	   UA_Server_addObjectNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
	   const UA_NodeId parentNodeId,
	   const UA_NodeId referenceTypeId,
	   const UA_QualifiedName browseName,
	   const UA_NodeId typeDefinition,
	   const UA_ObjectAttributes attr,
	   void *nodeContext, UA_NodeId *outNewNodeId) {
	 */

	UA_Server_addObjectNode(server, UA_NODEID_NULL,
			UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
			UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
			UA_QUALIFIEDNAME(1, name),
			cylTypeId, /* this refers to the object type
						  identifier */
			oAttr, NULL, NULL);
}



static void
addRbtObjectInstance(UA_Server *server, char *name) {
	UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
	oAttr.displayName = UA_LOCALIZEDTEXT("en-US", name);
	/*
	   UA_Server_addObjectNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
	   const UA_NodeId parentNodeId,
	   const UA_NodeId referenceTypeId,
	   const UA_QualifiedName browseName,
	   const UA_NodeId typeDefinition,
	   const UA_ObjectAttributes attr,
	   void *nodeContext, UA_NodeId *outNewNodeId) {
	 */

	UA_Server_addObjectNode(server, UA_NODEID_NULL,
			UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
			UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
			UA_QUALIFIEDNAME(1, name),
			robotTypeId, /* this refers to the object type
							identifier */
			oAttr, NULL, NULL);
}


static UA_StatusCode
pumpTypeConstructor(UA_Server *server,
		const UA_NodeId *sessionId, void *sessionContext,
		const UA_NodeId *typeId, void *typeContext,
		const UA_NodeId *nodeId, void **nodeContext) {
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "New pump created");

	/* Find the NodeId of the status child variable */
	UA_RelativePathElement rpe;
	UA_RelativePathElement_init(&rpe);
	rpe.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
	rpe.isInverse = false;
	rpe.includeSubtypes = false;
	rpe.targetName = UA_QUALIFIEDNAME(1, "Status");

	UA_BrowsePath bp;
	UA_BrowsePath_init(&bp);
	bp.startingNode = *nodeId;
	bp.relativePath.elementsSize = 1;
	bp.relativePath.elements = &rpe;

	UA_BrowsePathResult bpr =
		UA_Server_translateBrowsePathToNodeIds(server, &bp);
	if(bpr.statusCode != UA_STATUSCODE_GOOD ||
			bpr.targetsSize < 1)
		return bpr.statusCode;

	/* Set the status value */
	UA_Boolean status = true;
	UA_Variant value;
	UA_Variant_setScalar(&value, &status, &UA_TYPES[UA_TYPES_BOOLEAN]);
	UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
	UA_BrowsePathResult_clear(&bpr);

	/* At this point we could replace the node context .. */

	return UA_STATUSCODE_GOOD;
}

static void
addPumpTypeConstructor(UA_Server *server) {
	UA_NodeTypeLifecycle lifecycle;
	lifecycle.constructor = pumpTypeConstructor;
	lifecycle.destructor = NULL;
	UA_Server_setNodeTypeLifecycle(server, pumpTypeId, lifecycle);
}




//method 추가
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_String *inputStr = (UA_String*)input->data;
	UA_String tmp = UA_STRING_ALLOC("Hello ");
	if(inputStr->length > 0) {
		tmp.data = (UA_Byte *)UA_realloc(tmp.data, tmp.length + inputStr->length);
		memcpy(&tmp.data[tmp.length], inputStr->data, inputStr->length);
		tmp.length += inputStr->length;
	}
	UA_Variant_setScalarCopy(output, &tmp, &UA_TYPES[UA_TYPES_STRING]);
	UA_String_clear(&tmp);
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Hello World was called");
	return UA_STATUSCODE_GOOD;
}

static void
addHellWorldMethod(UA_Server *server) {
	UA_Argument inputArgument;
	UA_Argument_init(&inputArgument);
	inputArgument.description = UA_LOCALIZEDTEXT("en-US", "A String");
	inputArgument.name = UA_STRING("MyInput");
	inputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
	inputArgument.valueRank = UA_VALUERANK_SCALAR;

	UA_Argument outputArgument;
	UA_Argument_init(&outputArgument);
	outputArgument.description = UA_LOCALIZEDTEXT("en-US", "A String");
	outputArgument.name = UA_STRING("MyOutput");
	outputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
	outputArgument.valueRank = UA_VALUERANK_SCALAR;

	UA_MethodAttributes helloAttr = UA_MethodAttributes_default;
	helloAttr.description = UA_LOCALIZEDTEXT("en-US","Say `Hello World`");
	helloAttr.displayName = UA_LOCALIZEDTEXT("en-US","Hello World");
	helloAttr.executable = true;
	helloAttr.userExecutable = true;
	UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541),
			UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
			UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
			UA_QUALIFIEDNAME(1, "hello world"),
			helloAttr, &helloWorldMethodCallback,
			1, &inputArgument, 1, &outputArgument, NULL, NULL);
}


//이름으로 nodeId를 찾는 함수.
UA_StatusCode
findNodeIdWithName(UA_Server *server, UA_NodeId *startNodeId, char *name, UA_NodeId **retNodeId){

	/* Find the NodeId of the status child variable */
	UA_RelativePathElement rpe;
	UA_RelativePathElement_init(&rpe);
	rpe.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
	rpe.isInverse = false;
	rpe.includeSubtypes = false;
	rpe.targetName = UA_QUALIFIEDNAME(1,name); 

	UA_BrowsePath bp;
	UA_BrowsePath_init(&bp);


	bp.startingNode = *startNodeId;
	bp.relativePath.elementsSize = 1;
	bp.relativePath.elements = &rpe;

	UA_BrowsePathResult bpr =
		UA_Server_translateBrowsePathToNodeIds(server, &bp);
	if(bpr.statusCode != UA_STATUSCODE_GOOD ||
			bpr.targetsSize < 1)
		return bpr.statusCode;
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "found target is %d", bpr.targets->targetId.nodeId.identifier.numeric);
	**retNodeId = bpr.targets->targetId.nodeId;

}



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);
	//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, "SEQ");
	UA_QualifiedName myVarName = UA_QUALIFIEDNAME(1, "SEQ");
	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, "SEQ");
	UA_Server_setVariableNode_valueCallback(server, currentNodeId, callback);

	//설비 정의

	defineCylTypes(server);
	addCylObjectInstance(server, "pin");
	addCylObjectInstance(server, "latch");
	addCylObjectInstance(server, "clamp");
	defineRobotTypes(server);
	addRbtObjectInstance(server, "Loading Robot");

	/*
	   defineObjectTypes(server);
	   addPumpTypeConstructor(server);
	   addPumpObjectInstance(server, "pump4");
	 */


	//method
	addHellWorldMethod(server);


	//ladder를 시뮬래이션하는 부분.
	//thread로 latch fwd -> clamp fwd -> pin fwd -> robot in -> pin bwd: 공정 작업시간
	//         robot in off -> clamp bwd -> latch bwd 순으로 실행

#undef TESe
#ifdef TEST

	//테스트
	//각 설비값을 설정하는 부분
	UA_Variant tmp_value;
	bool bitoff = 0;
	UA_Variant_setScalar(&tmp_value, &bitoff, &UA_TYPES[UA_TYPES_BOOLEAN]);
	UA_NodeId tmpNodeId = UA_NODEID_STRING(1, "Status");
	UA_Server_writeValue(server, tmpNodeId, tmp_value);

	/* Find the NodeId of the status child variable */
	UA_RelativePathElement rpe;
	UA_RelativePathElement_init(&rpe);
	rpe.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
	rpe.isInverse = false;
	rpe.includeSubtypes = true;
	//rpe.targetName = UA_QUALIFIEDNAME(1, "Status");
	rpe.targetName = UA_QUALIFIEDNAME(1, "latch");
	//rpe.targetName = UA_QUALIFIEDNAME(1, "variable");


	UA_BrowsePath bp;
	UA_BrowsePath_init(&bp);
	UA_NodeId *nodeId  = UA_NodeId_new();
	*nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
	//UA_NodeId tmp = {0, UA_NODEIDTYPE_NUMERIC, {50238}};
	//printf("\nNode id is %d\n", tmp);
	//UA_NodeId *nodeId = &tmp;
	bp.startingNode = *nodeId;
	bp.relativePath.elementsSize = 1;
	bp.relativePath.elements = &rpe;

	UA_BrowsePathResult bpr =
		UA_Server_translateBrowsePathToNodeIds(server, &bp);
	if(bpr.statusCode != UA_STATUSCODE_GOOD ||
			bpr.targetsSize < 1)
		return bpr.statusCode;

	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "found target is %d", bpr.targets->targetId.nodeId.identifier.numeric);
	//print로 structure를 출력할 수 없음.
	//printf("\nnode id is %d\n", (UA_NodeId)bpr.targets->targetId.nodeId);
	//* Set the status value */
	UA_Boolean status = true;
	UA_Variant value;
	UA_Variant_setScalar(&value, &status, &UA_TYPES[UA_TYPES_BOOLEAN]);
	UA_Server_writeValue(server, bpr.targets->targetId.nodeId, value);
	//UA_Server_writeValue(server, tmp, value);

	//다시 아래로 내림.
	bp.startingNode = bpr.targets->targetId.nodeId;
	rpe.targetName = UA_QUALIFIEDNAME(1, "Status");
	UA_BrowsePathResult bpr2 =
		UA_Server_translateBrowsePathToNodeIds(server, &bp);
	if(bpr2.statusCode != UA_STATUSCODE_GOOD ||
			bpr2.targetsSize < 1)
		return bpr.statusCode;

	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "found target is %d", bpr2.targets->targetId.nodeId.identifier.numeric);
	UA_Server_writeValue(server, bpr2.targets->targetId.nodeId, value);
	UA_BrowsePathResult_clear(&bpr);
#endif

	//NodeId를 저장할 structure 생성
	struct eqiupNodeId{
		UA_NodeId latchNodeId;
		UA_NodeId clampNodeId;
		UA_NodeId pinNodeId;
		UA_NodeId robotNodeId;
	};

	struct eqiupNodeId myEquipNodeId;

	UA_NodeId **nodeIdtmp;
	UA_NodeId *nodeIdtmp2 = UA_NodeId_new();
	nodeIdtmp = &nodeIdtmp2;
	UA_NodeId *nodeId  = UA_NodeId_new();
	*nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);

	if(	findNodeIdWithName(server, nodeId, "latch", nodeIdtmp) == UA_STATUSCODE_GOOD){
		nodeId = *nodeIdtmp;
		if( findNodeIdWithName(server, nodeId, "Status", nodeIdtmp) == UA_STATUSCODE_GOOD){
			nodeId = *nodeIdtmp;
			myEquipNodeId.latchNodeId = *nodeId;
			//printf("Variable 변경\n");
			//* Set the status value */
			//			UA_Boolean status = false;
			//			UA_Variant value;
			//			UA_Variant_setScalar(&value, &status, &UA_TYPES[UA_TYPES_BOOLEAN]);
			//			UA_Server_writeValue(server, *nodeId, value);
			//			UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "node id %d value was written", nodeId->identifier.numeric);
		}
	}
	*nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
	if(	findNodeIdWithName(server, nodeId, "clamp", nodeIdtmp) == UA_STATUSCODE_GOOD){
		nodeId = *nodeIdtmp;
		if( findNodeIdWithName(server, nodeId, "Status", nodeIdtmp) == UA_STATUSCODE_GOOD){
			nodeId = *nodeIdtmp;
			myEquipNodeId.clampNodeId = *nodeId;

			//printf("Variable 변경\n");
			//* Set the status value */
			//			UA_Boolean status = false;
			//			UA_Variant value;
			//			UA_Variant_setScalar(&value, &status, &UA_TYPES[UA_TYPES_BOOLEAN]);
			//			UA_Server_writeValue(server, *nodeId, value);
			//			UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "node id %d value was written", nodeId->identifier.numeric);
		}
	}

	*nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
	if(	findNodeIdWithName(server, nodeId, "pin", nodeIdtmp) == UA_STATUSCODE_GOOD){
		nodeId = *nodeIdtmp;
		if( findNodeIdWithName(server, nodeId, "Status", nodeIdtmp) == UA_STATUSCODE_GOOD){
			nodeId = *nodeIdtmp;
			myEquipNodeId.pinNodeId = *nodeId;
			//printf("Variable 변경\n");
			//* Set the status value */
			//			UA_Boolean status = false;
			//			UA_Variant value;
			//			UA_Variant_setScalar(&value, &status, &UA_TYPES[UA_TYPES_BOOLEAN]);
			//			UA_Server_writeValue(server, *nodeId, value);
			//			UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "node id %d value was written", nodeId->identifier.numeric);
		}
	}

	*nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
	if(	findNodeIdWithName(server, nodeId, "Loading Robot", nodeIdtmp) == UA_STATUSCODE_GOOD){
		nodeId = *nodeIdtmp;
		if( findNodeIdWithName(server, nodeId, "Status", nodeIdtmp) == UA_STATUSCODE_GOOD){
			nodeId = *nodeIdtmp;
			myEquipNodeId.robotNodeId = *nodeId;
			//printf("Variable 변경\n");
			//* Set the status value */
			//			UA_Boolean status = false;
			//			UA_Variant value;
			//			UA_Variant_setScalar(&value, &status, &UA_TYPES[UA_TYPES_BOOLEAN]);
			//			UA_Server_writeValue(server, *nodeId, value);
			//			UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "node id %d value was written", nodeId->identifier.numeric);
		}
	}


	//구조체 출력.
	//printf("현재 노드는 %d \n", myEquipNodeId.latchNodeId.identifier.numeric);
	//printf("현재 노드는 %d \n", myEquipNodeId.clampNodeId.identifier.numeric);
	//printf("현재 노드는 %d \n", myEquipNodeId.pinNodeId.identifier.numeric);
	//printf("현재 노드는 %d \n", myEquipNodeId.robotNodeId.identifier.numeric);

	//한번에 업데이트
	UA_Boolean statusTrue = true;
	UA_Boolean statusFalse = false;
	UA_Variant value;
	UA_Variant_setScalar(&value, &statusFalse, &UA_TYPES[UA_TYPES_BOOLEAN]);

	//초기상태
	UA_Server_writeValue(server, myEquipNodeId.latchNodeId, value);
	UA_Server_writeValue(server, myEquipNodeId.clampNodeId, value);
	UA_Server_writeValue(server, myEquipNodeId.pinNodeId, value);
	UA_Server_writeValue(server, myEquipNodeId.robotNodeId, value);
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "모든 노드가 초기화(모든 Value가 0) 됨.");



	//서버 구동.
	//주기적으로 업데이트로 수정
	//https://youtu.be/abDnBv5u6bU//
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Starting server...every 5 secs");
	UA_StatusCode retval = UA_Server_run_startup(server);

	if(retval != UA_STATUSCODE_GOOD){
		UA_Server_delete(server);
		return retval;
	}


	int timestamp = time(0)+5;
	int equipStates = 0;
	int seqInt = 0;
	bool seqFlag=false;		//false: 현재 작업 중, true: 작업완료 다음 작업 시작

	while(running == true){
		// Handle Server
		UA_Server_run_iterate(server, true);


		//새로운 seq을 할당 받음
		if(seqFlag == true)
			seqInt = rand()%1000;


		//Update Status Variable
		if(time(0) > timestamp)
		{
			timestamp = time(0) + 5;

			switch(equipStates)
			{
				case 0: //초기상태
					equipStates = 1;
					//seq 기록.
					UA_Variant_setScalar(&value, &seqInt, &UA_TYPES[UA_TYPES_INT32]);
					UA_NodeId currentNodeId = UA_NODEID_STRING(1, "SEQ");
					UA_Server_writeValue(server, currentNodeId, value);
					//flag 비활성화
					seqFlag = false;

					UA_Variant_setScalar(&value, &statusFalse, &UA_TYPES[UA_TYPES_BOOLEAN]);

					UA_Server_writeValue(server, myEquipNodeId.latchNodeId, value);
					UA_Server_writeValue(server, myEquipNodeId.clampNodeId, value);
					UA_Server_writeValue(server, myEquipNodeId.pinNodeId, value);
					UA_Server_writeValue(server, myEquipNodeId.robotNodeId, value);
					UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "모든 노드가 초기화(모든 Value가 0) 됨.");
					break;

				case 1: //1단계, latch fwd
					equipStates = 2;
					//flag 비활성화
					seqFlag = false;

					UA_Variant_setScalar(&value, &statusFalse, &UA_TYPES[UA_TYPES_BOOLEAN]);

					UA_Server_writeValue(server, myEquipNodeId.clampNodeId, value);
					UA_Server_writeValue(server, myEquipNodeId.pinNodeId, value);
					UA_Server_writeValue(server, myEquipNodeId.robotNodeId, value);

					UA_Variant_setScalar(&value, &statusTrue, &UA_TYPES[UA_TYPES_BOOLEAN]);
					UA_Server_writeValue(server, myEquipNodeId.latchNodeId, value);
					UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "1단계 동작 완료..latch 전진");
					break;

				case 2: //2단계, clamp fwd
					equipStates = 3;
					//flag 비활성화
					seqFlag = false;

					UA_Variant_setScalar(&value, &statusFalse, &UA_TYPES[UA_TYPES_BOOLEAN]);
					UA_Server_writeValue(server, myEquipNodeId.pinNodeId, value);
					UA_Server_writeValue(server, myEquipNodeId.robotNodeId, value);

					UA_Variant_setScalar(&value, &statusTrue, &UA_TYPES[UA_TYPES_BOOLEAN]);
					UA_Server_writeValue(server, myEquipNodeId.latchNodeId, value);
					UA_Server_writeValue(server, myEquipNodeId.clampNodeId, value);
					UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "2단계 동작 완료..calmp 전진(latch, clamp 전진상태)");
					break;

				case 3: //3단계, pin fwd
					equipStates = 4;
					//flag 비활성화
					seqFlag = false;

					UA_Variant_setScalar(&value, &statusFalse, &UA_TYPES[UA_TYPES_BOOLEAN]);
					UA_Server_writeValue(server, myEquipNodeId.robotNodeId, value);

					UA_Variant_setScalar(&value, &statusTrue, &UA_TYPES[UA_TYPES_BOOLEAN]);
					UA_Server_writeValue(server, myEquipNodeId.latchNodeId, value);
					UA_Server_writeValue(server, myEquipNodeId.clampNodeId, value);
					UA_Server_writeValue(server, myEquipNodeId.pinNodeId, value);
					UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "3단계 동작 완료..pin 전진(latch, clamp, pin 전진상태)");
					break;

				case 4: //4단계, robot in
					equipStates = 5;
					//flag 비활성화
					seqFlag = false;

					UA_Variant_setScalar(&value, &statusTrue, &UA_TYPES[UA_TYPES_BOOLEAN]);

					UA_Server_writeValue(server, myEquipNodeId.robotNodeId, value);
					UA_Server_writeValue(server, myEquipNodeId.latchNodeId, value);
					UA_Server_writeValue(server, myEquipNodeId.clampNodeId, value);
					UA_Server_writeValue(server, myEquipNodeId.pinNodeId, value);
					UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "4단계 동작 완료..robot in (latch, clamp, pin 전진상태, robot 진입)");
					break;

				case 5: //5단계, robot in
					//다시 초기상태로 이동
					equipStates = 0;
					//flag 활성화
					seqFlag = true;

					UA_Variant_setScalar(&value, &statusTrue, &UA_TYPES[UA_TYPES_BOOLEAN]);

					UA_Server_writeValue(server, myEquipNodeId.robotNodeId, value);
					UA_Server_writeValue(server, myEquipNodeId.latchNodeId, value);
					UA_Server_writeValue(server, myEquipNodeId.clampNodeId, value);

					UA_Variant_setScalar(&value, &statusFalse, &UA_TYPES[UA_TYPES_BOOLEAN]);
					UA_Server_writeValue(server, myEquipNodeId.pinNodeId, value);

					UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "5단계 동작 완료..pin bwd(latch, clamp, 전진상태, robot 진입, pin 후진)");
					break;

				default:
					equipStates = 0;
					//flag 비활성화
					seqFlag = false;
					seqInt = 0;

					break;

			} //switch


		}	//if


	} //while

	retval = UA_Server_run_shutdown(server);
	//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;
} // main
#include "open62541.h"
#include <time.h>
#include <stdlib.h>


//#ifdef UA_ENABLE_METHODCALLS
//UA_StatusCode
//UA_Client_call(UA_Client *client, const UA_NodeId objectId,
//               const UA_NodeId methodId, size_t inputSize, const UA_Variant *input,
//               size_t *outputSize, UA_Variant **output);
//#endif

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;
    }


	//NodeId로 조회한 값을 저장할 structure 생성
	struct eqiupVal {
		UA_Int32 seq;
		UA_NodeId latchNodeId;
		UA_NodeId clampNodeId;
		UA_NodeId pinNodeId;
		UA_NodeId robotNodeId;
		UA_Boolean latchVal;
		UA_Boolean clampVal;
		UA_Boolean pinVal;
		UA_Boolean robotVal;
	};
	struct eqiupVal myEquipVal;
	time_t now;
	struct tm *ts;



    /* Read attribute */
    UA_Int32 value2 = 0;
    //printf("\nReading the value of node (1, \"Test Var:Me\"):\n");
    UA_Variant *val = UA_Variant_new();
    retval = UA_Client_readValueAttribute(client, UA_NODEID_STRING(1, "SEQ"), val);
    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,"current seq is %d",value2);

	}
//#ifdef UA_ENABLE_METHODCALLS
//UA_StatusCode
//UA_Client_call(UA_Client *client, const UA_NodeId objectId,
//               const UA_NodeId methodId, size_t inputSize, const UA_Variant *input,
//               size_t *outputSize, UA_Variant **output);
//#endif


	//method call

#ifdef UA_ENABLE_METHODCALLS
	/* Call a remote method */
	UA_Variant input;
	UA_String argString = UA_STRING("Hello Server");
	UA_Variant_init(&input);
	//UA_Variant_setScalarCopy(&input, &argString, &UA_TYPES[UA_TYPES_STRING]);
	UA_Variant_setScalarCopy(&input, &argString, &UA_TYPES[UA_TYPES_STRING]);
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "input data is %s\n",((UA_String*)input.data)->data);

	size_t outputSize;
	UA_Variant *output;
	retval = UA_Client_call(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
			UA_NODEID_NUMERIC(1, 62541), 1, &input, &outputSize, &output);

	if(retval == UA_STATUSCODE_GOOD) {
		printf("Method call was successful, and %lu returned values available.\n",
				(unsigned long)outputSize);

//		if(UA_Variant_hasScalarType(output, &UA_TYPES[UA_TYPES_STRING])) { // if you are paranoid you may also want to check the dimensions
//			UA_String value4 = *(UA_String *)output->data;
//			printf("value4 is %s\n",value4);
//		}
		UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "output data is %s\n",((UA_String*)output->data)->data);
		//UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, (UA_String*)output->data);

		//UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "output is %s", value4);
		UA_Array_delete(output, outputSize, &UA_TYPES[UA_TYPES_VARIANT]);
	} else {
		printf("Method call was unsuccessful, and %x returned values available.\n", retval);
	}
	UA_Variant_clear(&input);
#endif

    /* Browse some objects */
    printf("Browsing nodes in objects folder:\n");
    UA_BrowseRequest bReq;
    UA_BrowseRequest_init(&bReq);
    bReq.requestedMaxReferencesPerNode = 0;
    bReq.nodesToBrowse = UA_BrowseDescription_new();
    bReq.nodesToBrowseSize = 1;
    bReq.nodesToBrowse[0].nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); /* browse objects folder */
    bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; /* return everything */
    UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);
    printf("%-9s %-16s %-16s %-16s\n", "NAMESPACE", "NODEID", "BROWSE NAME", "DISPLAY NAME");
    for(size_t i = 0; i < bResp.resultsSize; ++i) {
        for(size_t j = 0; j < bResp.results[i].referencesSize; ++j) {
            UA_ReferenceDescription *ref = &(bResp.results[i].references[j]);
            if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_NUMERIC) {
                printf("%-9d %-16d %-16.*s %-16.*s\n", ref->nodeId.nodeId.namespaceIndex,
                       ref->nodeId.nodeId.identifier.numeric, (int)ref->browseName.name.length,
                       ref->browseName.name.data, (int)ref->displayName.text.length,
                       ref->displayName.text.data);
            } else if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_STRING) {
                printf("%-9d %-16.*s %-16.*s %-16.*s\n", ref->nodeId.nodeId.namespaceIndex,
                       (int)ref->nodeId.nodeId.identifier.string.length,
                       ref->nodeId.nodeId.identifier.string.data,
                       (int)ref->browseName.name.length, ref->browseName.name.data,
                       (int)ref->displayName.text.length, ref->displayName.text.data);
            }
            /* TODO: distinguish further types */
			//아래 문장으로 client 모든 brwose를 접근할 수 있음.
			//한 단계 아래만 보임.
			//UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);


			// object class 만 찾아 node로 기록.
			if(ref->nodeClass == UA_NODECLASS_OBJECT &&
					!strcmp(ref->browseName.name.data, "Loading Robot")){
				printf("\nNODE CLASS OBJECT\n");
				//임시로 node ID를 저장
				myEquipVal.robotNodeId = (UA_NodeId)ref->nodeId.nodeId;
			}
			// object class 만 찾아 node로 기록.
			if(ref->nodeClass == UA_NODECLASS_OBJECT &&
					!strcmp(ref->browseName.name.data, "pin")){
				printf("\nNODE CLASS OBJECT\n");
				//임시로 node ID를 저장
				myEquipVal.pinNodeId = (UA_NodeId)ref->nodeId.nodeId;
			}

			if(ref->nodeClass == UA_NODECLASS_OBJECT &&
					!strcmp(ref->browseName.name.data, "latch")){
				printf("\nNODE CLASS OBJECT\n");
				//임시로 node ID를 저장
				myEquipVal.latchNodeId = (UA_NodeId)ref->nodeId.nodeId;
			}
			if(ref->nodeClass == UA_NODECLASS_OBJECT &&
					!strcmp(ref->browseName.name.data, "clamp")){
				printf("\nNODE CLASS OBJECT\n");
				//임시로 node ID를 저장
				myEquipVal.clampNodeId = (UA_NodeId)ref->nodeId.nodeId;
			}

        }
    }
    UA_BrowseRequest_clear(&bReq);
	UA_BrowseResponse_clear(&bResp);

	//얻은 데이터로 value값을 찾기 위한 nodeId 확인
	//BaseDataType을 얻어내야 함
	UA_BrowseRequest_init(&bReq);
	bReq.requestedMaxReferencesPerNode = 0;
	bReq.requestedMaxReferencesPerNode = 0;
    bReq.nodesToBrowse = UA_BrowseDescription_new();
	bReq.nodesToBrowseSize = 1;
	bReq.nodesToBrowse[0].nodeId = myEquipVal.robotNodeId; /* browse objects folder */
    bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; /* return everything */
	//bResp로 다시 query
	bResp = UA_Client_Service_browse(client, bReq);
	for(size_t i = 0; i < bResp.resultsSize; ++i) {
		for(size_t j = 0; j < bResp.results[i].referencesSize; ++j) {
			UA_ReferenceDescription *ref = &(bResp.results[i].references[j]);
			//robot
			if(ref->nodeClass == UA_NODECLASS_VARIABLE &&
					!strcmp(ref->browseName.name.data, "Status")){
				printf("found\n");
				myEquipVal.robotNodeId = (UA_NodeId)ref->nodeId.nodeId;
			}
			
		}
	}

	bReq.nodesToBrowse[0].nodeId = myEquipVal.latchNodeId; /* browse objects folder */
    bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; /* return everything */
	//bResp로 다시 query
	bResp = UA_Client_Service_browse(client, bReq);
	for(size_t i = 0; i < bResp.resultsSize; ++i) {
		for(size_t j = 0; j < bResp.results[i].referencesSize; ++j) {
			UA_ReferenceDescription *ref = &(bResp.results[i].references[j]);
			//robot
			if(ref->nodeClass == UA_NODECLASS_VARIABLE &&
					!strcmp(ref->browseName.name.data, "Status")){
				printf("found\n");
				myEquipVal.latchNodeId = (UA_NodeId)ref->nodeId.nodeId;
			}
			
		}
	}
	bReq.nodesToBrowse[0].nodeId = myEquipVal.pinNodeId; /* browse objects folder */
    bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; /* return everything */
	//bResp로 다시 query
	bResp = UA_Client_Service_browse(client, bReq);
	for(size_t i = 0; i < bResp.resultsSize; ++i) {
		for(size_t j = 0; j < bResp.results[i].referencesSize; ++j) {
			UA_ReferenceDescription *ref = &(bResp.results[i].references[j]);
			//robot
			if(ref->nodeClass == UA_NODECLASS_VARIABLE &&
					!strcmp(ref->browseName.name.data, "Status")){
				printf("found\n");
				myEquipVal.pinNodeId = (UA_NodeId)ref->nodeId.nodeId;
			}
			
		}
	}

	bReq.nodesToBrowse[0].nodeId = myEquipVal.clampNodeId; /* browse objects folder */
    bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; /* return everything */
	//bResp로 다시 query
	bResp = UA_Client_Service_browse(client, bReq);
	for(size_t i = 0; i < bResp.resultsSize; ++i) {
		for(size_t j = 0; j < bResp.results[i].referencesSize; ++j) {
			UA_ReferenceDescription *ref = &(bResp.results[i].references[j]);
			//robot
			if(ref->nodeClass == UA_NODECLASS_VARIABLE &&
					!strcmp(ref->browseName.name.data, "Status")){
				printf("found\n");
				myEquipVal.clampNodeId = (UA_NodeId)ref->nodeId.nodeId;
			}
			
		}
	}
    UA_BrowseRequest_clear(&bReq);
	UA_BrowseResponse_clear(&bResp);


	for(int i=0;i<100;i++){

    /* Read attribute */
    UA_Int32 value2 = 0;
    //printf("\nReading the value of node (1, \"Test Var:Me\"):\n");
    retval = UA_Client_readValueAttribute(client, UA_NODEID_STRING(1, "SEQ"), val);
    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,"current seq is %d",value2);
		myEquipVal.seq = *(UA_Int32*)val->data;

	}

		//nodeId로 Value를 얻음. 
		retval = UA_Client_readValueAttribute(client, myEquipVal.pinNodeId , val);
		if(retval == UA_STATUSCODE_GOOD && UA_Variant_isScalar(val) &&
				val->type == &UA_TYPES[UA_TYPES_BOOLEAN]) {
			myEquipVal.pinVal = *(UA_Boolean*)val->data;
			//UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"node id 50232(pin) is %d", *(UA_Boolean*)val->data);
		}

		//nodeId로 Value를 얻음. 
		retval = UA_Client_readValueAttribute(client, myEquipVal.robotNodeId, val);
		if(retval == UA_STATUSCODE_GOOD && UA_Variant_isScalar(val) &&
				val->type == &UA_TYPES[UA_TYPES_BOOLEAN]) {
			myEquipVal.robotVal = *(UA_Boolean*)val->data;
			//UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"node id 50246(robot) is %d", *(UA_Boolean*)val->data);
		}

		//nodeId로 Value를 얻음. 
		retval = UA_Client_readValueAttribute(client, myEquipVal.clampNodeId, val);
		if(retval == UA_STATUSCODE_GOOD && UA_Variant_isScalar(val) &&
				val->type == &UA_TYPES[UA_TYPES_BOOLEAN]) {
			myEquipVal.clampVal = *(UA_Boolean*)val->data;
			//UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"node id 50238(clamp) is %d", *(UA_Boolean*)val->data);
		}

		//nodeId로 Value를 얻음. 
		retval = UA_Client_readValueAttribute(client, myEquipVal.latchNodeId, val);
		if(retval == UA_STATUSCODE_GOOD && UA_Variant_isScalar(val) &&
				val->type == &UA_TYPES[UA_TYPES_BOOLEAN]) {
			myEquipVal.latchVal = *(UA_Boolean*)val->data;
			//UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"node id 50235(latch) is %d", *(UA_Boolean*)val->data);
		}


		///printf("myEquipVal is\n");
		///printf("pin is %d\n", myEquipVal.pinVal);
		///printf("robot is %d\n", myEquipVal.robotVal);
		///printf("latch is %d\n", myEquipVal.latchVal);
		///printf("clamp is %d\n", myEquipVal.clampVal);
		now = time(NULL);
		ts = localtime(&now);
		UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"%d cylcle read 완료",i);
		UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"%d-%d-%d, %d:%d:%d, seq,%d,latch,%d, clamp,%d, pin,%d,robot,%d.",
				//ts 3개 +3개 숫자.년월일 + 시각:분:초
				ts->tm_year+1900, ts->tm_mon+1, ts->tm_mday,
				ts->tm_hour, ts->tm_min, ts->tm_sec,
				myEquipVal.seq,
				myEquipVal.latchVal,myEquipVal.clampVal,
				myEquipVal.pinVal, myEquipVal.robotVal);

		sleep(3);
	}


	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;
}

이 글 공유하기:

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

댓글 남기기응답 취소

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

글 내비게이션

이전 글

open62531 server plc 시뮬레이션

다음 글

강화학습으로 훈련한 스토리지

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로 제작.