#include "ua_pubsub_internal.h"
#ifdef UA_ENABLE_PUBSUB
static UA_Boolean
UA_PubSubConnection_canConnect(UA_PubSubConnection *c);
static UA_StatusCode
UA_PubSubConnection_connect(UA_PubSubManager *psm, UA_PubSubConnection *c,
UA_Boolean validate);
static void
UA_PubSubConnection_process(UA_PubSubManager *psm, UA_PubSubConnection *c,
const UA_ByteString msg);
static void
UA_PubSubConnection_disconnect(UA_PubSubConnection *c);
UA_StatusCode
UA_PubSubConnectionConfig_copy(const UA_PubSubConnectionConfig *src,
UA_PubSubConnectionConfig *dst) {
UA_StatusCode res = UA_STATUSCODE_GOOD;
memcpy(dst, src, sizeof(UA_PubSubConnectionConfig));
res |= UA_PublisherId_copy(&src->publisherId, &dst->publisherId);
res |= UA_String_copy(&src->name, &dst->name);
res |= UA_Variant_copy(&src->address, &dst->address);
res |= UA_String_copy(&src->transportProfileUri, &dst->transportProfileUri);
res |= UA_Variant_copy(&src->connectionTransportSettings,
&dst->connectionTransportSettings);
res |= UA_KeyValueMap_copy(&src->connectionProperties,
&dst->connectionProperties);
if(res != UA_STATUSCODE_GOOD)
UA_PubSubConnectionConfig_clear(dst);
return res;
}
UA_PubSubConnection *
UA_PubSubConnection_find(UA_PubSubManager *psm, const UA_NodeId id) {
if(!psm)
return NULL;
UA_PubSubConnection *c;
TAILQ_FOREACH(c, &psm->connections, listEntry) {
if(UA_NodeId_equal(&id, &c->head.identifier))
break;
}
return c;
}
void
UA_PubSubConnectionConfig_clear(UA_PubSubConnectionConfig *connectionConfig) {
UA_PublisherId_clear(&connectionConfig->publisherId);
UA_String_clear(&connectionConfig->name);
UA_String_clear(&connectionConfig->transportProfileUri);
UA_Variant_clear(&connectionConfig->connectionTransportSettings);
UA_Variant_clear(&connectionConfig->address);
UA_KeyValueMap_clear(&connectionConfig->connectionProperties);
}
UA_StatusCode
UA_PubSubConnection_create(UA_PubSubManager *psm, const UA_PubSubConnectionConfig *cc,
UA_NodeId *cId) {
UA_PubSubConnection *c = (UA_PubSubConnection*)
UA_calloc(1, sizeof(UA_PubSubConnection));
if(!c)
return UA_STATUSCODE_BADOUTOFMEMORY;
c->head.componentType = UA_PUBSUBCOMPONENT_CONNECTION;
UA_StatusCode ret = UA_PubSubConnectionConfig_copy(cc, &c->config);
UA_CHECK_STATUS(ret, UA_free(c); return ret);
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
addPubSubConnectionRepresentation(psm->sc.server, c);
#else
UA_PubSubManager_generateUniqueNodeId(psm, &c->head.identifier);
#endif
TAILQ_INSERT_HEAD(&psm->connections, c, listEntry);
psm->connectionsSize++;
ret = UA_PubSubConnection_connect(psm, c, true);
if(ret != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(psm->logging, UA_LOGCATEGORY_PUBSUB,
"Could not create the PubSubConnection. "
"The connection parameters did not validate.");
UA_PubSubComponent_freeWithoutLifecycleCallback(
psm, c, UA_PUBSUBCOMPONENT_CONNECTION);
return ret;
}
char tmpLogIdStr[128];
mp_snprintf(tmpLogIdStr, 128, "PubSubConnection %N\t| ", c->head.identifier);
c->head.logIdString = UA_STRING_ALLOC(tmpLogIdStr);
UA_Server *server = psm->sc.server;
if(server->config.pubSubConfig.componentLifecycleCallback) {
UA_StatusCode res = server->config.pubSubConfig.
componentLifecycleCallback(server, c->head.identifier,
UA_PUBSUBCOMPONENT_CONNECTION, false);
if(res != UA_STATUSCODE_GOOD) {
UA_PubSubComponent_freeWithoutLifecycleCallback(
psm, c, UA_PUBSUBCOMPONENT_CONNECTION);
return res;
}
}
UA_LOG_INFO_PUBSUB(psm->logging, c, "Connection created (State: %s)",
UA_PubSubState_name(c->head.state));
if(cId)
UA_NodeId_copy(&c->head.identifier, cId);
if(cc->enabled)
UA_PubSubConnection_setPubSubState(psm, c, UA_PUBSUBSTATE_OPERATIONAL);
return UA_STATUSCODE_GOOD;
}
static void
delayedPubSubConnection_delete(void *application, void *context) {
UA_PubSubManager *psm = (UA_PubSubManager*)application;
UA_Server *server = psm->sc.server;
UA_PubSubConnection *c = (UA_PubSubConnection*)context;
lockServer(server);
UA_PubSubConnection_delete(psm, c);
unlockServer(server);
}
UA_StatusCode
UA_PubSubConnection_delete(UA_PubSubManager *psm, UA_PubSubConnection *c) {
UA_LOCK_ASSERT(&psm->sc.server->serviceMutex);
UA_Server *server = psm->sc.server;
if(server->config.pubSubConfig.componentLifecycleCallback) {
UA_StatusCode res = server->config.pubSubConfig.
componentLifecycleCallback(server, c->head.identifier,
UA_PUBSUBCOMPONENT_CONNECTION, true);
if(res != UA_STATUSCODE_GOOD)
return res;
}
c->deleteFlag = true;
UA_PubSubConnection_setPubSubState(psm, c, UA_PUBSUBSTATE_DISABLED);
UA_ReaderGroup *rg, *tmpRg;
LIST_FOREACH(rg, &c->readerGroups, listEntry) {
UA_ReaderGroup_setPubSubState(psm, rg, UA_PUBSUBSTATE_DISABLED);
}
UA_WriterGroup *wg, *tmpWg;
LIST_FOREACH(wg, &c->writerGroups, listEntry) {
UA_WriterGroup_setPubSubState(psm, wg, UA_PUBSUBSTATE_DISABLED);
}
LIST_FOREACH_SAFE(rg, &c->readerGroups, listEntry, tmpRg) {
UA_ReaderGroup_remove(psm, rg);
}
LIST_FOREACH_SAFE(wg, &c->writerGroups, listEntry, tmpWg) {
UA_WriterGroup_remove(psm, wg);
}
if(c->sendChannel != 0 || c->recvChannelsSize > 0)
return UA_STATUSCODE_BADINTERNALERROR;
if(!LIST_EMPTY(&c->writerGroups) || !LIST_EMPTY(&c->readerGroups)) {
UA_EventLoop *el = psm->sc.server->config.eventLoop;
c->dc.callback = delayedPubSubConnection_delete;
c->dc.application = psm;
c->dc.context = c;
el->addDelayedCallback(el, &c->dc);
return UA_STATUSCODE_BADINTERNALERROR;
}
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
deleteNode(psm->sc.server, c->head.identifier, true);
#endif
TAILQ_REMOVE(&psm->connections, c, listEntry);
psm->connectionsSize--;
UA_LOG_INFO_PUBSUB(psm->logging, c, "Connection deleted");
UA_PubSubConnectionConfig_clear(&c->config);
UA_PubSubComponentHead_clear(&c->head);
UA_free(c);
return UA_STATUSCODE_GOOD;
}
static void
UA_PubSubConnection_process(UA_PubSubManager *psm, UA_PubSubConnection *c,
const UA_ByteString msg) {
UA_LOG_TRACE_PUBSUB(psm->logging, c, "Processing a received buffer");
#ifdef UA_DEBUG_DUMP_PKGS
UA_dump_hex_pkg(msg.data, msg.length);
#endif
UA_Boolean processed = false;
UA_NetworkMessage nm;
memset(&nm, 0, sizeof(UA_NetworkMessage));
UA_ReaderGroup *rg;
UA_StatusCode res = UA_STATUSCODE_BADNOTFOUND;
LIST_FOREACH(rg, &c->readerGroups, listEntry) {
if(rg->head.state != UA_PUBSUBSTATE_OPERATIONAL &&
rg->head.state != UA_PUBSUBSTATE_PREOPERATIONAL)
continue;
if(rg->config.encodingMimeType == UA_PUBSUB_ENCODING_UADP) {
res = UA_ReaderGroup_decodeNetworkMessage(psm, rg, msg, &nm);
} else {
#ifdef UA_ENABLE_JSON_ENCODING
res = UA_ReaderGroup_decodeNetworkMessageJSON(psm, rg, msg, &nm);
#else
res = UA_STATUSCODE_BADNOTSUPPORTED;
UA_LOG_WARNING_PUBSUB(psm->logging, c, "JSON support is not activated");
#endif
}
if(res == UA_STATUSCODE_GOOD)
break;
}
if(res != UA_STATUSCODE_GOOD)
goto finish;
LIST_FOREACH(rg, &c->readerGroups, listEntry) {
if(rg->head.state != UA_PUBSUBSTATE_OPERATIONAL &&
rg->head.state != UA_PUBSUBSTATE_PREOPERATIONAL)
continue;
processed |= UA_ReaderGroup_process(psm, rg, &nm);
}
UA_NetworkMessage_clear(&nm);
finish:
if(!processed) {
UA_DateTime nowM = UA_DateTime_nowMonotonic();
if(c->silenceErrorUntil < nowM) {
UA_LOG_WARNING_PUBSUB(psm->logging, c,
"Message received that could not be processed "
"with StatusCode %s. Check PublisherId, "
"WriterGroupId and DatasetWriterId",
UA_StatusCode_name(res));
c->silenceErrorUntil = nowM + (UA_DateTime)(10.0 * UA_DATETIME_SEC);
}
}
}
UA_StatusCode
UA_PubSubConnection_setPubSubState(UA_PubSubManager *psm, UA_PubSubConnection *c,
UA_PubSubState targetState) {
if(c->deleteFlag && targetState != UA_PUBSUBSTATE_DISABLED) {
UA_LOG_WARNING_PUBSUB(psm->logging, c,
"The connection is being deleted. Can only be disabled.");
return UA_STATUSCODE_BADINTERNALERROR;
}
UA_Server *server = psm->sc.server;
if(server->config.pubSubConfig.beforeStateChangeCallback) {
server->config.pubSubConfig.
beforeStateChangeCallback(server, c->head.identifier, &targetState);
}
UA_StatusCode ret = UA_STATUSCODE_GOOD;
UA_PubSubState oldState = c->head.state;
UA_Boolean isTransient = c->head.transientState;
c->head.transientState = true;
if(c->config.customStateMachine) {
ret = c->config.customStateMachine(server, c->head.identifier, c->config.context,
&c->head.state, targetState);
goto finalize_state_machine;
}
switch(targetState) {
case UA_PUBSUBSTATE_ERROR:
case UA_PUBSUBSTATE_DISABLED:
UA_PubSubConnection_disconnect(c);
c->head.state = targetState;
break;
case UA_PUBSUBSTATE_PAUSED:
case UA_PUBSUBSTATE_PREOPERATIONAL:
case UA_PUBSUBSTATE_OPERATIONAL:
if(psm->sc.state != UA_LIFECYCLESTATE_STARTED) {
if(oldState != UA_PUBSUBSTATE_PAUSED) {
UA_LOG_WARNING_PUBSUB(psm->logging, c,
"Cannot enable the connection while the "
"server is not running -> Paused State");
}
c->head.state = UA_PUBSUBSTATE_PAUSED;
UA_PubSubConnection_disconnect(c);
break;
}
c->head.state = UA_PUBSUBSTATE_OPERATIONAL;
if(UA_PubSubConnection_canConnect(c))
ret = UA_PubSubConnection_connect(psm, c, false);
break;
default:
ret = UA_STATUSCODE_BADINTERNALERROR;
break;
}
if(ret != UA_STATUSCODE_GOOD) {
c->head.state = UA_PUBSUBSTATE_ERROR;
UA_PubSubConnection_disconnect(c);
}
finalize_state_machine:
c->head.transientState = isTransient;
if(c->head.transientState)
return ret;
if(c->head.state == oldState)
return ret;
UA_LOG_INFO_PUBSUB(psm->logging, c, "%s -> %s",
UA_PubSubState_name(oldState),
UA_PubSubState_name(c->head.state));
if(server->config.pubSubConfig.stateChangeCallback) {
server->config.pubSubConfig.
stateChangeCallback(server, c->head.identifier, c->head.state, ret);
}
UA_ReaderGroup *rg;
LIST_FOREACH(rg, &c->readerGroups, listEntry) {
if(psm->pubSubInitialSetupMode && rg->config.enabled) {
UA_ReaderGroup_setPubSubState(psm, rg, UA_PUBSUBSTATE_OPERATIONAL);
} else {
UA_ReaderGroup_setPubSubState(psm, rg, rg->head.state);
}
}
UA_WriterGroup *wg;
LIST_FOREACH(wg, &c->writerGroups, listEntry) {
if(psm->pubSubInitialSetupMode && wg->config.enabled) {
UA_WriterGroup_setPubSubState(psm, wg, UA_PUBSUBSTATE_OPERATIONAL);
} else {
UA_WriterGroup_setPubSubState(psm, wg, wg->head.state);
}
}
UA_PubSubManager_setState(psm, psm->sc.state);
return ret;
}
static UA_StatusCode
enablePubSubConnection(UA_PubSubManager *psm, const UA_NodeId connectionId) {
UA_PubSubConnection *c = UA_PubSubConnection_find(psm, connectionId);
return (c) ? UA_PubSubConnection_setPubSubState(psm, c, UA_PUBSUBSTATE_OPERATIONAL)
: UA_STATUSCODE_BADNOTFOUND;
}
static UA_StatusCode
disablePubSubConnection(UA_PubSubManager *psm, const UA_NodeId connectionId) {
UA_PubSubConnection *c = UA_PubSubConnection_find(psm, connectionId);
return (c) ? UA_PubSubConnection_setPubSubState(psm, c, UA_PUBSUBSTATE_DISABLED)
: UA_STATUSCODE_BADNOTFOUND;
}
static UA_StatusCode
UA_PubSubConnection_connectUDP(UA_PubSubManager *psm, UA_PubSubConnection *c,
UA_Boolean validate);
static UA_StatusCode
UA_PubSubConnection_connectETH(UA_PubSubManager *psm, UA_PubSubConnection *c,
UA_Boolean validate);
typedef struct {
UA_String profileURI;
UA_String protocol;
UA_Boolean json;
UA_StatusCode (*connect)(UA_PubSubManager *psm, UA_PubSubConnection *c,
UA_Boolean validate);
} ConnectionProfileMapping;
static ConnectionProfileMapping connectionProfiles[UA_PUBSUB_PROFILES_SIZE] = {
{UA_STRING_STATIC("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp"),
UA_STRING_STATIC("udp"), false, UA_PubSubConnection_connectUDP},
{UA_STRING_STATIC("http://opcfoundation.org/UA-Profile/Transport/pubsub-mqtt-uadp"),
UA_STRING_STATIC("mqtt"), false, NULL},
{UA_STRING_STATIC("http://opcfoundation.org/UA-Profile/Transport/pubsub-mqtt-json"),
UA_STRING_STATIC("mqtt"), true, NULL},
{UA_STRING_STATIC("http://opcfoundation.org/UA-Profile/Transport/pubsub-eth-uadp"),
UA_STRING_STATIC("eth"), false, UA_PubSubConnection_connectETH}
};
static void
UA_PubSubConnection_detachConnection(UA_PubSubManager *psm,
UA_ConnectionManager *cm,
UA_PubSubConnection *c,
uintptr_t connectionId) {
if(c->sendChannel == connectionId) {
UA_LOG_INFO_PUBSUB(psm->logging, c, "Detach send-connection %S %u",
cm->protocol, (unsigned)connectionId);
c->sendChannel = 0;
return;
}
for(size_t i = 0; i < UA_PUBSUB_MAXCHANNELS; i++) {
if(c->recvChannels[i] != connectionId)
continue;
UA_LOG_INFO_PUBSUB(psm->logging, c, "Detach receive-connection %S %u",
cm->protocol, (unsigned)connectionId);
c->recvChannels[i] = 0;
c->recvChannelsSize--;
return;
}
}
static UA_StatusCode
UA_PubSubConnection_attachSendConnection(UA_PubSubManager *psm,
UA_ConnectionManager *cm,
UA_PubSubConnection *c,
uintptr_t connectionId) {
if(c->sendChannel != 0 && c->sendChannel != connectionId)
return UA_STATUSCODE_BADINTERNALERROR;
UA_LOG_INFO_PUBSUB(psm->logging, c, "Attach send-connection %S %u",
cm->protocol, (unsigned)connectionId);
c->sendChannel = connectionId;
return UA_STATUSCODE_GOOD;
}
static UA_StatusCode
UA_PubSubConnection_attachRecvConnection(UA_PubSubManager *psm,
UA_ConnectionManager *cm,
UA_PubSubConnection *c,
uintptr_t connectionId) {
for(size_t i = 0; i < UA_PUBSUB_MAXCHANNELS; i++) {
if(c->recvChannels[i] == connectionId)
return UA_STATUSCODE_GOOD;
}
if(c->recvChannelsSize >= UA_PUBSUB_MAXCHANNELS)
return UA_STATUSCODE_BADINTERNALERROR;
for(size_t i = 0; i < UA_PUBSUB_MAXCHANNELS; i++) {
if(c->recvChannels[i] != 0)
continue;
UA_LOG_INFO_PUBSUB(psm->logging, c, "Attach receive-connection %S %u",
cm->protocol, (unsigned)connectionId);
c->recvChannels[i] = connectionId;
c->recvChannelsSize++;
break;
}
return UA_STATUSCODE_GOOD;
}
static void
UA_PubSubConnection_disconnect(UA_PubSubConnection *c) {
if(!c->cm)
return;
if(c->sendChannel != 0)
c->cm->closeConnection(c->cm, c->sendChannel);
for(size_t i = 0; i < UA_PUBSUB_MAXCHANNELS; i++) {
if(c->recvChannels[i] != 0)
c->cm->closeConnection(c->cm, c->recvChannels[i]);
}
}
static void
PubSubChannelCallback(UA_ConnectionManager *cm, uintptr_t connectionId,
void *application, void **connectionContext,
UA_ConnectionState state, const UA_KeyValueMap *params,
UA_ByteString msg, UA_Boolean recv) {
if(!connectionContext)
return;
UA_PubSubConnection *psc = (UA_PubSubConnection*)*connectionContext;
UA_PubSubManager *psm = (UA_PubSubManager*)application;
UA_Server *server = psm->sc.server;
UA_LOG_TRACE_PUBSUB(psm->logging, psc,
"Connection Callback with state %i", state);
lockServer(server);
if(state == UA_CONNECTIONSTATE_CLOSING) {
UA_PubSubConnection_detachConnection(psm, cm, psc, connectionId);
if(psc->deleteFlag && psc->recvChannelsSize == 0 && psc->sendChannel == 0) {
UA_PubSubConnection_delete(psm, psc);
unlockServer(server);
return;
}
if(psc->head.state == UA_PUBSUBSTATE_OPERATIONAL)
UA_PubSubConnection_connect(psm, psc, false);
UA_PubSubManager_setState(psm, psm->sc.state);
unlockServer(server);
return;
}
UA_StatusCode res = (recv) ?
UA_PubSubConnection_attachRecvConnection(psm, cm, psc, connectionId) :
UA_PubSubConnection_attachSendConnection(psm, cm, psc, connectionId);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_WARNING_PUBSUB(psm->logging, psc,
"No more space for an additional EventLoop connection");
if(psc->cm)
psc->cm->closeConnection(psc->cm, connectionId);
unlockServer(server);
return;
}
UA_PubSubConnection_setPubSubState(psm, psc, psc->head.state);
if(UA_LIKELY(recv && msg.length > 0))
UA_PubSubConnection_process(psm, psc, msg);
unlockServer(server);
}
static void
PubSubRecvChannelCallback(UA_ConnectionManager *cm, uintptr_t connectionId,
void *application, void **connectionContext,
UA_ConnectionState state, const UA_KeyValueMap *params,
UA_ByteString msg) {
PubSubChannelCallback(cm, connectionId, application, connectionContext,
state, params, msg, true);
}
static void
PubSubSendChannelCallback(UA_ConnectionManager *cm, uintptr_t connectionId,
void *application, void **connectionContext,
UA_ConnectionState state, const UA_KeyValueMap *params,
UA_ByteString msg) {
PubSubChannelCallback(cm, connectionId, application, connectionContext,
state, params, msg, false);
}
static UA_StatusCode
UA_PubSubConnection_connectUDP(UA_PubSubManager *psm, UA_PubSubConnection *c,
UA_Boolean validate) {
UA_LOCK_ASSERT(&psm->sc.server->serviceMutex);
UA_NetworkAddressUrlDataType *addressUrl = (UA_NetworkAddressUrlDataType*)
c->config.address.data;
UA_String address;
UA_UInt16 port;
UA_StatusCode res = UA_parseEndpointUrl(&addressUrl->url, &address, &port, NULL);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_PUBSUB(psm->logging, c, "Could not parse the UDP network URL");
return res;
}
UA_String localhostAddr = UA_STRING_STATIC("localhost");
UA_Boolean receive_all =
(address.length == 0) || UA_String_equal(&localhostAddr, &address);
UA_Boolean listen = true;
UA_Boolean reuse = true;
UA_Boolean loopback = true;
UA_KeyValuePair kvp[7];
UA_KeyValueMap kvm = {5, kvp};
kvp[0].key = UA_QUALIFIEDNAME(0, "port");
UA_Variant_setScalar(&kvp[0].value, &port, &UA_TYPES[UA_TYPES_UINT16]);
kvp[1].key = UA_QUALIFIEDNAME(0, "listen");
UA_Variant_setScalar(&kvp[1].value, &listen, &UA_TYPES[UA_TYPES_BOOLEAN]);
kvp[2].key = UA_QUALIFIEDNAME(0, "validate");
UA_Variant_setScalar(&kvp[2].value, &validate, &UA_TYPES[UA_TYPES_BOOLEAN]);
kvp[3].key = UA_QUALIFIEDNAME(0, "reuse");
UA_Variant_setScalar(&kvp[3].value, &reuse, &UA_TYPES[UA_TYPES_BOOLEAN]);
kvp[4].key = UA_QUALIFIEDNAME(0, "loopback");
UA_Variant_setScalar(&kvp[4].value, &loopback, &UA_TYPES[UA_TYPES_BOOLEAN]);
if(!receive_all) {
kvp[5].key = UA_QUALIFIEDNAME(0, "address");
UA_Variant_setScalar(&kvp[5].value, &address, &UA_TYPES[UA_TYPES_STRING]);
kvm.mapSize++;
}
if(!UA_String_isEmpty(&addressUrl->networkInterface)) {
kvp[kvm.mapSize].key = UA_QUALIFIEDNAME(0, "interface");
UA_Variant_setScalar(&kvp[kvm.mapSize].value, &addressUrl->networkInterface,
&UA_TYPES[UA_TYPES_STRING]);
kvm.mapSize++;
}
if(validate || (c->recvChannelsSize == 0 && c->readerGroupsSize > 0)) {
res = c->cm->openConnection(c->cm, &kvm, psm, c, PubSubRecvChannelCallback);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_PUBSUB(psm->logging, c,
"Could not open an UDP channel for receiving");
return res;
}
}
if(receive_all) {
UA_LOG_INFO_PUBSUB(psm->logging, c,
"Localhost address - don't open UDP send connection");
return UA_STATUSCODE_GOOD;
}
if(validate || (c->sendChannel == 0 && c->writerGroupsSize > 0)) {
listen = false;
res = c->cm->openConnection(c->cm, &kvm, psm, c, PubSubSendChannelCallback);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_PUBSUB(psm->logging, c, "Could not open an UDP recv channel");
return res;
}
}
return UA_STATUSCODE_GOOD;
}
static UA_StatusCode
UA_PubSubConnection_connectETH(UA_PubSubManager *psm, UA_PubSubConnection *c,
UA_Boolean validate) {
UA_LOCK_ASSERT(&psm->sc.server->serviceMutex);
UA_NetworkAddressUrlDataType *addressUrl = (UA_NetworkAddressUrlDataType*)
c->config.address.data;
UA_String address;
UA_UInt16 vid = 0;
UA_Byte pcp = 0;
UA_StatusCode res = UA_parseEndpointUrlEthernet(&addressUrl->url, &address, &vid, &pcp);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_PUBSUB(psm->logging, c, "Could not parse the ETH network URL");
return res;
}
UA_Boolean listen = true;
UA_KeyValuePair kvp[7];
UA_KeyValueMap kvm = {7, kvp};
kvp[0].key = UA_QUALIFIEDNAME(0, "address");
UA_Variant_setScalar(&kvp[0].value, &address, &UA_TYPES[UA_TYPES_STRING]);
kvp[1].key = UA_QUALIFIEDNAME(0, "listen");
UA_Variant_setScalar(&kvp[1].value, &listen, &UA_TYPES[UA_TYPES_BOOLEAN]);
kvp[2].key = UA_QUALIFIEDNAME(0, "interface");
UA_Variant_setScalar(&kvp[2].value, &addressUrl->networkInterface,
&UA_TYPES[UA_TYPES_STRING]);
kvp[3].key = UA_QUALIFIEDNAME(0, "validate");
UA_Variant_setScalar(&kvp[3].value, &validate, &UA_TYPES[UA_TYPES_BOOLEAN]);
UA_UInt16 ether_type = 0xB62C;
kvp[4].key = UA_QUALIFIEDNAME(0, "ethertype");
UA_Variant_setScalar(&kvp[4].value, ðer_type, &UA_TYPES[UA_TYPES_UINT16]);
kvp[5].key = UA_QUALIFIEDNAME(0,"vid");
UA_Variant_setScalar(&kvp[5].value, &vid, &UA_TYPES[UA_TYPES_UINT16]);
kvp[6].key = UA_QUALIFIEDNAME(0,"pcp");
UA_Variant_setScalar(&kvp[6].value, &pcp, &UA_TYPES[UA_TYPES_BYTE]);
if(validate || (c->recvChannelsSize == 0 && c->readerGroupsSize > 0)) {
res = c->cm->openConnection(c->cm, &kvm, psm, c, PubSubRecvChannelCallback);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_PUBSUB(psm->logging, c, "Could not open an ETH recv channel");
return res;
}
}
if(validate || (c->sendChannel == 0 && c->writerGroupsSize > 0)) {
listen = false;
res = c->cm->openConnection(c->cm, &kvm, psm, c, PubSubSendChannelCallback);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_PUBSUB(psm->logging, c,
"Could not open an ETH channel for sending");
}
}
return res;
}
static UA_Boolean
UA_PubSubConnection_canConnect(UA_PubSubConnection *c) {
if(c->sendChannel == 0 && c->writerGroupsSize > 0)
return true;
if(c->recvChannelsSize == 0 && c->readerGroupsSize > 0)
return true;
return false;
}
static UA_StatusCode
UA_PubSubConnection_connect(UA_PubSubManager *psm, UA_PubSubConnection *c,
UA_Boolean validate) {
UA_LOCK_ASSERT(&psm->sc.server->serviceMutex);
UA_EventLoop *el = psm->sc.server->config.eventLoop;
if(!el) {
UA_LOG_ERROR_PUBSUB(psm->logging, c, "No EventLoop configured");
return UA_STATUSCODE_BADINTERNALERROR;
}
ConnectionProfileMapping *profile = NULL;
for(size_t i = 0; i < UA_PUBSUB_PROFILES_SIZE; i++) {
if(!UA_String_equal(&c->config.transportProfileUri,
&connectionProfiles[i].profileURI))
continue;
profile = &connectionProfiles[i];
break;
}
UA_ConnectionManager *cm = (profile) ? getCM(el, profile->protocol) : NULL;
if(!cm || (c->cm && cm != c->cm)) {
UA_LOG_ERROR_PUBSUB(psm->logging, c,
"The requested profile \"%S\"is not supported",
c->config.transportProfileUri);
return UA_STATUSCODE_BADINTERNALERROR;
}
if(!UA_Variant_hasScalarType(&c->config.address,
&UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE])) {
UA_LOG_ERROR_PUBSUB(psm->logging, c, "No NetworkAddressUrlDataType "
"for the address configuration");
return UA_STATUSCODE_BADINTERNALERROR;
}
c->cm = cm;
c->json = profile->json;
return (profile->connect) ?
profile->connect(psm, c, validate) : UA_STATUSCODE_GOOD;
}
UA_StatusCode
UA_Server_getPubSubConnectionConfig(UA_Server *server, const UA_NodeId connection,
UA_PubSubConnectionConfig *config) {
if(!server || !config)
return UA_STATUSCODE_BADINVALIDARGUMENT;
lockServer(server);
UA_PubSubConnection *c = UA_PubSubConnection_find(getPSM(server), connection);
UA_StatusCode res = (c) ?
UA_PubSubConnectionConfig_copy(&c->config, config) : UA_STATUSCODE_BADNOTFOUND;
unlockServer(server);
return res;
}
UA_StatusCode
UA_Server_addPubSubConnection(UA_Server *server,
const UA_PubSubConnectionConfig *cc,
UA_NodeId *cId) {
if(!server || !cc)
return UA_STATUSCODE_BADINTERNALERROR;
lockServer(server);
UA_PubSubManager *psm = getPSM(server);
UA_StatusCode res = (psm) ?
UA_PubSubConnection_create(psm, cc, cId) : UA_STATUSCODE_BADINTERNALERROR;
unlockServer(server);
return res;
}
UA_StatusCode
UA_Server_removePubSubConnection(UA_Server *server, const UA_NodeId cId) {
if(!server)
return UA_STATUSCODE_BADINTERNALERROR;
lockServer(server);
UA_PubSubManager *psm = getPSM(server);
UA_PubSubConnection *c = UA_PubSubConnection_find(psm, cId);
if(!c) {
unlockServer(server);
return UA_STATUSCODE_BADNOTFOUND;
}
UA_PubSubConnection_setPubSubState(psm, c, UA_PUBSUBSTATE_DISABLED);
UA_PubSubConnection_delete(psm, c);
unlockServer(server);
return UA_STATUSCODE_GOOD;
}
UA_StatusCode
UA_Server_enablePubSubConnection(UA_Server *server, const UA_NodeId cId) {
if(!server)
return UA_STATUSCODE_BADINTERNALERROR;
lockServer(server);
UA_PubSubManager *psm = getPSM(server);
UA_StatusCode res = (psm) ?
enablePubSubConnection(psm, cId) : UA_STATUSCODE_BADINTERNALERROR;
unlockServer(server);
return res;
}
UA_StatusCode
UA_Server_disablePubSubConnection(UA_Server *server, const UA_NodeId cId) {
if(!server)
return UA_STATUSCODE_BADINTERNALERROR;
lockServer(server);
UA_PubSubManager *psm = getPSM(server);
UA_StatusCode res = (psm) ?
disablePubSubConnection(psm, cId) : UA_STATUSCODE_BADINTERNALERROR;
unlockServer(server);
return res;
}
UA_StatusCode
UA_Server_processPubSubConnectionReceive(UA_Server *server,
const UA_NodeId connectionId,
const UA_ByteString packet) {
if(!server)
return UA_STATUSCODE_BADINTERNALERROR;
lockServer(server);
UA_StatusCode res = UA_STATUSCODE_BADINTERNALERROR;
UA_PubSubManager *psm = getPSM(server);
if(psm) {
UA_PubSubConnection *c = UA_PubSubConnection_find(psm, connectionId);
if(c) {
res = UA_STATUSCODE_GOOD;
UA_PubSubConnection_process(psm, c, packet);
} else {
res = UA_STATUSCODE_BADNOTFOUND;
}
}
unlockServer(server);
return res;
}
UA_StatusCode
UA_Server_updatePubSubConnectionConfig(UA_Server *server,
const UA_NodeId connectionId,
const UA_PubSubConnectionConfig *config) {
if(!server || !config)
return UA_STATUSCODE_BADINVALIDARGUMENT;
lockServer(server);
UA_PubSubManager *psm = getPSM(server);
UA_PubSubConnection *c = UA_PubSubConnection_find(psm, connectionId);
if(!c) {
unlockServer(server);
return UA_STATUSCODE_BADNOTFOUND;
}
if(UA_PubSubState_isEnabled(c->head.state)) {
UA_LOG_ERROR_PUBSUB(psm->logging, c,
"The PubSubConnection must be disabled to update the config");
unlockServer(server);
return UA_STATUSCODE_BADINTERNALERROR;
}
UA_PubSubConnectionConfig oldConfig = c->config;
memset(&c->config, 0, sizeof(UA_PubSubConnectionConfig));
UA_StatusCode res = UA_PubSubConnectionConfig_copy(config, &c->config);
if(res != UA_STATUSCODE_GOOD)
goto errout;
res = UA_PubSubConnection_connect(psm, c, true);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_PUBSUB(psm->logging, c, "The connection parameters did not validate");
goto errout;
}
UA_PubSubConnectionConfig_clear(&oldConfig);
unlockServer(server);
return UA_STATUSCODE_GOOD;
errout:
UA_PubSubConnectionConfig_clear(&c->config);
c->config = oldConfig;
unlockServer(server);
return res;
}
#endif