Merge remote-tracking branch 'origin/1.4' into merge_14_master_18

This commit is contained in:
Julius Pfrommer 2024-07-26 17:51:05 +02:00
commit 1f8e9f04fa
20 changed files with 236 additions and 142 deletions

View File

@ -36,7 +36,7 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# overwritten with more detailed information if git is available.
set(OPEN62541_VER_MAJOR 1)
set(OPEN62541_VER_MINOR 4)
set(OPEN62541_VER_PATCH 2)
set(OPEN62541_VER_PATCH 3)
set(OPEN62541_VER_LABEL "-undefined") # like "-rc1" or "-g4538abcd" or "-g4538abcd-dirty"
set(OPEN62541_VER_COMMIT "unknown-commit")
@ -1009,7 +1009,7 @@ set(UA_NODESET_DIR ${PROJECT_SOURCE_DIR}/deps/ua-nodeset CACHE STRING "The path
if(UA_NAMESPACE_ZERO STREQUAL "FULL")
# Use the "full" schema files also for datatypes and statuscodes
set(UA_SCHEMA_DIR ${UA_NODESET_DIR}/Schema)
set(UA_SCHEMA_DIR ${UA_NODESET_DIR}/Schema CACHE INTERNAL "")
# Set the full Nodeset for NS0
if(NOT UA_FILE_NS0)
@ -1028,7 +1028,7 @@ if(UA_NAMESPACE_ZERO STREQUAL "FULL")
endif()
else()
# Directory with the schema files for installation
set(UA_SCHEMA_DIR ${PROJECT_SOURCE_DIR}/tools/schema)
set(UA_SCHEMA_DIR ${PROJECT_SOURCE_DIR}/tools/schema CACHE INTERNAL "")
# Set the reduced Nodeset for NS0
if(NOT UA_FILE_NS0)
@ -1374,7 +1374,12 @@ foreach(lib ${open62541_PUBLIC_LIBRARIES})
set(pkgcfgpubliclibs "${pkgcfgpubliclibs}-l${lib} ")
endforeach()
string(REPLACE ";" ", " pkgcfglibs "${open62541_LIBRARIES}")
if(BUILD_SHARED_LIBS)
foreach(lib ${open62541_LIBRARIES})
set(pkgcfgpubliclibs "${pkgcfgpubliclibs}-l${lib} ")
endforeach()
endif()
configure_file(tools/open62541.pc.in ${PROJECT_BINARY_DIR}/src_generated/open62541.pc @ONLY)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
@ -1382,6 +1387,7 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
endif()
# Install nodeset compiler
install(DIRECTORY "tools/certs" "tools/nodeset_compiler"
DESTINATION ${open62541_install_tools_dir}
FILES_MATCHING
@ -1391,6 +1397,7 @@ install(DIRECTORY "tools/certs" "tools/nodeset_compiler"
PATTERN ".git*" EXCLUDE
PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE)
# Install schemas
# Trailing slash to prevent "schema/schema" nesting
install(DIRECTORY ${UA_SCHEMA_DIR}/
DESTINATION ${open62541_install_schema_dir}
@ -1401,6 +1408,7 @@ install(DIRECTORY ${UA_SCHEMA_DIR}/
PATTERN "NodeIds.csv"
PERMISSIONS OWNER_READ GROUP_READ)
# Install python tools
set(UA_install_tools_files "tools/generate_datatypes.py"
"tools/generate_nodeid_header.py"
"tools/generate_statuscode_descriptions.py")

View File

@ -384,21 +384,6 @@ TCP_registerListenSocket(UA_POSIXConnectionManager *pcm, struct addrinfo *ai,
return UA_STATUSCODE_BADINTERNALERROR;
}
/* If the INADDR_ANY is used, use the local hostname */
char hoststr[UA_MAXHOSTNAME_LENGTH];
if(hostname) {
UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
"TCP %u\t| Creating listen socket for \"%s\" on port %u",
(unsigned)listenSocket, hostname, port);
} else {
gethostname(hoststr, UA_MAXHOSTNAME_LENGTH);
hostname = hoststr;
UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
"TCP %u\t| Creating listen socket for \"%s\" "
"(with local hostname \"%s\") on port %u",
(unsigned)listenSocket, addrstr, hostname, port);
}
/* Some Linux distributions have net.ipv6.bindv6only not activated. So
* sockets can double-bind to IPv4 and IPv6. This leads to problems. Use
* AF_INET6 sockets only for IPv6. */
@ -445,6 +430,31 @@ TCP_registerListenSocket(UA_POSIXConnectionManager *pcm, struct addrinfo *ai,
/* Bind socket to address */
int ret = bind(listenSocket, ai->ai_addr, (socklen_t)ai->ai_addrlen);
/* Get the port being used if dynamic porting was used */
if(port == 0) {
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
socklen_t len = sizeof(sin);
getsockname(listenSocket, (struct sockaddr *)&sin, &len);
port = ntohs(sin.sin_port);
}
/* If the INADDR_ANY is used, use the local hostname */
char hoststr[UA_MAXHOSTNAME_LENGTH];
if(hostname) {
UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
"TCP %u\t| Creating listen socket for \"%s\" on port %u",
(unsigned)listenSocket, hostname, port);
} else {
gethostname(hoststr, UA_MAXHOSTNAME_LENGTH);
hostname = hoststr;
UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
"TCP %u\t| Creating listen socket for \"%s\" "
"(with local hostname \"%s\") on port %u",
(unsigned)listenSocket, addrstr, hostname, port);
}
if(ret < 0) {
UA_LOG_SOCKET_ERRNO_WRAP(
UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,

View File

@ -771,10 +771,6 @@ UDP_registerListenSocket(UA_POSIXConnectionManager *pcm, UA_UInt16 port,
return UA_STATUSCODE_BADCONNECTIONREJECTED;
}
UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
"UDP %u\t| New listen socket for \"%s\" on port %u",
(unsigned)listenSocket, hoststr, port);
/* Set the socket configuration per the parameters */
UA_StatusCode res =
setConnectionConfig(listenSocket, params,
@ -816,6 +812,20 @@ UDP_registerListenSocket(UA_POSIXConnectionManager *pcm, UA_UInt16 port,
#else
int ret = bind(listenSocket, info->ai_addr, (socklen_t)info->ai_addrlen);
#endif
/* Get the port being used if dynamic porting was used */
if(port == 0) {
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
socklen_t len = sizeof(sin);
getsockname(listenSocket, (struct sockaddr *)&sin, &len);
port = ntohs(sin.sin_port);
}
UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
"UDP %u\t| New listen socket for \"%s\" on port %u",
(unsigned)listenSocket, hoststr, port);
if(ret < 0) {
UA_LOG_SOCKET_ERRNO_WRAP(
UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,

View File

@ -241,7 +241,7 @@ int main(int argc, char **argv) {
config->mdnsConfig.mdnsServerName = UA_String_fromChars("Sample-Multicast-Server");
//setting custom outbound interface
config->mdnsInterfaceIP = UA_String_fromChars("42.42.42.42"); //this line will produce an error and set the interface to 0.0.0.0
config->mdnsInterfaceIP = UA_String_fromChars("0.0.0.0");
// See http://www.opcfoundation.org/UA/schemas/1.03/ServerCapabilities.csv
// For a LDS server, you should only indicate the LDS capability.

View File

@ -442,7 +442,7 @@ UA_Client_connectSecureChannelAsync(UA_Client *client, const char *endpointUrl)
cc->endpointUrl = UA_STRING_ALLOC(endpointUrl);
/* Connect */
return __UA_Client_connect(client, false);
return __UA_Client_connect(client, true);
})
/* Connect to the server and create+activate a Session with the given username

View File

@ -136,11 +136,13 @@
# define _BSD_SOURCE
# endif
/* Define _GNU_SOURCE to get functions like ppoll. Comment this out to
/* Define _GNU_SOURCE to get functions like poll. Comment this out to
* only use standard POSIX definitions. */
# ifndef _GNU_SOURCE
# define _GNU_SOURCE
# endif
#define UA_HAS_GETIFADDR 1
#endif
/**

View File

@ -119,16 +119,16 @@ typedef uint64_t UA_UInt64;
* ^^^^^
* An IEEE single precision (32 bit) floating point value. */
typedef float UA_Float;
#define UA_FLOAT_MIN FLT_MIN;
#define UA_FLOAT_MAX FLT_MAX;
#define UA_FLOAT_MIN FLT_MIN
#define UA_FLOAT_MAX FLT_MAX
/**
* Double
* ^^^^^^
* An IEEE double precision (64 bit) floating point value. */
typedef double UA_Double;
#define UA_DOUBLE_MIN DBL_MIN;
#define UA_DOUBLE_MAX DBL_MAX;
#define UA_DOUBLE_MIN DBL_MIN
#define UA_DOUBLE_MAX DBL_MAX
/**
* .. _statuscode:

View File

@ -28,8 +28,9 @@ UA_ConnectionConfig UA_ConnectionConfig_default;
/* Creates a new server config with one endpoint and custom buffer size.
*
* The config will set the tcp network layer to the given port and adds a single
* endpoint with the security policy ``SecurityPolicy#None`` to the server. A
* server certificate may be supplied but is optional.
* endpoint with the security policy ``SecurityPolicy#None`` to the server.
* If the port is set to 0, it will be dynamically assigned.
* A server certificate may be supplied but is optional.
* Additionally you can define a custom buffer size for send and receive buffer.
*
* @param portNumber The port number for the tcp network layer
@ -124,6 +125,7 @@ UA_ServerConfig_setBasics(UA_ServerConfig *conf);
* Use the various UA_ServerConfig_addXxx functions to add them.
* The config will set the tcp network layer to the given port if the
* eventloop is not already set.
* If the port is set to 0, it will be dynamically assigned.
*
* @param conf The configuration to manipulate
* @param portNumber The port number for the tcp network layer

View File

@ -387,34 +387,34 @@ setDefaultConfig(UA_ServerConfig *conf, UA_UInt16 portNumber) {
if(portNumber == 0) {
UA_LOG_WARNING(conf->logging, UA_LOGCATEGORY_USERLAND,
"Cannot set the ServerUrl with a zero port");
} else {
if(conf->serverUrlsSize > 0) {
UA_LOG_WARNING(conf->logging, UA_LOGCATEGORY_USERLAND,
"ServerUrls already set. Overriding.");
UA_Array_delete(conf->serverUrls, conf->serverUrlsSize,
&UA_TYPES[UA_TYPES_STRING]);
conf->serverUrls = NULL;
conf->serverUrlsSize = 0;
}
/* Listen on all interfaces (also external). This must be the first
* entry if this is desired. Otherwise some interfaces may be blocked
* (already in use) with a hostname that is only locally reachable.*/
mp_snprintf(serverUrlBuffer[0], sizeof(serverUrlBuffer[0]),
"opc.tcp://:%u", portNumber);
serverUrls[serverUrlsSize] = UA_STRING(serverUrlBuffer[0]);
serverUrlsSize++;
/* Add to the config */
UA_StatusCode retval =
UA_Array_copy(serverUrls, serverUrlsSize,
(void**)&conf->serverUrls, &UA_TYPES[UA_TYPES_STRING]);
if(retval != UA_STATUSCODE_GOOD)
return retval;
conf->serverUrlsSize = serverUrlsSize;
"Dynamic port assignment will be used.");
}
if(conf->serverUrlsSize > 0) {
UA_LOG_WARNING(conf->logging, UA_LOGCATEGORY_USERLAND,
"ServerUrls already set. Overriding.");
UA_Array_delete(conf->serverUrls, conf->serverUrlsSize,
&UA_TYPES[UA_TYPES_STRING]);
conf->serverUrls = NULL;
conf->serverUrlsSize = 0;
}
/* Listen on all interfaces (also external). This must be the first
* entry if this is desired. Otherwise some interfaces may be blocked
* (already in use) with a hostname that is only locally reachable.*/
mp_snprintf(serverUrlBuffer[0], sizeof(serverUrlBuffer[0]),
"opc.tcp://:%u", portNumber);
serverUrls[serverUrlsSize] = UA_STRING(serverUrlBuffer[0]);
serverUrlsSize++;
/* Add to the config */
UA_StatusCode retval =
UA_Array_copy(serverUrls, serverUrlsSize,
(void**)&conf->serverUrls, &UA_TYPES[UA_TYPES_STRING]);
if(retval != UA_STATUSCODE_GOOD)
return retval;
conf->serverUrlsSize = serverUrlsSize;
/* Endpoints */
/* conf->endpoints = {0, NULL}; */

View File

@ -920,12 +920,12 @@ responseGetEndpoints(UA_Client *client, void *userdata,
}
/* We have found a matching endpoint.
* But does it contain a matching user token policy? */
* But maybe not a amtching user token policy. */
bestEndpointIndex = i;
/* Look for a supported user token policy */
/* Compare the available UserTokenPolicies */
for(size_t j = 0; j < endpoint->userIdentityTokensSize; ++j) {
UA_UserTokenPolicy* tokenPolicy = &endpoint->userIdentityTokens[j];
UA_UserTokenPolicy *tokenPolicy = &endpoint->userIdentityTokens[j];
const UA_DataType *tokenType =
client->config.userIdentityToken.content.decoded.type;
@ -998,19 +998,8 @@ responseGetEndpoints(UA_Client *client, void *userdata,
bestEndpointLevel = endpoint->securityLevel;
bestTokenIndex = j;
/* Move to the client config */
UA_EndpointDescription_clear(&client->config.endpoint);
client->config.endpoint = *endpoint;
UA_EndpointDescription_init(endpoint);
UA_UserTokenPolicy_clear(&client->config.userTokenPolicy);
client->config.userTokenPolicy = *tokenPolicy;
UA_UserTokenPolicy_init(tokenPolicy);
/* Store the Server Description */
UA_ApplicationDescription_clear(&client->serverDescription);
UA_ApplicationDescription_copy(&endpoint->server,
&client->serverDescription);
/* Stop search for the UserTokenPolicy. But we go on searching for
* an endpoints with a better security level. */
break;
}
}
@ -1022,7 +1011,9 @@ responseGetEndpoints(UA_Client *client, void *userdata,
closeSecureChannel(client);
UA_UNLOCK(&client->clientMutex);
return;
} else if(bestTokenIndex == notFound) {
}
if(!client->config.noSession && bestTokenIndex == notFound) {
UA_LOG_ERROR(client->config.logging, UA_LOGCATEGORY_CLIENT,
"No suitable UserTokenPolicy found for the possible endpoints");
client->connectStatus = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
@ -1031,27 +1022,43 @@ responseGetEndpoints(UA_Client *client, void *userdata,
return;
}
/* Log the selected Endpoint and UserTokenPolicy */
#if UA_LOGLEVEL <= 300
UA_EndpointDescription *endpoint = &client->config.endpoint;
UA_UserTokenPolicy *tokenPolicy = &client->config.userTokenPolicy;
const char *securityModeNames[3] = {"None", "Sign", "SignAndEncrypt"};
const char *userTokenTypeNames[4] = {"Anonymous", "UserName", "Certificate", "IssuedToken"};
/* Move the application description and endpoint information to the client config */
UA_EndpointDescription *endpoint = &resp->endpoints[bestEndpointIndex];
UA_ApplicationDescription_clear(&client->serverDescription);
UA_ApplicationDescription_copy(&endpoint->server, &client->serverDescription);
UA_EndpointDescription_clear(&client->config.endpoint);
client->config.endpoint = *endpoint;
#if UA_LOGLEVEL <= 300
const char *securityModeNames[3] = {"None", "Sign", "SignAndEncrypt"};
UA_LOG_INFO(client->config.logging, UA_LOGCATEGORY_CLIENT,
"Selected endpoint %lu in URL %S with SecurityMode "
"%s and SecurityPolicy %S",
(long unsigned)bestEndpointIndex, endpoint->endpointUrl,
securityModeNames[endpoint->securityMode - 1],
endpoint->securityPolicyUri);
UA_LOG_INFO(client->config.logging, UA_LOGCATEGORY_CLIENT,
"Selected UserTokenPolicy %S with UserTokenType %s "
"and SecurityPolicy %S", tokenPolicy->policyId,
userTokenTypeNames[tokenPolicy->tokenType],
tokenPolicy->securityPolicyUri);
#endif
/* Move the UserTokenPolicy information to the client config */
if(bestTokenIndex != notFound) {
UA_UserTokenPolicy *tokenPolicy = &endpoint->userIdentityTokens[bestTokenIndex];
UA_assert(tokenPolicy);
#if UA_LOGLEVEL <= 300
const char *userTokenTypeNames[4] = {"Anonymous", "UserName", "Certificate", "IssuedToken"};
UA_LOG_INFO(client->config.logging, UA_LOGCATEGORY_CLIENT,
"Selected UserTokenPolicy %S with UserTokenType %s "
"and SecurityPolicy %S", tokenPolicy->policyId,
userTokenTypeNames[tokenPolicy->tokenType],
tokenPolicy->securityPolicyUri);
#endif
UA_UserTokenPolicy_clear(&client->config.userTokenPolicy);
client->config.userTokenPolicy = *tokenPolicy;
UA_UserTokenPolicy_init(tokenPolicy);
}
/* Don't clean up later -- was moved to the client config */
UA_EndpointDescription_init(endpoint);
/* Close the SecureChannel -- a different SecurityMode or SecurityPolicy is
* defined by the Endpoint. */
if(client->config.endpoint.securityMode != client->channel.securityMode ||

View File

@ -13,6 +13,7 @@
#include <open62541/client_highlevel.h>
#include <open62541/client_highlevel_async.h>
#include "util/ua_util_internal.h"
/* The highlevel client API is an "outer onion layer". This file does not
* include ua_client_internal.h on purpose. */
@ -873,13 +874,21 @@ AttributeReadCallback(UA_Client *client, void *userdata,
goto finish;
}
/* Check we have a scalar value of the right datatype */
if(!dv->hasValue ||
!UA_Variant_hasScalarType(&dv->value, ctx->resultType)) {
/* Check we have a value */
if(!dv->hasValue) {
res = UA_STATUSCODE_BADINTERNALERROR;
goto finish;
}
/* Check the type. Try to adjust "in situ" if no match. */
if(!UA_Variant_hasScalarType(&dv->value, ctx->resultType)) {
adjustType(&dv->value, ctx->resultType);
if(!UA_Variant_hasScalarType(&dv->value, ctx->resultType)) {
res = UA_STATUSCODE_BADINTERNALERROR;
goto finish;
}
}
/* Callback into userland */
ctx->userCallback(client, ctx->userContext, requestId,
UA_STATUSCODE_GOOD, dv->value.data);

View File

@ -543,7 +543,7 @@ UA_ReaderGroup_freezeConfiguration(UA_Server *server, UA_ReaderGroup *rg) {
if(dsrCount > 1) {
UA_LOG_WARNING_READERGROUP(server->config.logging, rg,
"Mutiple DSR in a readerGroup not supported in RT "
"Multiple DSR in a readerGroup not supported in RT "
"fixed size configuration");
return UA_STATUSCODE_BADNOTIMPLEMENTED;
}

View File

@ -581,7 +581,7 @@ mdns_set_address_record(UA_DiscoveryManager *dm, const char *fullServiceDomain,
struct ifaddrs *ifaddr;
struct ifaddrs *ifa;
if(getifaddrs(&ifaddr) == -1) {
UA_LOG_ERROR(dm->logging, UA_LOGCATEGORY_SERVER,
UA_LOG_ERROR(dm->server->config.logging, UA_LOGCATEGORY_SERVER,
"getifaddrs returned an unexpected error. Not setting mDNS A records.");
return;
}
@ -1238,9 +1238,14 @@ UA_Discovery_addRecord(UA_DiscoveryManager *dm, const UA_String servername,
listEntry->txtSet = true;
UA_STACKARRAY(char, newUrl, 10 + hostname.length + 8 + path.length + 1);
mp_snprintf(newUrl, 10 + hostname.length + 8 + path.length + 1,
"opc.tcp://%S:%d%s%S", hostname, port, path.length > 0 ? "/" : "", path);
const size_t newUrlSize = 10 + hostname.length + 8 + path.length + 1;
UA_STACKARRAY(char, newUrl, newUrlSize);
memset(newUrl, 0, newUrlSize);
if(path.length > 0) {
mp_snprintf(newUrl, newUrlSize, "opc.tcp://%S:%d/%S", hostname, port, path);
} else {
mp_snprintf(newUrl, newUrlSize, "opc.tcp://%S:%d", hostname, port);
}
listEntry->serverOnNetwork.discoveryUrl = UA_String_fromChars(newUrl);
listEntry->srvSet = true;
}

View File

@ -903,6 +903,8 @@ UA_Server_run_iterate(UA_Server *server, UA_Boolean waitInternal) {
/* Return the time until the next scheduled callback */
UA_DateTime now = el->dateTime_nowMonotonic(el);
UA_DateTime nextTimeout = (el->nextCyclicTime(el) - now) / UA_DATETIME_MSEC;
if(nextTimeout < 0)
nextTimeout = 0;
if(nextTimeout > UA_UINT16_MAX)
nextTimeout = UA_UINT16_MAX;
return (UA_UInt16)nextTimeout;

View File

@ -800,14 +800,6 @@ UA_Server_readObjectProperty(UA_Server *server, const UA_NodeId objectId,
/* Type Checking */
/*****************/
static UA_DataTypeKind
typeEquivalence(const UA_DataType *t) {
UA_DataTypeKind k = (UA_DataTypeKind)t->typeKind;
if(k == UA_DATATYPEKIND_ENUM)
return UA_DATATYPEKIND_INT32;
return k;
}
UA_Boolean
compatibleValueDataType(UA_Server *server, const UA_DataType *dataType,
const UA_NodeId *constraintDataType) {
@ -1150,41 +1142,21 @@ adjustValueType(UA_Server *server, UA_Variant *value,
if(!type)
return;
/* Unwrap ExtensionObject arrays if they all contain the same DataType */
unwrapEOArray(server, value);
/* The target type is already achieved. No adjustment needed. */
if(UA_NodeId_equal(&type->typeId, targetDataTypeId))
return;
/* Unwrap ExtensionObject arrays if they all contain the same DataType */
unwrapEOArray(server, value);
/* Find the target type */
const UA_DataType *targetType =
UA_findDataTypeWithCustom(targetDataTypeId, server->config.customDataTypes);
if(!targetType)
return;
/* A string is written to a byte array. the valuerank and array dimensions
* are checked later */
if(targetType == &UA_TYPES[UA_TYPES_BYTE] &&
type == &UA_TYPES[UA_TYPES_BYTESTRING] &&
UA_Variant_isScalar(value)) {
UA_ByteString *str = (UA_ByteString*)value->data;
value->type = &UA_TYPES[UA_TYPES_BYTE];
value->arrayLength = str->length;
value->data = str->data;
return;
}
/* An enum was sent as an int32, or an opaque type as a bytestring. This
* is detected with the typeKind indicating the "true" datatype. */
UA_DataTypeKind te1 = typeEquivalence(targetType);
UA_DataTypeKind te2 = typeEquivalence(type);
if(te1 == te2 && te1 <= UA_DATATYPEKIND_ENUM) {
value->type = targetType;
return;
}
/* Add more possible type adjustments here. What are they? */
/* Use the generic functionality shared by client and server */
adjustType(value, targetType);
}
static UA_StatusCode

View File

@ -54,6 +54,47 @@ UA_AttributeId_fromName(const UA_String name) {
return UA_ATTRIBUTEID_INVALID;
}
static UA_DataTypeKind
typeEquivalence(const UA_DataType *t) {
UA_DataTypeKind k = (UA_DataTypeKind)t->typeKind;
if(k == UA_DATATYPEKIND_ENUM)
return UA_DATATYPEKIND_INT32;
return k;
}
void
adjustType(UA_Variant *value, const UA_DataType *targetType) {
/* If the value is empty, there is nothing we can do here */
const UA_DataType *type = value->type;
if(!type || !targetType)
return;
/* A string is written to a byte array. the valuerank and array dimensions
* are checked later */
if(targetType == &UA_TYPES[UA_TYPES_BYTE] &&
type == &UA_TYPES[UA_TYPES_BYTESTRING] &&
UA_Variant_isScalar(value)) {
UA_ByteString *str = (UA_ByteString*)value->data;
value->type = &UA_TYPES[UA_TYPES_BYTE];
value->arrayLength = str->length;
value->data = str->data;
if(value->storageType != UA_VARIANT_DATA_NODELETE)
UA_free(str);
return;
}
/* An enum was sent as an int32, or an opaque type as a bytestring. This
* is detected with the typeKind indicating the "true" datatype. */
UA_DataTypeKind te1 = typeEquivalence(targetType);
UA_DataTypeKind te2 = typeEquivalence(type);
if(te1 == te2 && te1 <= UA_DATATYPEKIND_ENUM) {
value->type = targetType;
return;
}
/* Add more possible type adjustments here. What are they? */
}
size_t
UA_readNumberWithBase(const UA_Byte *buf, size_t buflen, UA_UInt32 *number, UA_Byte base) {
UA_assert(buf);

View File

@ -27,6 +27,11 @@ _UA_BEGIN_DECLS
/* Macro-Expand for MSVC workarounds */
#define UA_MACRO_EXPAND(x) x
/* Try if the type of the value can be adjusted "in situ" to the target type.
* That can be done, for example, to map between int32 and an enum. */
void
adjustType(UA_Variant *value, const UA_DataType *targetType);
/* Short names for integer. These are not exposed on the public API, since many
* user-applications make the same definitions in their headers. */
typedef UA_Byte u8;

View File

@ -138,6 +138,37 @@ START_TEST(Client_read_async) {
UA_Client_delete(client);
} END_TEST
static void
asyncReadNodeClassAttributeCallback(UA_Client *client, void *userdata,
UA_UInt32 requestId, UA_StatusCode status,
UA_NodeClass *nodeClass) {
ck_assert_uint_eq(status, UA_STATUSCODE_GOOD);
UA_UInt16 *asyncCounter = (UA_UInt16*)userdata;
(*asyncCounter)++;
}
START_TEST(Client_readNodeClass_async) {
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
UA_UInt16 asyncCounter = 0;
UA_NodeId cTimeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
retval = UA_Client_readNodeClassAttribute_async(client, cTimeId,
asyncReadNodeClassAttributeCallback,
&asyncCounter, NULL);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Process async responses during 1s */
while(asyncCounter == 0)
retval |= UA_Client_run_iterate(client, 999);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
UA_Client_disconnect(client);
UA_Client_delete(client);
} END_TEST
START_TEST(Client_read_async_timed) {
UA_Client *client = UA_Client_newForUnitTest();
UA_ClientConfig *clientConfig = UA_Client_getConfig(client);
@ -242,6 +273,7 @@ static Suite* testSuite_Client(void) {
TCase *tc_client = tcase_create("Client Basic");
tcase_add_checked_fixture(tc_client, setup, teardown);
tcase_add_test(tc_client, Client_read_async);
tcase_add_test(tc_client, Client_readNodeClass_async);
tcase_add_test(tc_client, Client_read_async_timed);
tcase_add_test(tc_client, Client_connectivity_check);
tcase_add_test(tc_client, Client_highlevel_async_readValue);

View File

@ -19,20 +19,10 @@ else()
find_library(MBEDCRYPTO_LIBRARY mbedcrypto HINTS ${MBEDTLS_FOLDER_LIBRARY})
endif()
if (NOT TARGET mbedtls::mbedtls)
# Only add library if not yet added. If project referenced multiple times, the target is also created multiple times.
add_library(mbedtls::mbedtls UNKNOWN IMPORTED)
set_property(TARGET mbedtls::mbedtls PROPERTY IMPORTED_LOCATION "${MBEDTLS_LIBRARY}")
add_library(mbedtls::mbedx509 UNKNOWN IMPORTED)
set_property(TARGET mbedtls::mbedx509 PROPERTY IMPORTED_LOCATION "${MBEDX509_LIBRARY}")
add_library(mbedtls::mbedcrypto UNKNOWN IMPORTED)
set_property(TARGET mbedtls::mbedcrypto PROPERTY IMPORTED_LOCATION "${MBEDCRYPTO_LIBRARY}")
endif()
set(MBEDTLS_LIBRARIES mbedtls::mbedtls mbedtls::mbedx509 mbedtls::mbedcrypto)
set(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY} ${MBEDX509_LIBRARY} ${MBEDCRYPTO_LIBRARY})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MbedTLS DEFAULT_MSG
MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)
find_package_handle_standard_args(MbedTLS DEFAULT_MSG MBEDTLS_INCLUDE_DIRS
MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)
mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)

View File

@ -6,5 +6,4 @@ Name: open62541
Description: open62541 is an open source C (C99) implementation of OPC UA
Version: @OPEN62541_VER_MAJOR@.@OPEN62541_VER_MINOR@.@OPEN62541_VER_PATCH@@OPEN62541_VER_LABEL@
Libs: -L${libdir} -lopen62541 @pkgcfgpubliclibs@
Requires.private: @pkgcfglibs@
Cflags: -I${includedir}