static void dvsConfigListener(sshsNode node, void *userData, enum sshs_node_attribute_events event, const char *changeKey, enum sshs_node_attr_value_type changeType, union sshs_node_attr_value changeValue) { UNUSED_ARGUMENT(node); caerModuleData moduleData = userData; if (event == SSHS_ATTRIBUTE_MODIFIED) { if (changeType == SSHS_BOOL && caerStrEquals(changeKey, "TimestampReset")) { caerDeviceConfigSet( moduleData->moduleState, EDVS_CONFIG_DVS, EDVS_CONFIG_DVS_TIMESTAMP_RESET, changeValue.boolean); } else if (changeType == SSHS_BOOL && caerStrEquals(changeKey, "Run")) { caerDeviceConfigSet(moduleData->moduleState, EDVS_CONFIG_DVS, EDVS_CONFIG_DVS_RUN, changeValue.boolean); } } }
static void usbConfigListener(sshsNode node, void *userData, enum sshs_node_attribute_events event, const char *changeKey, enum sshs_node_attr_value_type changeType, union sshs_node_attr_value changeValue) { UNUSED_ARGUMENT(node); caerModuleData moduleData = userData; if (event == ATTRIBUTE_MODIFIED) { if (changeType == INT && caerStrEquals(changeKey, "BufferNumber")) { caerDeviceConfigSet(moduleData->moduleState, CAER_HOST_CONFIG_USB, CAER_HOST_CONFIG_USB_BUFFER_NUMBER, U32T(changeValue.iint)); } else if (changeType == INT && caerStrEquals(changeKey, "BufferSize")) { caerDeviceConfigSet(moduleData->moduleState, CAER_HOST_CONFIG_USB, CAER_HOST_CONFIG_USB_BUFFER_SIZE, U32T(changeValue.iint)); } } }
static void systemConfigListener(sshsNode node, void *userData, enum sshs_node_attribute_events event, const char *changeKey, enum sshs_node_attr_value_type changeType, union sshs_node_attr_value changeValue) { UNUSED_ARGUMENT(node); caerModuleData moduleData = userData; if (event == SSHS_ATTRIBUTE_MODIFIED) { if (changeType == SSHS_INT && caerStrEquals(changeKey, "PacketContainerMaxPacketSize")) { caerDeviceConfigSet(moduleData->moduleState, CAER_HOST_CONFIG_PACKETS, CAER_HOST_CONFIG_PACKETS_MAX_CONTAINER_PACKET_SIZE, U32T(changeValue.iint)); } else if (changeType == SSHS_INT && caerStrEquals(changeKey, "PacketContainerInterval")) { caerDeviceConfigSet(moduleData->moduleState, CAER_HOST_CONFIG_PACKETS, CAER_HOST_CONFIG_PACKETS_MAX_CONTAINER_INTERVAL, U32T(changeValue.iint)); } } }
void caerConfigInit(const char *configFile, int argc, char *argv[]) { // If configFile is NULL, no config file will be accessed at all, // and neither will it be written back at shutdown. if (configFile != NULL) { // Let's try to open the file for reading, or create it. int configFileFd = open(configFile, O_RDONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP); if (configFileFd >= 0) { // File opened for reading successfully. // This means it exists and we can access it, so let's remember // it for writing the config later at shutdown (if permitted). caerConfigFilePath = realpath(configFile, NULL); // Determine if there is actual content to parse first. struct stat configFileStat; fstat(configFileFd, &configFileStat); if (configFileStat.st_size > 0) { sshsNodeImportSubTreeFromXML(sshsGetNode(sshsGetGlobal(), "/"), configFileFd, true); } close(configFileFd); // Ensure configuration is written back at shutdown. atexit(&caerConfigShutDownWriteBack); } else { caerLog(CAER_LOG_EMERGENCY, "Config", "Could not create and/or read from the configuration file '%s'. Error: %d.", configFile, errno); exit(EXIT_FAILURE); } } else { caerLog(CAER_LOG_EMERGENCY, "Config", "No configuration file defined, using default values for everything."); } // Override with command line arguments if requested. if (argc > 1) { // Format: -o node key type value (5 arguments). Equal to caerctl format. for (size_t i = 1; i < (size_t) argc; i += 5) { if ((i + 4) < (size_t) argc && caerStrEquals(argv[i], "-o")) { sshsNode node = sshsGetNode(sshsGetGlobal(), argv[i + 1]); if (node == NULL) { caerLog(CAER_LOG_EMERGENCY, "Config", "SSHS Node %s doesn't exist.", argv[i + 1]); continue; } if (!sshsNodeStringToNodeConverter(node, argv[i + 2], argv[i + 3], argv[i + 4])) { caerLog(CAER_LOG_EMERGENCY, "Config", "Failed to convert attribute %s of type %s with value %s.", argv[i + 2], argv[i + 3], argv[i + 4]); } } } } }
static void logLevelListener(sshsNode node, void *userData, enum sshs_node_attribute_events event, const char *changeKey, enum sshs_node_attr_value_type changeType, union sshs_node_attr_value changeValue) { UNUSED_ARGUMENT(node); caerModuleData moduleData = userData; if (event == SSHS_ATTRIBUTE_MODIFIED && changeType == SSHS_INT && caerStrEquals(changeKey, "logLevel")) { caerDeviceConfigSet( moduleData->moduleState, CAER_HOST_CONFIG_LOG, CAER_HOST_CONFIG_LOG_LEVEL, U32T(changeValue.iint)); } }
static void serialConfigListener(sshsNode node, void *userData, enum sshs_node_attribute_events event, const char *changeKey, enum sshs_node_attr_value_type changeType, union sshs_node_attr_value changeValue) { UNUSED_ARGUMENT(node); caerModuleData moduleData = userData; if (event == SSHS_ATTRIBUTE_MODIFIED) { if (changeType == SSHS_INT && caerStrEquals(changeKey, "ReadSize")) { caerDeviceConfigSet(moduleData->moduleState, CAER_HOST_CONFIG_SERIAL, CAER_HOST_CONFIG_SERIAL_READ_SIZE, U32T(changeValue.iint)); } } }
static void usbConfigListener(sshsNode node, void *userData, enum sshs_node_attribute_events event, const char *changeKey, enum sshs_node_attr_value_type changeType, union sshs_node_attr_value changeValue) { UNUSED_ARGUMENT(node); caerModuleData moduleData = userData; if (event == SSHS_ATTRIBUTE_MODIFIED) { if (changeType == SSHS_INT && caerStrEquals(changeKey, "BufferNumber")) { caerDeviceConfigSet(moduleData->moduleState, CAER_HOST_CONFIG_USB, CAER_HOST_CONFIG_USB_BUFFER_NUMBER, U32T(changeValue.iint)); } else if (changeType == SSHS_INT && caerStrEquals(changeKey, "BufferSize")) { caerDeviceConfigSet(moduleData->moduleState, CAER_HOST_CONFIG_USB, CAER_HOST_CONFIG_USB_BUFFER_SIZE, U32T(changeValue.iint)); } else if (changeType == SSHS_INT && caerStrEquals(changeKey, "EarlyPacketDelay")) { caerDeviceConfigSet( moduleData->moduleState, DAVIS_CONFIG_USB, DAVIS_CONFIG_USB_EARLY_PACKET_DELAY, U32T(changeValue.iint)); } else if (changeType == SSHS_BOOL && caerStrEquals(changeKey, "Run")) { caerDeviceConfigSet(moduleData->moduleState, DAVIS_CONFIG_USB, DAVIS_CONFIG_USB_RUN, changeValue.boolean); } } }
static void caerModuleShutdownListener(sshsNode node, void *userData, enum sshs_node_attribute_events event, const char *changeKey, enum sshs_node_attr_value_type changeType, union sshs_node_attr_value changeValue) { UNUSED_ARGUMENT(node); caerModuleData data = userData; if (event == ATTRIBUTE_MODIFIED && changeType == BOOL && caerStrEquals(changeKey, "shutdown")) { // Shutdown changed, let's see. if (changeValue.boolean == true) { atomic_store(&data->running, false); } else { atomic_store(&data->running, true); } } }
static char *getFullFilePath(caerModuleData moduleData, const char *directory, const char *prefix) { // First get time suffix string. time_t currentTimeEpoch = time(NULL); #if defined(OS_WINDOWS) // localtime() is thread-safe on Windows (and there is no localtime_r() at all). struct tm *currentTime = localtime(¤tTimeEpoch); #else // From localtime_r() man-page: "According to POSIX.1-2004, localtime() // is required to behave as though tzset(3) was called, while // localtime_r() does not have this requirement." // So we make sure to call it here, to be portable. tzset(); struct tm currentTimeStruct; struct tm *currentTime = ¤tTimeStruct; localtime_r(¤tTimeEpoch, currentTime); #endif // Following time format uses exactly 19 characters (5 separators, // 4 year, 2 month, 2 day, 2 hours, 2 minutes, 2 seconds). size_t currentTimeStringLength = 19; char currentTimeString[currentTimeStringLength + 1]; // + 1 for terminating NUL byte. strftime(currentTimeString, currentTimeStringLength + 1, "%Y_%m_%d_%H_%M_%S", currentTime); if (caerStrEquals(prefix, "")) { // If the prefix is the empty string, use a minimal one. prefix = DEFAULT_PREFIX; } // Assemble together: directory/prefix-time.aedat size_t filePathLength = strlen(directory) + strlen(prefix) + currentTimeStringLength + 9; // 1 for the directory/prefix separating slash, 1 for prefix-time separating // dash, 6 for file extension, 1 for terminating NUL byte = +9. char *filePath = malloc(filePathLength); if (filePath == NULL) { caerModuleLog(moduleData, CAER_LOG_CRITICAL, "Unable to allocate memory for full file path."); return (NULL); } snprintf(filePath, filePathLength, "%s/%s-%s.aedat", directory, prefix, currentTimeString); return (filePath); }
static void handleNewServerConnections(outputCommonState state) { // First let's see if any new connections are waiting on the listening // socket to be accepted. This returns right away (non-blocking). socklen_t clientAddrLength = sizeof(struct sockaddr_in); struct sockaddr_in clientAddr; memset(&clientAddr, 0, clientAddrLength); int acceptResult = accept(state->fileDescriptors->serverFd, (struct sockaddr *) &clientAddr, &clientAddrLength); if (acceptResult < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK) { // Only log real failure. EAGAIN/EWOULDBLOCK just means no // connections are present for non-blocking accept() right now. caerLog(CAER_LOG_ERROR, state->parentModule->moduleSubSystemString, "TCP server accept() failed. Error: %d.", errno); } return; } // New connection present! // Put it in the list of FDs and send header, if there is space left, or close. for (size_t i = 0; i < state->fileDescriptors->fdsSize; i++) { if (state->fileDescriptors->fds[i] == -1) { caerLog(CAER_LOG_DEBUG, state->parentModule->moduleSubSystemString, "Accepted new TCP connection from client (fd %d).", acceptResult); // Commit current buffer, so that for the new clients, it's empty, and they // don't get packets and data that are possibly cut in the middle. commitOutputBuffer(state); // Empty place in FD list, add this one. state->fileDescriptors->fds[i] = acceptResult; // Successfully connected, send header to client. sendNetworkHeader(state, &state->fileDescriptors->fds[i]); // Add client IP to list. This is a comma separated string. char *connectedClientsStr = sshsNodeGetString(state->parentModule->moduleNode, "connectedClients"); size_t newConnectedClientStrLength = strlen(connectedClientsStr) + 1 + INET_ADDRSTRLEN + 1; char *newConnectedClientsStr = realloc(connectedClientsStr, newConnectedClientStrLength); if (newConnectedClientsStr == NULL) { free(connectedClientsStr); caerLog(CAER_LOG_ERROR, state->parentModule->moduleSubSystemString, "Failed to update connectedClients information."); return; } const char *clientAddrStr = inet_ntop(AF_INET, &clientAddr.sin_addr, (char[INET_ADDRSTRLEN] ) { 0x00 }, INET_ADDRSTRLEN); if (!caerStrEquals(newConnectedClientsStr, "")) { // Only prepend comma if the string wasn't empty before. strncat(newConnectedClientsStr, ",", 1); } strncat(newConnectedClientsStr, clientAddrStr, INET_ADDRSTRLEN); sshsNodePutString(state->parentModule->moduleNode, "connectedClients", newConnectedClientsStr); free(newConnectedClientsStr); return; }
static void biasConfigListener(sshsNode node, void *userData, enum sshs_node_attribute_events event, const char *changeKey, enum sshs_node_attr_value_type changeType, union sshs_node_attr_value changeValue) { UNUSED_ARGUMENT(node); caerModuleData moduleData = userData; if (event == ATTRIBUTE_MODIFIED) { if (changeType == INT && caerStrEquals(changeKey, "cas")) { caerDeviceConfigSet(moduleData->moduleState, DVS128_CONFIG_BIAS, DVS128_CONFIG_BIAS_CAS, U32T(changeValue.iint)); } else if (changeType == INT && caerStrEquals(changeKey, "injGnd")) { caerDeviceConfigSet(moduleData->moduleState, DVS128_CONFIG_BIAS, DVS128_CONFIG_BIAS_INJGND, U32T(changeValue.iint)); } else if (changeType == INT && caerStrEquals(changeKey, "reqPd")) { caerDeviceConfigSet(moduleData->moduleState, DVS128_CONFIG_BIAS, DVS128_CONFIG_BIAS_REQPD, U32T(changeValue.iint)); } else if (changeType == INT && caerStrEquals(changeKey, "puX")) { caerDeviceConfigSet(moduleData->moduleState, DVS128_CONFIG_BIAS, DVS128_CONFIG_BIAS_PUX, U32T(changeValue.iint)); } else if (changeType == INT && caerStrEquals(changeKey, "diffOff")) { caerDeviceConfigSet(moduleData->moduleState, DVS128_CONFIG_BIAS, DVS128_CONFIG_BIAS_DIFFOFF, U32T(changeValue.iint)); } else if (changeType == INT && caerStrEquals(changeKey, "req")) { caerDeviceConfigSet(moduleData->moduleState, DVS128_CONFIG_BIAS, DVS128_CONFIG_BIAS_REQ, U32T(changeValue.iint)); } else if (changeType == INT && caerStrEquals(changeKey, "refr")) { caerDeviceConfigSet(moduleData->moduleState, DVS128_CONFIG_BIAS, DVS128_CONFIG_BIAS_REFR, U32T(changeValue.iint)); } else if (changeType == INT && caerStrEquals(changeKey, "puY")) { caerDeviceConfigSet(moduleData->moduleState, DVS128_CONFIG_BIAS, DVS128_CONFIG_BIAS_PUY, U32T(changeValue.iint)); } else if (changeType == INT && caerStrEquals(changeKey, "diffOn")) { caerDeviceConfigSet(moduleData->moduleState, DVS128_CONFIG_BIAS, DVS128_CONFIG_BIAS_DIFFON, U32T(changeValue.iint)); } else if (changeType == INT && caerStrEquals(changeKey, "diff")) { caerDeviceConfigSet(moduleData->moduleState, DVS128_CONFIG_BIAS, DVS128_CONFIG_BIAS_DIFF, U32T(changeValue.iint)); } else if (changeType == INT && caerStrEquals(changeKey, "foll")) { caerDeviceConfigSet(moduleData->moduleState, DVS128_CONFIG_BIAS, DVS128_CONFIG_BIAS_FOLL, U32T(changeValue.iint)); } else if (changeType == INT && caerStrEquals(changeKey, "pr")) { caerDeviceConfigSet(moduleData->moduleState, DVS128_CONFIG_BIAS, DVS128_CONFIG_BIAS_PR, U32T(changeValue.iint)); } } }