static UA_StatusCode readInteger(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *dataValue) { dataValue->hasValue = UA_TRUE; UA_Variant_setScalarCopy(&dataValue->value, (UA_UInt32*)handle, &UA_TYPES[UA_TYPES_INT32]); //note that this is only possible if the identifier is a string - but we are sure to have one here UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node read %.*s",nodeid.identifier.string.length, nodeid.identifier.string.data); UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "read value %i", *(UA_UInt32*)handle); return UA_STATUSCODE_GOOD; }
static UA_StatusCode writeInteger(void *handle, const UA_NodeId nodeid, const UA_Variant *data, const UA_NumericRange *range){ if(UA_Variant_isScalar(data) && data->type == &UA_TYPES[UA_TYPES_INT32] && data->data){ *(UA_UInt32*)handle = *(UA_Int32*)data->data; } //note that this is only possible if the identifier is a string - but we are sure to have one here UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node written %.*s",nodeid.identifier.string.length, nodeid.identifier.string.data); UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "written value %i", *(UA_UInt32*)handle); return UA_STATUSCODE_GOOD; }
static UA_StatusCode readInteger(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *dataValue) { dataValue->hasValue = true; UA_Variant_setScalarCopy(&dataValue->value, (UA_UInt32*)handle, &UA_TYPES[UA_TYPES_INT32]); // we know the nodeid is a string UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node read %.*s", nodeid.identifier.string.length, nodeid.identifier.string.data); UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "read value %i", *(UA_UInt32*)handle); return UA_STATUSCODE_GOOD; }
static UA_StatusCode writeInteger(void *handle, const UA_NodeId nodeid, const UA_Variant *data, const UA_NumericRange *range) { if(UA_Variant_isScalar(data) && data->type == &UA_TYPES[UA_TYPES_INT32] && data->data){ *(UA_UInt32*)handle = *(UA_UInt32*)data->data; } // we know the nodeid is a string UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node written %.*s", nodeid.identifier.string.length, nodeid.identifier.string.data); UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "written value %i", *(UA_UInt32*)handle); return UA_STATUSCODE_GOOD; }
//######################################REP JOB####################### static void testCallback(UA_Server *server, void *data) { UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "testcallback"); ButtonValue = gpioRead(22); }
static int run(UA_String *transportProfile, UA_NetworkAddressUrlDataType *networkAddressUrl) { signal(SIGINT, stopHandler); signal(SIGTERM, stopHandler); UA_Server *server = UA_Server_new(); UA_ServerConfig *config = UA_Server_getConfig(server); UA_ServerConfig_setMinimal(config, 4801, NULL); /* Details about the PubSubTransportLayer can be found inside the * tutorial_pubsub_connection */ config->pubsubTransportLayers = (UA_PubSubTransportLayer *) UA_calloc(2, sizeof(UA_PubSubTransportLayer)); if(!config->pubsubTransportLayers) { UA_Server_delete(server); return EXIT_FAILURE; } config->pubsubTransportLayers[0] = UA_PubSubTransportLayerUDPMP(); config->pubsubTransportLayersSize++; #ifdef UA_ENABLE_PUBSUB_ETH_UADP config->pubsubTransportLayers[1] = UA_PubSubTransportLayerEthernet(); config->pubsubTransportLayersSize++; #endif UA_PubSubConnectionConfig connectionConfig; memset(&connectionConfig, 0, sizeof(connectionConfig)); connectionConfig.name = UA_STRING("UADP Connection 1"); connectionConfig.transportProfileUri = *transportProfile; connectionConfig.enabled = UA_TRUE; UA_Variant_setScalar(&connectionConfig.address, networkAddressUrl, &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]); UA_NodeId connectionIdent; UA_StatusCode retval = UA_Server_addPubSubConnection(server, &connectionConfig, &connectionIdent); if(retval == UA_STATUSCODE_GOOD) UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "The PubSub Connection was created successfully!"); /* The following lines register the listening on the configured multicast * address and configure a repeated job, which is used to handle received * messages. */ UA_PubSubConnection *connection = UA_PubSubConnection_findConnectionbyId(server, connectionIdent); if(connection != NULL) { UA_StatusCode rv = connection->channel->regist(connection->channel, NULL, NULL); if (rv == UA_STATUSCODE_GOOD) { UA_UInt64 subscriptionCallbackId; UA_Server_addRepeatedCallback(server, (UA_ServerCallback)subscriptionPollingCallback, connection, 100, &subscriptionCallbackId); } else { UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "register channel failed: %s!", UA_StatusCode_name(rv)); } } retval |= UA_Server_run(server, &running); UA_Server_delete(server); return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;; }
static void serverOnNetworkCallback(const UA_ServerOnNetwork *serverOnNetwork, UA_Boolean isServerAnnounce, UA_Boolean isTxtReceived, void *data) { if(discovery_url != NULL || !isServerAnnounce) { UA_LOG_DEBUG(logger, UA_LOGCATEGORY_SERVER, "serverOnNetworkCallback called, but discovery URL " "already initialized or is not announcing. Ignoring."); return; // we already have everything we need or we only want server announces } if(self_discovery_url != NULL && UA_String_equal(&serverOnNetwork->discoveryUrl, self_discovery_url)) { // skip self return; } if(!isTxtReceived) return; // we wait until the corresponding TXT record is announced. // Problem: how to handle if a Server does not announce the // optional TXT? // here you can filter for a specific LDS server, e.g. call FindServers on // the serverOnNetwork to make sure you are registering with the correct // LDS. We will ignore this for now UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Another server announced itself on %.*s", (int)serverOnNetwork->discoveryUrl.length, serverOnNetwork->discoveryUrl.data); if(discovery_url != NULL) UA_free(discovery_url); discovery_url = (char*)UA_malloc(serverOnNetwork->discoveryUrl.length + 1); memcpy(discovery_url, serverOnNetwork->discoveryUrl.data, serverOnNetwork->discoveryUrl.length); discovery_url[serverOnNetwork->discoveryUrl.length] = 0; }
UA_Boolean compatibleValue(UA_Server *server, const UA_NodeId *targetDataTypeId, UA_Int32 targetValueRank, size_t targetArrayDimensionsSize, const UA_UInt32 *targetArrayDimensions, const UA_Variant *value, const UA_NumericRange *range) { /* Empty variant is only allowed for BaseDataType */ if(!value->type) { if(UA_NodeId_equal(targetDataTypeId, &UA_TYPES[UA_TYPES_VARIANT].typeId) || UA_NodeId_equal(targetDataTypeId, &UA_NODEID_NULL)) return true; UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER, "Only Variables with data type BaseDataType may contain " "a null (empty) value"); return false; } /* Has the value a subtype of the required type? BaseDataType (Variant) can * be anything... */ if(!compatibleDataType(server, &value->type->typeId, targetDataTypeId)) return false; /* Array dimensions are checked later when writing the range */ if(range) return true; /* See if the array dimensions match. */ if(!compatibleValueArrayDimensions(value, targetArrayDimensionsSize, targetArrayDimensions)) return false; /* Check if the valuerank allows for the value dimension */ return compatibleValueRankValue(targetValueRank, value); }
/* call only from the single networking thread */ static UA_StatusCode ServerNetworkLayerTCP_add(ServerNetworkLayerTCP *layer, UA_Int32 newsockfd) { UA_Connection *c = malloc(sizeof(UA_Connection)); if(!c) return UA_STATUSCODE_BADINTERNALERROR; struct sockaddr_in addr; socklen_t addrlen = sizeof(struct sockaddr_in); getsockname(newsockfd, (struct sockaddr*)&addr, &addrlen); UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK, "New Connection %i over TCP from %s:%d", newsockfd, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); UA_Connection_init(c); c->sockfd = newsockfd; c->handle = layer; c->localConf = layer->conf; c->send = socket_write; c->close = ServerNetworkLayerTCP_closeConnection; c->getSendBuffer = ServerNetworkLayerGetSendBuffer; c->releaseSendBuffer = ServerNetworkLayerReleaseSendBuffer; c->releaseRecvBuffer = ServerNetworkLayerReleaseRecvBuffer; c->state = UA_CONNECTION_OPENING; struct ConnectionMapping *nm; nm = realloc(layer->mappings, sizeof(struct ConnectionMapping)*(layer->mappingsSize+1)); if(!nm) { UA_LOG_ERROR(layer->logger, UA_LOGCATEGORY_NETWORK, "No memory for a new Connection"); free(c); return UA_STATUSCODE_BADINTERNALERROR; } layer->mappings = nm; layer->mappings[layer->mappingsSize] = (struct ConnectionMapping){c, newsockfd}; layer->mappingsSize++; return UA_STATUSCODE_GOOD; }
/** * Triggering an event * ^^^^^^^^^^^^^^^^^^^ * First a node representing an event is generated using ``setUpEvent``. Once our event is good to go, we specify * a node which emits the event - in this case the server node. We can use ``UA_Server_triggerEvent`` to trigger our * event onto said node. Passing ``NULL`` as the second-last argument means we will not receive the `EventId`. * The last boolean argument states whether the node should be deleted. */ static UA_StatusCode generateEventMethodCallback(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_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Creating event"); /* set up event */ UA_NodeId eventNodeId; UA_StatusCode retval = setUpEvent(server, &eventNodeId); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Creating event failed. StatusCode %s", UA_StatusCode_name(retval)); return retval; } retval = UA_Server_triggerEvent(server, eventNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), NULL, UA_TRUE); if(retval != UA_STATUSCODE_GOOD) UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Triggering event failed. StatusCode %s", UA_StatusCode_name(retval)); return retval; }
/*receives hello ack, opens secure channel*/ UA_StatusCode processACKResponseAsync(void *application, UA_Connection *connection, UA_ByteString *chunk) { UA_Client *client = (UA_Client*)application; /* Decode the message */ size_t offset = 0; UA_TcpMessageHeader messageHeader; UA_TcpAcknowledgeMessage ackMessage; client->connectStatus = UA_TcpMessageHeader_decodeBinary (chunk, &offset, &messageHeader); client->connectStatus |= UA_TcpAcknowledgeMessage_decodeBinary( chunk, &offset, &ackMessage); if (client->connectStatus != UA_STATUSCODE_GOOD) { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Decoding ACK message failed"); return client->connectStatus; } UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Received ACK message"); client->connectStatus = UA_Connection_processHELACK(connection, &client->config.localConnectionConfig, (const UA_ConnectionConfig*)&ackMessage); if(client->connectStatus != UA_STATUSCODE_GOOD) return client->connectStatus; client->state = UA_CLIENTSTATE_CONNECTED; /* Open a SecureChannel. TODO: Select with endpoint */ client->channel.connection = &client->connection; client->connectStatus = openSecureChannelAsync(client/*, false*/); return client->connectStatus; }
static void writeVariable(UA_Server *server) { UA_StatusCode retval = UA_Server_writeValueRank(server, pointVariableId, 0); UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Setting the Value Rank failed with Status Code %s", UA_StatusCode_name(retval)); }
static UA_StatusCode readInteger(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, UA_Boolean includeSourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) { UA_Int32 *myInteger = (UA_Int32*)nodeContext; value->hasValue = true; UA_Variant_setScalarCopy(&value->value, myInteger, &UA_TYPES[UA_TYPES_INT32]); // we know the nodeid is a string UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node read %.*s", (int)nodeId->identifier.string.length, nodeId->identifier.string.data); UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "read value %i", *(UA_UInt32 *)myInteger); return UA_STATUSCODE_GOOD; }
/** * process binary message received from Connection * dose not modify UA_ByteString you have to free it youself. * use of connection->getSendBuffer() and connection->sent() to answer Message */ void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, const UA_ByteString *msg) { size_t pos = 0; UA_TcpMessageHeader tcpMessageHeader; do { if(UA_TcpMessageHeader_decodeBinary(msg, &pos, &tcpMessageHeader)) { UA_LOG_INFO(server->logger, UA_LOGCATEGORY_NETWORK, "Decoding of message header failed"); connection->close(connection); break; } size_t targetpos = pos - 8 + tcpMessageHeader.messageSize; switch(tcpMessageHeader.messageTypeAndFinal & 0xffffff) { case UA_MESSAGETYPEANDFINAL_HELF & 0xffffff: processHEL(connection, msg, &pos); break; case UA_MESSAGETYPEANDFINAL_OPNF & 0xffffff: processOPN(connection, server, msg, &pos); break; case UA_MESSAGETYPEANDFINAL_MSGF & 0xffffff: #ifndef EXTENSION_STATELESS if(connection->state != UA_CONNECTION_ESTABLISHED) { connection->close(connection); return; } #endif processMSG(connection, server, msg, &pos); break; case UA_MESSAGETYPEANDFINAL_CLOF & 0xffffff: processCLO(connection, server, msg, &pos); connection->close(connection); return; default: UA_LOG_INFO(server->logger, UA_LOGCATEGORY_NETWORK, "Unknown request type on Connection %i", connection->sockfd); } UA_TcpMessageHeader_deleteMembers(&tcpMessageHeader); if(pos != targetpos) { UA_LOG_INFO(server->logger, UA_LOGCATEGORY_NETWORK, "Message on Connection %i was not entirely processed. " "Arrived at position %i, skip after the announced length to position %i", connection->sockfd, pos, targetpos); pos = targetpos; } } while(msg->length > pos); }
static UA_StatusCode onRead(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *dataValue) { UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "VALUE READ!!! %i", *(UA_UInt32*)handle); handle = &ButtonValue; UA_Variant_setScalarCopy(&dataValue->value, (UA_UInt32*)handle, &UA_TYPES[UA_TYPES_INT32]); }
static UA_StatusCode writeInteger(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, const UA_NumericRange *range, const UA_DataValue *value) { UA_Int32 *myInteger = (UA_Int32*)nodeContext; if(value->hasValue && UA_Variant_isScalar(&value->value) && value->value.type == &UA_TYPES[UA_TYPES_INT32] && value->value.data) *myInteger = *(UA_Int32 *)value->value.data; // we know the nodeid is a string UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node written %.*s", (int)nodeId->identifier.string.length, nodeId->identifier.string.data); UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "written value %i", *(UA_UInt32 *)myInteger); return UA_STATUSCODE_GOOD; }
int main(void) { signal(SIGINT, stopHandler); signal(SIGTERM, stopHandler); UA_ServerConfig *config = UA_ServerConfig_new_minimal(4801, NULL); /* Details about the PubSubTransportLayer can be found inside the * tutorial_pubsub_connection */ config->pubsubTransportLayers = (UA_PubSubTransportLayer *) UA_malloc(sizeof(UA_PubSubTransportLayer)); if (!config->pubsubTransportLayers) { UA_ServerConfig_delete(config); return -1; } config->pubsubTransportLayers[0] = UA_PubSubTransportLayerUDPMP(); config->pubsubTransportLayersSize++; UA_Server *server = UA_Server_new(config); UA_PubSubConnectionConfig connectionConfig; memset(&connectionConfig, 0, sizeof(connectionConfig)); connectionConfig.name = UA_STRING("UDP-UADP Connection 1"); connectionConfig.transportProfileUri = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp"); connectionConfig.enabled = UA_TRUE; UA_NetworkAddressUrlDataType networkAddressUrl = {UA_STRING_NULL , UA_STRING("opc.udp://224.0.0.22:4840/")}; UA_Variant_setScalar(&connectionConfig.address, &networkAddressUrl, &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]); UA_NodeId connectionIdent; UA_StatusCode retval = UA_Server_addPubSubConnection(server, &connectionConfig, &connectionIdent); if(retval == UA_STATUSCODE_GOOD) UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "The PubSub Connection was created successfully!"); /* The following lines register the listening on the configured multicast * address and configure a repeated job, which is used to handle received * messages. */ UA_PubSubConnection *connection = UA_PubSubConnection_findConnectionbyId(server, connectionIdent); if(connection != NULL) { UA_StatusCode rv = connection->channel->regist(connection->channel, NULL); if (rv == UA_STATUSCODE_GOOD) { UA_UInt64 subscriptionCallbackId; UA_Server_addRepeatedCallback(server, (UA_ServerCallback)subscriptionPollingCallback, connection, 100, &subscriptionCallbackId); } else { UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "register channel failed: %s!", UA_StatusCode_name(rv)); } } retval |= UA_Server_run(server, &running); UA_Server_delete(server); UA_ServerConfig_delete(config); return (int)retval; }
void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, UA_ByteString *msg) { if(msg->length <= 0) return; size_t pos = 0; UA_TcpMessageHeader tcpMessageHeader; do { if(UA_TcpMessageHeader_decodeBinary(msg, &pos, &tcpMessageHeader)) { UA_LOG_INFO(server->logger, UA_LOGCATEGORY_COMMUNICATION, "Decoding of message header failed"); connection->close(connection); break; } size_t targetpos = pos - 8 + tcpMessageHeader.messageSize; switch(tcpMessageHeader.messageTypeAndFinal & 0xffffff) { case UA_MESSAGETYPEANDFINAL_HELF & 0xffffff: processHEL(connection, msg, &pos); break; case UA_MESSAGETYPEANDFINAL_OPNF & 0xffffff: processOPN(connection, server, msg, &pos); break; case UA_MESSAGETYPEANDFINAL_MSGF & 0xffffff: #ifndef EXTENSION_STATELESS if(connection->state != UA_CONNECTION_ESTABLISHED) connection->close(connection); else #endif processMSG(connection, server, msg, &pos); break; case UA_MESSAGETYPEANDFINAL_CLOF & 0xffffff: processCLO(connection, server, msg, &pos); connection->close(connection); return; } UA_TcpMessageHeader_deleteMembers(&tcpMessageHeader); if(pos != targetpos) { UA_LOG_INFO(server->logger, UA_LOGCATEGORY_COMMUNICATION, "The message was not entirely processed, skipping to the end"); pos = targetpos; } } while(msg->length > (UA_Int32)pos); }
//################################################BUTTON########################## static UA_StatusCode readIntegerB(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *dataValue) { // Let LED BLink gpioPWM(17,255); usleep( 1000 ); gpioPWM(17,0); // Read Digital value of GPIO 22 and set as Node Value handle = &ButtonValue; dataValue->hasValue = true; UA_Variant_setScalarCopy(&dataValue->value, (UA_UInt32*)handle, &UA_TYPES[UA_TYPES_INT32]); // we know the nodeid is a string UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node read %.*s", nodeid.identifier.string.length, nodeid.identifier.string.data); UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "read value %i", *(UA_UInt32*)handle); return UA_STATUSCODE_GOOD; }
int main(void) { signal(SIGINT, stopHandler); /* catches ctrl-c */ UA_ClientConfig config = UA_ClientConfig_default; /* default timeout is 5 seconds. Set it to 1 second here for demo */ config.timeout = 1000; UA_Client *client = UA_Client_new(config); /* Read the value attribute of the node. UA_Client_readValueAttribute is a * wrapper for the raw read service available as UA_Client_Service_read. */ UA_Variant value; /* Variants can hold scalar values and arrays of any type */ UA_Variant_init(&value); /* Endless loop reading the server time */ while (running) { /* if already connected, this will return GOOD and do nothing */ /* if the connection is closed/errored, the connection will be reset and then reconnected */ /* Alternatively you can also use UA_Client_getState to get the current state */ UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840"); if (retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_CLIENT, "Not connected. Retrying to connect in 1 second"); /* The connect may timeout after 1 second (see above) or it may fail immediately on network errors */ /* E.g. name resolution errors or unreachable network. Thus there should be a small sleep here */ UA_sleep_ms(1000); continue; } /* NodeId of the variable holding the current time */ const UA_NodeId nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME); retval = UA_Client_readValueAttribute(client, nodeId, &value); if (retval == UA_STATUSCODE_BADCONNECTIONCLOSED) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_CLIENT, "Connection was closed. Reconnecting ..."); continue; } if (retval == UA_STATUSCODE_GOOD && UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_DATETIME])) { UA_DateTime raw_date = *(UA_DateTime *) value.data; UA_String string_date = UA_DateTime_toString(raw_date); UA_LOG_INFO(logger, UA_LOGCATEGORY_CLIENT, "string date is: %.*s", (int) string_date.length, string_date.data); UA_String_deleteMembers(&string_date); } UA_sleep_ms(1000); }; /* Clean up */ UA_Variant_deleteMembers(&value); UA_Client_delete(client); /* Disconnects the client internally */ return UA_STATUSCODE_GOOD; }
static UA_StatusCode helloWorldMethod(const UA_NodeId objectId, const UA_Variant *input, UA_Variant *output, void *handle) { UA_String *inputStr = (UA_String*)input->data; UA_String tmp = UA_STRING_ALLOC("Hello "); if(inputStr->length > 0) { tmp.data = 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_deleteMembers(&tmp); UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Hello World was called"); return UA_STATUSCODE_GOOD; }
/* callback triggered from the server */ static void ServerNetworkLayerTCP_closeConnection(UA_Connection *connection) { #ifdef UA_MULTITHREADING if(uatomic_xchg(&connection->state, UA_CONNECTION_CLOSED) == UA_CONNECTION_CLOSED) return; #else if(connection->state == UA_CONNECTION_CLOSED) return; connection->state = UA_CONNECTION_CLOSED; #endif ServerNetworkLayerTCP *layer = connection->handle; UA_LOG_INFO(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "Closing the Connection %i", connection->sockfd); /* only "shutdown" here. this triggers the select, where the socket is "closed" in the mainloop */ shutdown(connection->sockfd, 2); }
/* Example 3 */ static UA_StatusCode fooBarMethod(void *handle, const UA_NodeId objectId, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output) { /* the same as helloWorld, but returns foobar */ UA_String *inputStr = (UA_String*)input->data; UA_String tmp = UA_STRING_ALLOC("FooBar! "); if(inputStr->length > 0) { tmp.data = 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_deleteMembers(&tmp); UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "FooBar was called"); return UA_STATUSCODE_GOOD; }
/** * Called in processCompleteChunk for every complete chunk which is received by the server. * * It will first try to decode the message to get the name of the called service. * When we have a name the message is dumped as binary to that file. * If the file already exists a new file will be created with a counter at the end. */ void UA_debug_dumpCompleteChunk(UA_Server *const server, UA_Connection *const connection, UA_ByteString *messageBuffer) { struct UA_dump_filename dump_filename; dump_filename.messageType = NULL; dump_filename.serviceName[0] = 0; if(!connection->channel) { UA_debug_dump_setName_withoutChannel(server, connection, messageBuffer, &dump_filename); } else { UA_SecureChannel dummy = *connection->channel; TAILQ_INIT(&dummy.messages); UA_ByteString messageBufferCopy; UA_ByteString_copy(messageBuffer, &messageBufferCopy); UA_SecureChannel_decryptAddChunk(&dummy, &messageBufferCopy, UA_TRUE); UA_SecureChannel_processCompleteMessages(&dummy, &dump_filename, UA_debug_dump_setName_withChannel); UA_SecureChannel_deleteMessages(&dummy); UA_ByteString_deleteMembers(&messageBufferCopy); } char fileName[250]; snprintf(fileName, 255, "%s/%05d_%s%s", UA_CORPUS_OUTPUT_DIR, ++UA_dump_chunkCount, dump_filename.messageType ? dump_filename.messageType : "", dump_filename.serviceName); char dumpOutputFile[255]; snprintf(dumpOutputFile, 255, "%s.bin", fileName); // check if file exists and if yes create a counting filename to avoid overwriting unsigned cnt = 1; while ( access( dumpOutputFile, F_OK ) != -1 ) { snprintf(dumpOutputFile, 255, "%s_%d.bin", fileName, cnt); cnt++; } UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER, "Dumping package %s", dumpOutputFile); FILE *write_ptr = fopen(dumpOutputFile, "ab"); fwrite(messageBuffer->data, messageBuffer->length, 1, write_ptr); // write 10 bytes from our buffer // add the available memory size. See the UA_DUMP_RAM_SIZE define for more info. uint32_t ramSize = UA_DUMP_RAM_SIZE; fwrite(&ramSize, sizeof(ramSize), 1, write_ptr); fclose(write_ptr); }
static UA_StatusCode sendHELMessage(UA_Client *client) { /* Get a buffer */ UA_ByteString message; UA_Connection *conn = &client->connection; UA_StatusCode retval = conn->getSendBuffer(conn, UA_MINMESSAGESIZE, &message); if(retval != UA_STATUSCODE_GOOD) return retval; /* Prepare the HEL message and encode at offset 8 */ UA_TcpHelloMessage hello; UA_String_copy(&client->endpointUrl, &hello.endpointUrl); /* must be less than 4096 bytes */ memcpy(&hello, &client->config.localConnectionConfig, sizeof(UA_ConnectionConfig)); /* same struct layout */ UA_Byte *bufPos = &message.data[8]; /* skip the header */ const UA_Byte *bufEnd = &message.data[message.length]; client->connectStatus = UA_TcpHelloMessage_encodeBinary(&hello, &bufPos, bufEnd); UA_TcpHelloMessage_deleteMembers (&hello); /* Encode the message header at offset 0 */ UA_TcpMessageHeader messageHeader; messageHeader.messageTypeAndChunkType = UA_CHUNKTYPE_FINAL + UA_MESSAGETYPE_HEL; messageHeader.messageSize = (UA_UInt32) ((uintptr_t)bufPos - (uintptr_t)message.data); bufPos = message.data; retval = UA_TcpMessageHeader_encodeBinary(&messageHeader, &bufPos, bufEnd); if(retval != UA_STATUSCODE_GOOD) { conn->releaseSendBuffer(conn, &message); return retval; } /* Send the HEL message */ message.length = messageHeader.messageSize; retval = conn->send (conn, &message); if(retval == UA_STATUSCODE_GOOD) { UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Sent HEL message"); } else { UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_NETWORK, "Sending HEL failed"); } return retval; }
int main(int argc, char** argv) { signal(SIGINT, stopHandler); /* catches ctrl-c */ UA_ServerConfig *config = UA_ServerConfig_new_default(); /* load certificate */ config->serverCertificate = loadFile("server_cert.der"); if(config->serverCertificate.length > 0) UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Certificate loaded"); UA_Server *server = UA_Server_new(config); UA_StatusCode retval = UA_Server_run(server, &running); /* deallocate certificate's memory */ UA_ByteString_clear(&config->serverCertificate); UA_Server_delete(server); UA_ServerConfig_delete(config); return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE; }
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 size_t ServerNetworkLayerTCP_stop(ServerNetworkLayerTCP *layer, UA_Job **jobs) { UA_LOG_INFO(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "Shutting down the TCP network layer with %d open connection(s)", layer->mappingsSize); shutdown(layer->serversockfd,2); CLOSESOCKET(layer->serversockfd); UA_Job *items = malloc(sizeof(UA_Job) * layer->mappingsSize * 2); if(!items) return 0; for(size_t i = 0; i < layer->mappingsSize; i++) { socket_close(layer->mappings[i].connection); items[i*2].type = UA_JOBTYPE_DETACHCONNECTION; items[i*2].job.closeConnection = layer->mappings[i].connection; items[(i*2)+1].type = UA_JOBTYPE_METHODCALL_DELAYED; items[(i*2)+1].job.methodCall.method = FreeConnectionCallback; items[(i*2)+1].job.methodCall.data = layer->mappings[i].connection; } #ifdef _WIN32 WSACleanup(); #endif *jobs = items; return layer->mappingsSize*2; }
static UA_StatusCode ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl) { ServerNetworkLayerTCP *layer = nl->handle; #ifdef _WIN32 if((layer->serversockfd = socket(PF_INET, SOCK_STREAM,0)) == (UA_Int32)INVALID_SOCKET) { UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error opening socket, code: %d", WSAGetLastError()); return UA_STATUSCODE_BADINTERNALERROR; } #else if((layer->serversockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error opening socket"); return UA_STATUSCODE_BADINTERNALERROR; } #endif const struct sockaddr_in serv_addr = {.sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY, .sin_port = htons(layer->port), .sin_zero = {0}}; int optval = 1; if(setsockopt(layer->serversockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(optval)) == -1) { UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error during setting of socket options"); CLOSESOCKET(layer->serversockfd); return UA_STATUSCODE_BADINTERNALERROR; } if(bind(layer->serversockfd, (const struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error during socket binding"); CLOSESOCKET(layer->serversockfd); return UA_STATUSCODE_BADINTERNALERROR; } socket_set_nonblocking(layer->serversockfd); listen(layer->serversockfd, MAXBACKLOG); UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK, "TCP network layer listening on %.*s", nl->discoveryUrl.length, nl->discoveryUrl.data); return UA_STATUSCODE_GOOD; } static size_t ServerNetworkLayerTCP_getJobs(UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt16 timeout) { ServerNetworkLayerTCP *layer = nl->handle; fd_set fdset; UA_Int32 highestfd = setFDSet(layer, &fdset); struct timeval tmptv = {0, timeout}; UA_Int32 resultsize; resultsize = select(highestfd+1, &fdset, NULL, NULL, &tmptv); if(resultsize < 0) { *jobs = NULL; return 0; } /* accept new connections (can only be a single one) */ if(FD_ISSET(layer->serversockfd, &fdset)) { resultsize--; struct sockaddr_in cli_addr; socklen_t cli_len = sizeof(cli_addr); int newsockfd = accept(layer->serversockfd, (struct sockaddr *) &cli_addr, &cli_len); int i = 1; setsockopt(newsockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&i, sizeof(i)); if(newsockfd >= 0) { socket_set_nonblocking(newsockfd); ServerNetworkLayerTCP_add(layer, newsockfd); } } /* alloc enough space for a cleanup-connection and free-connection job per resulted socket */ if(resultsize == 0) return 0; UA_Job *js = malloc(sizeof(UA_Job) * resultsize * 2); if(!js) return 0; /* read from established sockets */ size_t j = 0; UA_ByteString buf = UA_BYTESTRING_NULL; for(size_t i = 0; i < layer->mappingsSize && j < (size_t)resultsize; i++) { if(!(FD_ISSET(layer->mappings[i].sockfd, &fdset))) continue; UA_StatusCode retval = socket_recv(layer->mappings[i].connection, &buf, 0); if(retval == UA_STATUSCODE_GOOD) { UA_Boolean realloced = UA_FALSE; retval = UA_Connection_completeMessages(layer->mappings[i].connection, &buf, &realloced); if(retval != UA_STATUSCODE_GOOD || buf.length == 0) continue; js[j].job.binaryMessage.connection = layer->mappings[i].connection; js[j].job.binaryMessage.message = buf; if(!realloced) js[j].type = UA_JOBTYPE_BINARYMESSAGE_NETWORKLAYER; else js[j].type = UA_JOBTYPE_BINARYMESSAGE_ALLOCATED; j++; } else if (retval == UA_STATUSCODE_BADCONNECTIONCLOSED) { UA_Connection *c = layer->mappings[i].connection; /* the socket was closed from remote */ js[j].type = UA_JOBTYPE_DETACHCONNECTION; js[j].job.closeConnection = layer->mappings[i].connection; layer->mappings[i] = layer->mappings[layer->mappingsSize-1]; layer->mappingsSize--; j++; js[j].type = UA_JOBTYPE_METHODCALL_DELAYED; js[j].job.methodCall.method = FreeConnectionCallback; js[j].job.methodCall.data = c; j++; } } if(j == 0) { free(js); js = NULL; } *jobs = js; return j; }
static void stopHandler(int sign) { UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c"); running = false; }