ua_services_call.c 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. #include "ua_services.h"
  2. #include "ua_server_internal.h"
  3. #include "ua_statuscodes.h"
  4. #include "ua_util.h"
  5. #include "ua_nodestore.h"
  6. #include "ua_nodes.h"
  7. static const UA_VariableNode *getArgumentsVariableNode(UA_Server *server, const UA_MethodNode *ofMethod,
  8. UA_String withBrowseName) {
  9. const UA_Node *refTarget;
  10. UA_NodeId hasProperty = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY);
  11. for(UA_Int32 i = 0; i < ofMethod->referencesSize; i++) {
  12. if(ofMethod->references[i].isInverse == UA_FALSE &&
  13. UA_NodeId_equal(&hasProperty, &ofMethod->references[i].referenceTypeId)) {
  14. refTarget = UA_NodeStore_get(server->nodestore, &ofMethod->references[i].targetId.nodeId);
  15. if(!refTarget)
  16. continue;
  17. if(refTarget->nodeClass == UA_NODECLASS_VARIABLE &&
  18. refTarget->browseName.namespaceIndex == 0 &&
  19. UA_String_equal(&withBrowseName, &refTarget->browseName.name)) {
  20. return (const UA_VariableNode*) refTarget;
  21. }
  22. UA_NodeStore_release(refTarget);
  23. }
  24. }
  25. return UA_NULL;
  26. }
  27. static UA_StatusCode statisfySignature(UA_Variant *var, UA_Argument arg) {
  28. if(!UA_NodeId_equal(&var->type->typeId, &arg.dataType) )
  29. return UA_STATUSCODE_BADINVALIDARGUMENT;
  30. // Note: The namespace compiler will compile nodes with their actual array dimensions, never -1
  31. if(arg.arrayDimensionsSize > 0 && var->arrayDimensionsSize > 0)
  32. if(var->arrayDimensionsSize != arg.arrayDimensionsSize)
  33. return UA_STATUSCODE_BADINVALIDARGUMENT;
  34. // Continue with jpfr's statisfySignature from here on
  35. /* ValueRank Semantics
  36. * n >= 1: the value is an array with the specified number of dimens*ions.
  37. * n = 0: the value is an array with one or more dimensions.
  38. * n = -1: the value is a scalar.
  39. * n = -2: the value can be a scalar or an array with any number of dimensions.
  40. * n = -3: the value can be a scalar or a one dimensional array. */
  41. UA_Boolean scalar = UA_Variant_isScalar(var);
  42. if(arg.valueRank == 0 && scalar)
  43. return UA_STATUSCODE_BADINVALIDARGUMENT;
  44. if(arg.valueRank == -1 && !scalar)
  45. return UA_STATUSCODE_BADINVALIDARGUMENT;
  46. if(arg.valueRank == -3 && var->arrayDimensionsSize > 1)
  47. return UA_STATUSCODE_BADINVALIDARGUMENT;
  48. if(arg.valueRank > 1 && var->arrayDimensionsSize != arg.arrayDimensionsSize)
  49. return UA_STATUSCODE_BADINVALIDARGUMENT;
  50. //variants do not always encode the dimension flag (e.g. 1d array)
  51. if(var->arrayDimensionsSize==-1 && arg.arrayDimensionsSize == 1 &&
  52. var->arrayLength > 0 && arg.arrayDimensions[0] == (UA_UInt32)var->arrayLength ){
  53. return UA_STATUSCODE_GOOD;
  54. }else{
  55. if(arg.valueRank >= 1 && var->arrayDimensionsSize != arg.arrayDimensionsSize)
  56. return UA_STATUSCODE_BADINVALIDARGUMENT;
  57. if(arg.arrayDimensionsSize >= 1) {
  58. if(arg.arrayDimensionsSize != var->arrayDimensionsSize)
  59. return UA_STATUSCODE_BADINVALIDARGUMENT;
  60. for(UA_Int32 i = 0; i < arg.arrayDimensionsSize; i++) {
  61. if(arg.arrayDimensions[i] != (UA_UInt32) var->arrayDimensions[i])
  62. return UA_STATUSCODE_BADINVALIDARGUMENT;
  63. }
  64. }
  65. }
  66. return UA_STATUSCODE_GOOD;
  67. }
  68. static UA_StatusCode argConformsToDefinition(UA_CallMethodRequest *rs, const UA_VariableNode *argDefinition) {
  69. if(argDefinition->value.variant.value.type != &UA_TYPES[UA_TYPES_ARGUMENT] &&
  70. argDefinition->value.variant.value.type != &UA_TYPES[UA_TYPES_EXTENSIONOBJECT])
  71. return UA_STATUSCODE_BADINTERNALERROR;
  72. if(rs->inputArgumentsSize < argDefinition->value.variant.value.arrayLength)
  73. return UA_STATUSCODE_BADARGUMENTSMISSING;
  74. if(rs->inputArgumentsSize > argDefinition->value.variant.value.arrayLength)
  75. return UA_STATUSCODE_BADINVALIDARGUMENT;
  76. const UA_ExtensionObject *thisArgDefExtObj;
  77. UA_Variant *var;
  78. UA_Argument arg;
  79. size_t decodingOffset = 0;
  80. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  81. UA_NodeId ArgumentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ARGUMENT + UA_ENCODINGOFFSET_BINARY);
  82. for(int i = 0; i<rs->inputArgumentsSize; i++) {
  83. var = &rs->inputArguments[i];
  84. if(argDefinition->value.variant.value.type == &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]) {
  85. thisArgDefExtObj = &((const UA_ExtensionObject *) (argDefinition->value.variant.value.data))[i];
  86. decodingOffset = 0;
  87. if(!UA_NodeId_equal(&ArgumentNodeId, &thisArgDefExtObj->typeId))
  88. return UA_STATUSCODE_BADINTERNALERROR;
  89. UA_decodeBinary(&thisArgDefExtObj->body, &decodingOffset, &arg, &UA_TYPES[UA_TYPES_ARGUMENT]);
  90. } else if(argDefinition->value.variant.value.type == &UA_TYPES[UA_TYPES_ARGUMENT])
  91. arg = ((UA_Argument *) argDefinition->value.variant.value.data)[i];
  92. retval |= statisfySignature(var, arg);
  93. }
  94. return retval;
  95. }
  96. static void callMethod(UA_Server *server, UA_Session *session, UA_CallMethodRequest *request,
  97. UA_CallMethodResult *result) {
  98. const UA_MethodNode *methodCalled = (const UA_MethodNode*) UA_NodeStore_get(server->nodestore,
  99. &request->methodId);
  100. if(!methodCalled) {
  101. result->statusCode = UA_STATUSCODE_BADMETHODINVALID;
  102. return;
  103. }
  104. const UA_ObjectNode *withObject = (const UA_ObjectNode *) UA_NodeStore_get(server->nodestore,
  105. &request->objectId);
  106. if(!withObject) {
  107. result->statusCode = UA_STATUSCODE_BADNODEIDINVALID;
  108. goto releaseMethodReturn;
  109. }
  110. if(methodCalled->nodeClass != UA_NODECLASS_METHOD) {
  111. result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID;
  112. goto releaseBothReturn;
  113. }
  114. if(withObject->nodeClass != UA_NODECLASS_OBJECT && withObject->nodeClass != UA_NODECLASS_OBJECTTYPE) {
  115. result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID;
  116. goto releaseBothReturn;
  117. }
  118. /* Verify method/object relations */
  119. // Object must have a hasComponent reference (or any inherited referenceType from sayd reference)
  120. // to be valid for a methodCall...
  121. result->statusCode = UA_STATUSCODE_BADMETHODINVALID;
  122. for(UA_Int32 i = 0; i < withObject->referencesSize; i++) {
  123. if(withObject->references[i].referenceTypeId.identifier.numeric == UA_NS0ID_HASCOMPONENT) {
  124. // FIXME: Not checking any subtypes of HasComponent at the moment
  125. if(UA_NodeId_equal(&withObject->references[i].targetId.nodeId, &methodCalled->nodeId)) {
  126. result->statusCode = UA_STATUSCODE_GOOD;
  127. break;
  128. }
  129. }
  130. }
  131. if(result->statusCode != UA_STATUSCODE_GOOD)
  132. goto releaseBothReturn;
  133. /* Verify method executable */
  134. if(methodCalled->executable == UA_FALSE || methodCalled->userExecutable == UA_FALSE) {
  135. result->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE?
  136. goto releaseBothReturn;
  137. }
  138. /* Verify Input Argument count, types and sizes */
  139. const UA_VariableNode *inputArguments = getArgumentsVariableNode(server, methodCalled,
  140. UA_STRING("InputArguments"));
  141. if(inputArguments) {
  142. // Expects arguments
  143. result->statusCode = argConformsToDefinition(request, inputArguments);
  144. UA_NodeStore_release((const UA_Node*)inputArguments);
  145. if(result->statusCode != UA_STATUSCODE_GOOD)
  146. goto releaseBothReturn;
  147. } else if(request->inputArgumentsSize > 0) {
  148. // Expects no arguments, but got some
  149. result->statusCode = UA_STATUSCODE_BADINVALIDARGUMENT;
  150. goto releaseBothReturn;
  151. }
  152. const UA_VariableNode *outputArguments = getArgumentsVariableNode(server, methodCalled,
  153. UA_STRING("OutputArguments"));
  154. if(!outputArguments) {
  155. // A MethodNode must have an OutputArguments variable (which may be empty)
  156. result->statusCode = UA_STATUSCODE_BADINTERNALERROR;
  157. goto releaseBothReturn;
  158. }
  159. // Call method if available
  160. if(methodCalled->attachedMethod) {
  161. result->outputArguments = UA_Array_new(&UA_TYPES[UA_TYPES_VARIANT],
  162. outputArguments->value.variant.value.arrayLength);
  163. result->outputArgumentsSize = outputArguments->value.variant.value.arrayLength;
  164. result->statusCode = methodCalled->attachedMethod(withObject->nodeId, request->inputArguments,
  165. result->outputArguments, methodCalled->methodHandle);
  166. }
  167. else
  168. result->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE?
  169. /* FIXME: Verify Output Argument count, types and sizes */
  170. if(outputArguments)
  171. UA_NodeStore_release((const UA_Node*)outputArguments);
  172. releaseBothReturn:
  173. UA_NodeStore_release((const UA_Node*)withObject);
  174. releaseMethodReturn:
  175. UA_NodeStore_release((const UA_Node*)methodCalled);
  176. }
  177. void Service_Call(UA_Server *server, UA_Session *session, const UA_CallRequest *request,
  178. UA_CallResponse *response) {
  179. if(request->methodsToCallSize <= 0) {
  180. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  181. return;
  182. }
  183. response->results = UA_Array_new(&UA_TYPES[UA_TYPES_CALLMETHODRESULT], request->methodsToCallSize);
  184. if(!response->results) {
  185. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  186. return;
  187. }
  188. response->resultsSize = request->methodsToCallSize;
  189. for(UA_Int32 i = 0; i < request->methodsToCallSize;i++)
  190. callMethod(server, session, &request->methodsToCall[i], &response->results[i]);
  191. }