튜토리얼에 method call이 있어 따라 해봤다. method를 서버에 설정하고, client에서 반응을 보고 싶었는데, 잘 안됬다. 두 시간 삽질만에 UA_VARINT에서 UA_String을 출력했다. 처음에는 몰랐는데 gdb로 구조를 보면 대략 알 수 있다. UA_String도 구조체라 data와 length로 구성된다. UA_Variant로 data를 출력하려면 UA_String으로 캐스팅하여 data의 data를 찾아야 한다.–;
(gdb) 45 /* Call a remote method */ 46 UA_Variant input; 47 UA_String argString = UA_STRING("Hello Server"); 48 UA_Variant_init(&input); 49 //UA_Variant_setScalarCopy(&input, &argString, &UA_TYPES[UA_TYPES_STRING]); 50 UA_Variant_setScalarCopy(&input, &argString, &UA_TYPES[UA_TYPES_STRING]); 51 UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "input data is %s\n",(UA_String*)input.data); 52 53 size_t outputSize; 54 UA_Variant *output; (gdb) display input 1: input = {type = 0xbefff4c8, storageType = (unknown: 3070066356), arrayLength = 0, data = 0xb6ffd13c, arrayDimensionsSize = 4, arrayDimensions = 0xb6ffd14c} (gdb) n 48 UA_Variant_init(&input); 1: input = {type = 0xbefff4c8, storageType = (unknown: 3070066356), arrayLength = 0, data = 0xb6ffd13c, arrayDimensionsSize = 4, arrayDimensions = 0xb6ffd14c} (gdb) print argString $1 = {length = 12, data = 0x10f0c "Hello Server"} (gdb) print argString->data $2 = (UA_Byte *) 0x10f0c "Hello Server" (gdb) n 50 UA_Variant_setScalarCopy(&input, &argString, &UA_TYPES[UA_TYPES_STRING]); 1: input = {type = 0x0, storageType = UA_VARIANT_DATA, arrayLength = 0, data = 0x0, arrayDimensionsSize = 0, arrayDimensions = 0x0} (gdb) n 51 UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "input data is %s\n",(UA_String*)input.data); 1: input = {type = 0x22250 <UA_TYPES+704>, storageType = UA_VARIANT_DATA, arrayLength = 0, data = 0x282d8, arrayDimensionsSize = 0, arrayDimensions = 0x0} (gdb) print input->data $3 = (void *) 0x282d8 (gdb) print input->data->data Attempt to dereference a generic pointer. (gdb) print (UA_String*)(input->data)->data Attempt to dereference a generic pointer. (gdb) print (UA_String*)(input.data)->data Attempt to dereference a generic pointer. (gdb) display argString 2: argString = {length = 12, data = 0x10f0c "Hello Server"} (gdb) display 1: input = {type = 0x22250 <UA_TYPES+704>, storageType = UA_VARIANT_DATA, arrayLength = 0, data = 0x282d8, arrayDimensionsSize = 0, arrayDimensions = 0x0} 2: argString = {length = 12, data = 0x10f0c "Hello Server"} (gdb) display &argString 3: &argString = (UA_String *) 0xbefff368
[2020-09-07 04:29:15.352 (UTC+0900)] info/userland input data is Hello Server�� Method call was successful, and 1 returned values available. [2020-09-07 04:29:15.352 (UTC+0900)] info/userland output data is Hello Hello Server [2020-09-07 04:29:15.353 (UTC+0900)] info/client Client Status: ChannelState: Closed, SessionState: Closed, ConnectStatus: Good
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 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, "Test Var:Me"), 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,"myData is %d",value2); } //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); UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "output data is %s\n",((UA_String*)output->data)->data); 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 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; }