|
@@ -9,7 +9,9 @@
|
|
|
#include <stdlib.h>
|
|
|
#include <signal.h>
|
|
|
#define __USE_XOPEN2K
|
|
|
+#ifdef UA_MULTITHREADING
|
|
|
#include <pthread.h>
|
|
|
+#endif
|
|
|
|
|
|
|
|
|
#include "ua_server.h"
|
|
@@ -18,120 +20,236 @@
|
|
|
#include "logger_stdout.h"
|
|
|
#include "networklayer_tcp.h"
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+UA_Boolean running = 1;
|
|
|
+UA_Logger logger;
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
|
static UA_StatusCode readTimeData(const void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
|
|
|
- UA_DateTime *currentTime = UA_DateTime_new();
|
|
|
- if(!currentTime)
|
|
|
- return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
- *currentTime = UA_DateTime_now();
|
|
|
- value->value.type = &UA_TYPES[UA_TYPES_DATETIME];
|
|
|
- value->value.arrayLength = 1;
|
|
|
- value->value.dataPtr = currentTime;
|
|
|
- value->value.arrayDimensionsSize = -1;
|
|
|
- value->value.arrayDimensions = NULL;
|
|
|
- value->hasVariant = UA_TRUE;
|
|
|
- if(sourceTimeStamp) {
|
|
|
- value->hasSourceTimestamp = UA_TRUE;
|
|
|
- value->sourceTimestamp = *currentTime;
|
|
|
- }
|
|
|
- return UA_STATUSCODE_GOOD;
|
|
|
+ UA_DateTime *currentTime = UA_DateTime_new();
|
|
|
+ if(!currentTime)
|
|
|
+ return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
+ *currentTime = UA_DateTime_now();
|
|
|
+ value->value.type = &UA_TYPES[UA_TYPES_DATETIME];
|
|
|
+ value->value.arrayLength = 1;
|
|
|
+ value->value.dataPtr = currentTime;
|
|
|
+ value->value.arrayDimensionsSize = -1;
|
|
|
+ value->value.arrayDimensions = NULL;
|
|
|
+ value->hasVariant = UA_TRUE;
|
|
|
+ if(sourceTimeStamp) {
|
|
|
+ value->hasSourceTimestamp = UA_TRUE;
|
|
|
+ value->sourceTimestamp = *currentTime;
|
|
|
+ }
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
|
|
|
static void releaseTimeData(const void *handle, UA_DataValue *value) {
|
|
|
- UA_DateTime_delete((UA_DateTime*)value->value.dataPtr);
|
|
|
+ UA_DateTime_delete((UA_DateTime*)value->value.dataPtr);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-UA_Int32 deviceStatus = 0;
|
|
|
-pthread_rwlock_t deviceStatusLock;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+FILE* temperatureFile = NULL;
|
|
|
+static UA_StatusCode readTemperature(const void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
|
|
|
+ UA_Double* currentTemperature = UA_Double_new();
|
|
|
+
|
|
|
+ if(!currentTemperature)
|
|
|
+ return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
+
|
|
|
+ fseek(temperatureFile, 0, SEEK_SET);
|
|
|
+
|
|
|
+ if(fscanf(temperatureFile, "%lf", currentTemperature) != 1){
|
|
|
+ UA_LOG_WARNING(logger, UA_LOGGERCATEGORY_USERLAND, "Can not parse temperature");
|
|
|
+ exit(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ *currentTemperature /= 1000.0;
|
|
|
+
|
|
|
+ value->value.type = &UA_TYPES[UA_TYPES_DOUBLE];
|
|
|
+ value->value.arrayLength = 1;
|
|
|
+ value->value.dataPtr = currentTemperature;
|
|
|
+ value->value.arrayDimensionsSize = -1;
|
|
|
+ value->value.arrayDimensions = NULL;
|
|
|
+ value->hasVariant = UA_TRUE;
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
|
|
|
-static void printDeviceStatus(UA_Server *server, void *data) {
|
|
|
- printf("Device Status: %i\n", deviceStatus);
|
|
|
+static void releaseTemperature(const void *handle, UA_DataValue *value) {
|
|
|
+ UA_Double_delete((UA_Double*)value->value.dataPtr);
|
|
|
}
|
|
|
|
|
|
-static UA_StatusCode readDeviceStatus(const void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+#ifdef UA_MULTITHREADING
|
|
|
+pthread_rwlock_t writeLock;
|
|
|
+#endif
|
|
|
+FILE* triggerFile = NULL;
|
|
|
+FILE* ledFile = NULL;
|
|
|
+UA_Boolean ledStatus = 0;
|
|
|
+
|
|
|
+static UA_StatusCode readLedStatus(const void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
|
|
|
+
|
|
|
and return a copy of the data. */
|
|
|
- pthread_rwlock_rdlock(&deviceStatusLock);
|
|
|
- value->value.type = &UA_TYPES[UA_TYPES_INT32];
|
|
|
- value->value.arrayLength = 1;
|
|
|
- value->value.dataPtr = &deviceStatus;
|
|
|
- value->value.arrayDimensionsSize = -1;
|
|
|
- value->value.arrayDimensions = NULL;
|
|
|
- value->hasVariant = UA_TRUE;
|
|
|
- if(sourceTimeStamp) {
|
|
|
- value->sourceTimestamp = UA_DateTime_now();
|
|
|
- value->hasSourceTimestamp = UA_TRUE;
|
|
|
- }
|
|
|
- return UA_STATUSCODE_GOOD;
|
|
|
+#ifdef UA_MULTITHREADING
|
|
|
+ pthread_rwlock_rdlock(&writeLock);
|
|
|
+#endif
|
|
|
+ value->value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
|
|
|
+ value->value.arrayLength = 1;
|
|
|
+ value->value.dataPtr = &ledStatus;
|
|
|
+ value->value.arrayDimensionsSize = -1;
|
|
|
+ value->value.arrayDimensions = NULL;
|
|
|
+ value->hasVariant = UA_TRUE;
|
|
|
+ if(sourceTimeStamp) {
|
|
|
+ value->sourceTimestamp = UA_DateTime_now();
|
|
|
+ value->hasSourceTimestamp = UA_TRUE;
|
|
|
+ }
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
|
|
|
-static void releaseDeviceStatus(const void *handle, UA_DataValue *value) {
|
|
|
-
|
|
|
+static void releaseLedStatus(const void *handle, UA_DataValue *value) {
|
|
|
+
|
|
|
variantdata. */
|
|
|
- value->value.arrayLength = -1;
|
|
|
- value->value.dataPtr = NULL;
|
|
|
- pthread_rwlock_unlock(&deviceStatusLock);
|
|
|
+ value->value.arrayLength = -1;
|
|
|
+ value->value.dataPtr = NULL;
|
|
|
+#ifdef UA_MULTITHREADING
|
|
|
+ pthread_rwlock_unlock(&writeLock);
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
-static UA_StatusCode writeDeviceStatus(const void *handle, const UA_Variant *data) {
|
|
|
- pthread_rwlock_wrlock(&deviceStatusLock);
|
|
|
- if(data->dataPtr)
|
|
|
- deviceStatus = *(UA_Int32*)data->dataPtr;
|
|
|
- pthread_rwlock_unlock(&deviceStatusLock);
|
|
|
- return UA_STATUSCODE_GOOD;
|
|
|
+static UA_StatusCode writeLedStatus(const void *handle, const UA_Variant *data) {
|
|
|
+#ifdef UA_MULTITHREADING
|
|
|
+ pthread_rwlock_wrlock(&writeLock);
|
|
|
+#endif
|
|
|
+ if(data->dataPtr)
|
|
|
+ ledStatus = *(UA_Boolean*)data->dataPtr;
|
|
|
+
|
|
|
+ if(triggerFile)
|
|
|
+ fseek(triggerFile, 0, SEEK_SET);
|
|
|
+
|
|
|
+ if(ledFile){
|
|
|
+ if(ledStatus == 1){
|
|
|
+ fprintf(ledFile, "%s", "1");
|
|
|
+ } else {
|
|
|
+ fprintf(ledFile, "%s", "0");
|
|
|
+ }
|
|
|
+ fflush(ledFile);
|
|
|
+ }
|
|
|
+#ifdef UA_MULTITHREADING
|
|
|
+ pthread_rwlock_unlock(&writeLock);
|
|
|
+#endif
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
|
|
|
-UA_Boolean running = 1;
|
|
|
+static void printLedStatus(UA_Server *server, void *data) {
|
|
|
+ UA_LOG_INFO(logger, UA_LOGGERCATEGORY_SERVER, ledStatus ? "LED is on" : "LED is off");
|
|
|
+}
|
|
|
|
|
|
static void stopHandler(int sign) {
|
|
|
- printf("Received Ctrl-C\n");
|
|
|
+ printf("Received Ctrl-C\n");
|
|
|
running = 0;
|
|
|
}
|
|
|
|
|
|
int main(int argc, char** argv) {
|
|
|
signal(SIGINT, stopHandler);
|
|
|
- pthread_rwlock_init(&deviceStatusLock, 0);
|
|
|
+#ifdef UA_MULTITHREADING
|
|
|
+ pthread_rwlock_init(&writeLock, 0);
|
|
|
+#endif
|
|
|
|
|
|
UA_Server *server = UA_Server_new();
|
|
|
- UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
|
|
|
+ logger = Logger_Stdout_new();
|
|
|
+ UA_Server_setLogger(server, logger);
|
|
|
+ UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
|
|
|
|
|
|
-
|
|
|
- UA_DataSource dateDataSource = (UA_DataSource)
|
|
|
- {.handle = NULL,
|
|
|
- .read = readTimeData,
|
|
|
- .release = releaseTimeData,
|
|
|
- .write = NULL};
|
|
|
- UA_QualifiedName dateName;
|
|
|
- UA_QUALIFIEDNAME_ASSIGN(dateName, "the time");
|
|
|
- UA_Server_addDataSourceVariableNode(server, dateDataSource, &UA_NODEID_NULL, &dateName,
|
|
|
- &UA_NODEID_STATIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
|
- &UA_NODEID_STATIC(0, UA_NS0ID_ORGANIZES));
|
|
|
-
|
|
|
-
|
|
|
- UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL,
|
|
|
- .work.methodCall = {.method = printDeviceStatus, .data = NULL} };
|
|
|
- UA_Server_addRepeatedWorkItem(server, &work, 20000000, NULL);
|
|
|
-
|
|
|
-
|
|
|
- UA_DataSource deviceStatusDataSource = (UA_DataSource)
|
|
|
+
|
|
|
+ UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL,
|
|
|
+ .work.methodCall = {.method = printLedStatus, .data = NULL} };
|
|
|
+ UA_Server_addRepeatedWorkItem(server, &work, 20000000, NULL);
|
|
|
+
|
|
|
+
|
|
|
+ UA_DataSource dateDataSource = (UA_DataSource)
|
|
|
{.handle = NULL,
|
|
|
- .read = readDeviceStatus,
|
|
|
- .release = releaseDeviceStatus,
|
|
|
- .write = writeDeviceStatus};
|
|
|
- UA_QualifiedName statusName;
|
|
|
- UA_QUALIFIEDNAME_ASSIGN(statusName, "device status");
|
|
|
- UA_Server_addDataSourceVariableNode(server, deviceStatusDataSource, &UA_NODEID_NULL, &statusName,
|
|
|
- &UA_NODEID_STATIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
|
- &UA_NODEID_STATIC(0, UA_NS0ID_ORGANIZES));
|
|
|
-
|
|
|
- UA_StatusCode retval = UA_Server_run(server, 1, &running);
|
|
|
+ .read = readTimeData,
|
|
|
+ .release = releaseTimeData,
|
|
|
+ .write = NULL};
|
|
|
+ UA_QualifiedName dateName;
|
|
|
+ UA_QUALIFIEDNAME_ASSIGN(dateName, "current time");
|
|
|
+ UA_Server_addDataSourceVariableNode(server, dateDataSource, &UA_NODEID_NULL, &dateName,
|
|
|
+ &UA_NODEID_STATIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
|
+ &UA_NODEID_STATIC(0, UA_NS0ID_ORGANIZES));
|
|
|
+
|
|
|
+ if(!(temperatureFile = fopen("/sys/class/thermal/thermal_zone0/temp", "r"))){
|
|
|
+ UA_LOG_WARNING(logger, UA_LOGGERCATEGORY_USERLAND, "[Linux specific] Can not open temperature file, no temperature node will be added");
|
|
|
+ } else {
|
|
|
+
|
|
|
+ UA_DataSource temperatureDataSource = (UA_DataSource)
|
|
|
+ {.handle = NULL,
|
|
|
+ .read = readTemperature,
|
|
|
+ .release = releaseTemperature,
|
|
|
+ .write = NULL};
|
|
|
+ UA_QualifiedName ledName;
|
|
|
+ UA_QUALIFIEDNAME_ASSIGN(ledName, "cpu temperature");
|
|
|
+ UA_Server_addDataSourceVariableNode(server, temperatureDataSource, &UA_NODEID_NULL, &ledName,
|
|
|
+ &UA_NODEID_STATIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
|
+ &UA_NODEID_STATIC(0, UA_NS0ID_ORGANIZES));
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( !(triggerFile = fopen("/sys/class/leds/led0/trigger", "w"))
|
|
|
+ || !(ledFile = fopen("/sys/class/leds/led0/brightness", "w"))) {
|
|
|
+ UA_LOG_WARNING(logger, UA_LOGGERCATEGORY_USERLAND, "[Raspberry Pi specific] Can not open trigger or LED file (try to run server with sudo if on a Raspberry PI)");
|
|
|
+ UA_LOG_WARNING(logger, UA_LOGGERCATEGORY_USERLAND, "An LED node will be added but no physical LED will be operated");
|
|
|
+ } else {
|
|
|
+
|
|
|
+ fprintf(triggerFile, "%s", "none");
|
|
|
+ fflush(triggerFile);
|
|
|
+
|
|
|
+
|
|
|
+ fprintf(ledFile, "%s", "1");
|
|
|
+ fflush(ledFile);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ UA_DataSource ledStatusDataSource = (UA_DataSource)
|
|
|
+ {.handle = NULL,
|
|
|
+ .read = readLedStatus,
|
|
|
+ .release = releaseLedStatus,
|
|
|
+ .write = writeLedStatus};
|
|
|
+ UA_QualifiedName statusName;
|
|
|
+ UA_QUALIFIEDNAME_ASSIGN(statusName, "status LED");
|
|
|
+ UA_Server_addDataSourceVariableNode(server, ledStatusDataSource, &UA_NODEID_NULL, &statusName,
|
|
|
+ &UA_NODEID_STATIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
|
+ &UA_NODEID_STATIC(0, UA_NS0ID_ORGANIZES));
|
|
|
+
|
|
|
+
|
|
|
+ UA_StatusCode retval = UA_Server_run(server, 1, &running);
|
|
|
+
|
|
|
+
|
|
|
UA_Server_delete(server);
|
|
|
- pthread_rwlock_destroy(&deviceStatusLock);
|
|
|
+
|
|
|
+ if(temperatureFile)
|
|
|
+ fclose(temperatureFile);
|
|
|
+
|
|
|
+ if(triggerFile){
|
|
|
+ fseek(triggerFile, 0, SEEK_SET);
|
|
|
+
|
|
|
+ fprintf(triggerFile, "%s", "mmc0");
|
|
|
+ fclose(triggerFile);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(ledFile){
|
|
|
+ fclose(ledFile);
|
|
|
+ }
|
|
|
+
|
|
|
+#ifdef UA_MULTITHREADING
|
|
|
+ pthread_rwlock_destroy(&writeLock);
|
|
|
+#endif
|
|
|
|
|
|
return retval;
|
|
|
}
|