static bool caerOutputFileInit(caerModuleData moduleData) { // First, always create all needed setting nodes, set their default values // and add their listeners. char *userHomeDir = getUserHomeDirectory(moduleData); if (userHomeDir == NULL) { // caerModuleLog() called inside getUserHomeDirectory(). return (false); } sshsNodeCreateString(moduleData->moduleNode, "directory", userHomeDir, 1, (PATH_MAX - MAX_PREFIX_LENGTH), SSHS_FLAGS_NORMAL, "Directory to write output data files in."); free(userHomeDir); // Support file-chooser in GUI, select any directory. sshsNodeCreateAttributeFileChooser(moduleData->moduleNode, "directory", "DIRECTORY"); sshsNodeCreateString(moduleData->moduleNode, "prefix", DEFAULT_PREFIX, 1, MAX_PREFIX_LENGTH, SSHS_FLAGS_NORMAL, "Output data files name prefix."); // Generate current file name and open it. char *directory = sshsNodeGetString(moduleData->moduleNode, "directory"); char *prefix = sshsNodeGetString(moduleData->moduleNode, "prefix"); char *filePath = getFullFilePath(moduleData, directory, prefix); free(directory); free(prefix); if (filePath == NULL) { // caerModuleLog() called inside getFullFilePath(). return (false); } int fileFd = open(filePath, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP); if (fileFd < 0) { caerModuleLog(moduleData, CAER_LOG_CRITICAL, "Could not create or open output file '%s' for writing. Error: %d.", filePath, errno); free(filePath); return (false); } caerModuleLog(moduleData, CAER_LOG_INFO, "Opened output file '%s' successfully for writing.", filePath); free(filePath); if (!caerOutputCommonInit(moduleData, fileFd, NULL)) { close(fileFd); return (false); } return (true); }
static bool caerInputUnixSocketInit(caerModuleData moduleData) { // First, always create all needed setting nodes, set their default values // and add their listeners. sshsNodeCreateString(moduleData->moduleNode, "socketPath", "/tmp/caer.sock", 2, PATH_MAX, SSHS_FLAGS_NORMAL, "Unix Socket path for reading input data."); // Open an existing Unix local socket at a known path, where we'll write to. int sockFd = socket(AF_UNIX, SOCK_STREAM, 0); if (sockFd < 0) { caerModuleLog(moduleData, CAER_LOG_CRITICAL, "Could not create local Unix socket. Error: %d.", errno); return (false); } struct sockaddr_un unixSocketAddr; memset(&unixSocketAddr, 0, sizeof(struct sockaddr_un)); unixSocketAddr.sun_family = AF_UNIX; char *socketPath = sshsNodeGetString(moduleData->moduleNode, "socketPath"); strncpy(unixSocketAddr.sun_path, socketPath, sizeof(unixSocketAddr.sun_path) - 1); unixSocketAddr.sun_path[sizeof(unixSocketAddr.sun_path) - 1] = '\0'; // Ensure NUL terminated string. free(socketPath); // Connect socket to above address. if (connect(sockFd, (struct sockaddr *) &unixSocketAddr, sizeof(struct sockaddr_un)) < 0) { close(sockFd); caerModuleLog(moduleData, CAER_LOG_CRITICAL, "Could not connect to local Unix socket. Error: %d.", errno); return (false); } if (!caerInputCommonInit(moduleData, sockFd, true, false)) { close(sockFd); return (false); } caerModuleLog(moduleData, CAER_LOG_INFO, "Local Unix socket ready at '%s'.", unixSocketAddr.sun_path); return (true); }
static int caerConfigServerRunner(void *inPtr) { UNUSED_ARGUMENT(inPtr); // Get the right configuration node first. sshsNode serverNode = sshsGetNode(sshsGetGlobal(), "/server/"); // Ensure default values are present. sshsNodePutStringIfAbsent(serverNode, "ipAddress", "127.0.0.1"); sshsNodePutIntIfAbsent(serverNode, "portNumber", 4040); sshsNodePutShortIfAbsent(serverNode, "backlogSize", 5); sshsNodePutShortIfAbsent(serverNode, "concurrentConnections", 5); // Open a TCP server socket for configuration handling. // TCP chosen for reliability, which is more important here than speed. int configServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (configServerSocket < 0) { caerLog(CAER_LOG_CRITICAL, "Config Server", "Could not create server socket. Error: %d.", errno); return (EXIT_FAILURE); } // Make socket address reusable right away. socketReuseAddr(configServerSocket, true); struct sockaddr_in configServerAddress; memset(&configServerAddress, 0, sizeof(struct sockaddr_in)); configServerAddress.sin_family = AF_INET; configServerAddress.sin_port = htons(sshsNodeGetInt(serverNode, "portNumber")); char *ipAddress = sshsNodeGetString(serverNode, "ipAddress"); inet_aton(ipAddress, &configServerAddress.sin_addr); // htonl() is implicit here. free(ipAddress); // Bind socket to above address. if (bind(configServerSocket, (struct sockaddr *) &configServerAddress, sizeof(struct sockaddr_in)) < 0) { caerLog(CAER_LOG_CRITICAL, "Config Server", "Could not bind server socket. Error: %d.", errno); return (EXIT_FAILURE); } // Listen to new connections on the socket. if (listen(configServerSocket, sshsNodeGetShort(serverNode, "backlogSize")) < 0) { caerLog(CAER_LOG_CRITICAL, "Config Server", "Could not listen on server socket. Error: %d.", errno); return (EXIT_FAILURE); } // Control message format: 1 byte ACTION, 1 byte TYPE, 2 bytes EXTRA_LEN, // 2 bytes NODE_LEN, 2 bytes KEY_LEN, 2 bytes VALUE_LEN, then up to 4086 // bytes split between EXTRA, NODE, KEY, VALUE (with 4 bytes for NUL). // Basically: (EXTRA_LEN + NODE_LEN + KEY_LEN + VALUE_LEN) <= 4086. // EXTRA, NODE, KEY, VALUE have to be NUL terminated, and their length // must include the NUL termination byte. // This results in a maximum message size of 4096 bytes (4KB). uint8_t configServerBuffer[4096]; caerLog(CAER_LOG_NOTICE, "Config Server", "Ready and listening on %s:%" PRIu16 ".", inet_ntoa(configServerAddress.sin_addr), ntohs(configServerAddress.sin_port)); size_t connections = (size_t) sshsNodeGetShort(serverNode, "concurrentConnections") + 1;// +1 for listening socket. struct pollfd pollSockets[connections]; // Initially ignore all pollfds, and react only to POLLIN events. for (size_t i = 0; i < connections; i++) { pollSockets[i].fd = -1; pollSockets[i].events = POLLIN; } // First pollfd is the listening socket, always. pollSockets[0].fd = configServerSocket; while (atomic_load_explicit(&configServerThread.running, memory_order_relaxed)) { int pollResult = poll(pollSockets, (nfds_t) connections, 1000); if (pollResult > 0) { // First let's check if there's a new connection waiting to be accepted. if ((pollSockets[0].revents & POLLIN) != 0) { int newClientSocket = accept(pollSockets[0].fd, NULL, NULL); if (newClientSocket >= 0) { // Put it in the list of watched fds if possible, or close. bool putInFDList = false; for (size_t i = 1; i < connections; i++) { if (pollSockets[i].fd == -1) { // Empty place in watch list, add this one. pollSockets[i].fd = newClientSocket; putInFDList = true; break; } } // No space for new connection, just close it (client will exit). if (!putInFDList) { close(newClientSocket); caerLog(CAER_LOG_DEBUG, "Config Server", "Rejected client (fd %d), queue full.", newClientSocket); } else { caerLog(CAER_LOG_DEBUG, "Config Server", "Accepted new connection from client (fd %d).", newClientSocket); } } } for (size_t i = 1; i < connections; i++) { // Work on existing connections, valid and with read events. if (pollSockets[i].fd != -1 && (pollSockets[i].revents & POLLIN) != 0) { // Accepted client connection, let's get all the data we need: first // the 10 byte header, and then whatever remains based on that. if (!recvUntilDone(pollSockets[i].fd, configServerBuffer, 10)) { // Closed on other side or error. Let's just close here too. close(pollSockets[i].fd); caerLog(CAER_LOG_DEBUG, "Config Server", "Disconnected client on recv (fd %d).", pollSockets[i].fd); pollSockets[i].fd = -1; continue; } // Decode header fields (all in little-endian). uint8_t action = configServerBuffer[0]; uint8_t type = configServerBuffer[1]; uint16_t extraLength = le16toh(*(uint16_t * )(configServerBuffer + 2)); uint16_t nodeLength = le16toh(*(uint16_t * )(configServerBuffer + 4)); uint16_t keyLength = le16toh(*(uint16_t * )(configServerBuffer + 6)); uint16_t valueLength = le16toh(*(uint16_t * )(configServerBuffer + 8)); // Total length to get for command. size_t readLength = (size_t) (extraLength + nodeLength + keyLength + valueLength); if (!recvUntilDone(pollSockets[i].fd, configServerBuffer + 10, readLength)) { // Closed on other side or error. Let's just close here too. close(pollSockets[i].fd); caerLog(CAER_LOG_DEBUG, "Config Server", "Disconnected client on recv (fd %d).", pollSockets[i].fd); pollSockets[i].fd = -1; continue; } // Now we have everything. The header fields are already // fully decoded: handle request (and send back data eventually). caerConfigServerHandleRequest(pollSockets[i].fd, action, type, configServerBuffer + 10, extraLength, configServerBuffer + 10 + extraLength, nodeLength, configServerBuffer + 10 + extraLength + nodeLength, keyLength, configServerBuffer + 10 + extraLength + nodeLength + keyLength, valueLength); } } } else if (pollResult < 0) { caerLog(CAER_LOG_ALERT, "Config Server", "poll() failed. Error: %d.", errno); } // pollResult == 0 just means nothing to do (timeout reached). } // Shutdown! close(configServerSocket); return (EXIT_SUCCESS); }
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 bool caerOutputUnixSocketServerInit(caerModuleData moduleData) { // First, always create all needed setting nodes, set their default values // and add their listeners. sshsNodeCreateString(moduleData->moduleNode, "socketPath", "/tmp/caer.sock", 2, PATH_MAX, SSHS_FLAGS_NORMAL, "Unix Socket path for writing output data (server mode, create new socket)."); sshsNodeCreateInt( moduleData->moduleNode, "backlogSize", 5, 1, 32, SSHS_FLAGS_NORMAL, "Maximum number of pending connections."); sshsNodeCreateInt(moduleData->moduleNode, "concurrentConnections", 10, 1, 128, SSHS_FLAGS_NORMAL, "Maximum number of concurrent active connections."); // Allocate memory. size_t numClients = (size_t) sshsNodeGetInt(moduleData->moduleNode, "concurrentConnections"); outputCommonNetIO streams = malloc(sizeof(*streams) + (numClients * sizeof(uv_stream_t *))); if (streams == NULL) { caerModuleLog(moduleData, CAER_LOG_ERROR, "Failed to allocate memory for streams structure."); return (false); } streams->server = malloc(sizeof(uv_pipe_t)); if (streams->server == NULL) { free(streams); caerModuleLog(moduleData, CAER_LOG_ERROR, "Failed to allocate memory for network server."); return (false); } // Initialize common info. streams->isTCP = false; streams->isUDP = false; streams->isPipe = true; streams->activeClients = 0; streams->clientsSize = numClients; for (size_t i = 0; i < streams->clientsSize; i++) { streams->clients[i] = NULL; } // Remember address. streams->address = sshsNodeGetString(moduleData->moduleNode, "socketPath"); streams->server->data = streams; // Initialize loop and network handles. int retVal = uv_loop_init(&streams->loop); UV_RET_CHECK(retVal, moduleData->moduleSubSystemString, "uv_loop_init", free(streams->server); free(streams->address); free(streams); return (false)); retVal = uv_pipe_init(&streams->loop, (uv_pipe_t *) streams->server, false); UV_RET_CHECK(retVal, moduleData->moduleSubSystemString, "uv_pipe_init", uv_loop_close(&streams->loop); free(streams->server); free(streams->address); free(streams); return (false)); retVal = uv_pipe_bind((uv_pipe_t *) streams->server, streams->address); UV_RET_CHECK(retVal, moduleData->moduleSubSystemString, "uv_pipe_bind", libuvCloseLoopHandles(&streams->loop); uv_loop_close(&streams->loop); free(streams->address); free(streams); return (false)); retVal = uv_listen( streams->server, sshsNodeGetInt(moduleData->moduleNode, "backlogSize"), &caerOutputCommonOnServerConnection); UV_RET_CHECK(retVal, moduleData->moduleSubSystemString, "uv_listen", libuvCloseLoopHandles(&streams->loop); uv_loop_close(&streams->loop); free(streams->address); free(streams); return (false)); // Start. if (!caerOutputCommonInit(moduleData, -1, streams)) { libuvCloseLoopHandles(&streams->loop); uv_loop_close(&streams->loop); free(streams->address); free(streams); return (false); } return (true); }
static bool caerOutputUnixSocketServerInit(caerModuleData moduleData) { // First, always create all needed setting nodes, set their default values // and add their listeners. sshsNodePutStringIfAbsent(moduleData->moduleNode, "socketPath", "/tmp/caer.sock"); sshsNodePutShortIfAbsent(moduleData->moduleNode, "backlogSize", 5); sshsNodePutShortIfAbsent(moduleData->moduleNode, "concurrentConnections", 10); // Open a Unix local socket on a known path, to be accessed by other processes (server-like mode). int serverSockFd = socket(AF_UNIX, SOCK_STREAM, 0); if (serverSockFd < 0) { caerLog(CAER_LOG_CRITICAL, moduleData->moduleSubSystemString, "Could not create local Unix socket. Error: %d.", errno); return (false); } struct sockaddr_un unixSocketAddr; memset(&unixSocketAddr, 0, sizeof(struct sockaddr_un)); unixSocketAddr.sun_family = AF_UNIX; char *socketPath = sshsNodeGetString(moduleData->moduleNode, "socketPath"); strncpy(unixSocketAddr.sun_path, socketPath, sizeof(unixSocketAddr.sun_path) - 1); unixSocketAddr.sun_path[sizeof(unixSocketAddr.sun_path) - 1] = '\0'; // Ensure NUL terminated string. free(socketPath); // Bind socket to above address. if (bind(serverSockFd, (struct sockaddr *) &unixSocketAddr, sizeof(struct sockaddr_un)) < 0) { close(serverSockFd); caerLog(CAER_LOG_CRITICAL, moduleData->moduleSubSystemString, "Could not bind local Unix socket. Error: %d.", errno); return (false); } // Listen to new connections on the socket. if (listen(serverSockFd, sshsNodeGetShort(moduleData->moduleNode, "backlogSize")) < 0) { close(serverSockFd); caerLog(CAER_LOG_CRITICAL, moduleData->moduleSubSystemString, "Could not listen on local Unix socket. Error: %d.", errno); return (false); } outputCommonFDs fileDescriptors = caerOutputCommonAllocateFdArray( (size_t) sshsNodeGetShort(moduleData->moduleNode, "concurrentConnections")); if (fileDescriptors == NULL) { close(serverSockFd); caerLog(CAER_LOG_CRITICAL, moduleData->moduleSubSystemString, "Unable to allocate memory for file descriptors."); return (false); } fileDescriptors->serverFd = serverSockFd; if (!caerOutputCommonInit(moduleData, fileDescriptors, true, false)) { close(serverSockFd); free(fileDescriptors); return (false); } caerLog(CAER_LOG_INFO, moduleData->moduleSubSystemString, "Local Unix socket ready at '%s'.", unixSocketAddr.sun_path); return (true); }
static bool caerInputDVS128Init(caerModuleData moduleData) { caerLog(CAER_LOG_DEBUG, moduleData->moduleSubSystemString, "Initializing module ..."); // USB port/bus/SN settings/restrictions. // These can be used to force connection to one specific device at startup. sshsNodePutShortIfAbsent(moduleData->moduleNode, "BusNumber", 0); sshsNodePutShortIfAbsent(moduleData->moduleNode, "DevAddress", 0); sshsNodePutStringIfAbsent(moduleData->moduleNode, "SerialNumber", ""); // Add auto-restart setting. sshsNodePutBoolIfAbsent(moduleData->moduleNode, "Auto-Restart", true); // Start data acquisition, and correctly notify mainloop of new data and module of exceptional // shutdown cases (device pulled, ...). char *serialNumber = sshsNodeGetString(moduleData->moduleNode, "SerialNumber"); moduleData->moduleState = caerDeviceOpen(moduleData->moduleID, CAER_DEVICE_DVS128, U8T(sshsNodeGetShort(moduleData->moduleNode, "BusNumber")), U8T(sshsNodeGetShort(moduleData->moduleNode, "DevAddress")), serialNumber); free(serialNumber); if (moduleData->moduleState == NULL) { // Failed to open device. return (false); } // Put global source information into SSHS. struct caer_dvs128_info devInfo = caerDVS128InfoGet(moduleData->moduleState); sshsNode sourceInfoNode = sshsGetRelativeNode(moduleData->moduleNode, "sourceInfo/"); sshsNodePutShort(sourceInfoNode, "logicVersion", devInfo.logicVersion); sshsNodePutBool(sourceInfoNode, "deviceIsMaster", devInfo.deviceIsMaster); sshsNodePutShort(sourceInfoNode, "dvsSizeX", devInfo.dvsSizeX); sshsNodePutShort(sourceInfoNode, "dvsSizeY", devInfo.dvsSizeY); // Put source information for "virtual" APS frame that can be used to display and debug filter information. sshsNodePutShort(sourceInfoNode, "apsSizeX", devInfo.dvsSizeX); sshsNodePutShort(sourceInfoNode, "apsSizeY", devInfo.dvsSizeY); caerModuleSetSubSystemString(moduleData, devInfo.deviceString); // Ensure good defaults for data acquisition settings. // No blocking behavior due to mainloop notification, and no auto-start of // all producers to ensure cAER settings are respected. caerDeviceConfigSet(moduleData->moduleState, CAER_HOST_CONFIG_DATAEXCHANGE, CAER_HOST_CONFIG_DATAEXCHANGE_BLOCKING, false); caerDeviceConfigSet(moduleData->moduleState, CAER_HOST_CONFIG_DATAEXCHANGE, CAER_HOST_CONFIG_DATAEXCHANGE_START_PRODUCERS, false); caerDeviceConfigSet(moduleData->moduleState, CAER_HOST_CONFIG_DATAEXCHANGE, CAER_HOST_CONFIG_DATAEXCHANGE_STOP_PRODUCERS, true); // Create default settings and send them to the device. createDefaultConfiguration(moduleData); sendDefaultConfiguration(moduleData); // Start data acquisition. bool ret = caerDeviceDataStart(moduleData->moduleState, &mainloopDataNotifyIncrease, &mainloopDataNotifyDecrease, caerMainloopGetReference(), &moduleShutdownNotify, moduleData->moduleNode); if (!ret) { // Failed to start data acquisition, close device and exit. caerDeviceClose((caerDeviceHandle *) &moduleData->moduleState); return (false); } return (true); }
static bool caerInputEDVSInit(caerModuleData moduleData) { caerModuleLog(moduleData, CAER_LOG_DEBUG, "Initializing module ..."); // Start data acquisition, and correctly notify mainloop of new data and module of exceptional // shutdown cases (device pulled, ...). char *serialPortName = sshsNodeGetString(moduleData->moduleNode, "serialPort"); moduleData->moduleState = caerDeviceOpenSerial(U16T(moduleData->moduleID), CAER_DEVICE_EDVS, serialPortName, U32T(sshsNodeGetInt(moduleData->moduleNode, "baudRate"))); free(serialPortName); if (moduleData->moduleState == NULL) { // Failed to open device. return (false); } // Initialize per-device log-level to module log-level. caerDeviceConfigSet(moduleData->moduleState, CAER_HOST_CONFIG_LOG, CAER_HOST_CONFIG_LOG_LEVEL, atomic_load(&moduleData->moduleLogLevel)); // Put global source information into SSHS. struct caer_edvs_info devInfo = caerEDVSInfoGet(moduleData->moduleState); sshsNode sourceInfoNode = sshsGetRelativeNode(moduleData->moduleNode, "sourceInfo/"); sshsNodeCreateBool(sourceInfoNode, "deviceIsMaster", devInfo.deviceIsMaster, SSHS_FLAGS_READ_ONLY | SSHS_FLAGS_NO_EXPORT, "Timestamp synchronization support: device master status."); sshsNodeCreateInt(sourceInfoNode, "polaritySizeX", devInfo.dvsSizeX, devInfo.dvsSizeX, devInfo.dvsSizeX, SSHS_FLAGS_READ_ONLY | SSHS_FLAGS_NO_EXPORT, "Polarity events width."); sshsNodeCreateInt(sourceInfoNode, "polaritySizeY", devInfo.dvsSizeY, devInfo.dvsSizeY, devInfo.dvsSizeY, SSHS_FLAGS_READ_ONLY | SSHS_FLAGS_NO_EXPORT, "Polarity events height."); // Put source information for generic visualization, to be used to display and debug filter information. sshsNodeCreateInt(sourceInfoNode, "dataSizeX", devInfo.dvsSizeX, devInfo.dvsSizeX, devInfo.dvsSizeX, SSHS_FLAGS_READ_ONLY | SSHS_FLAGS_NO_EXPORT, "Data width."); sshsNodeCreateInt(sourceInfoNode, "dataSizeY", devInfo.dvsSizeY, devInfo.dvsSizeY, devInfo.dvsSizeY, SSHS_FLAGS_READ_ONLY | SSHS_FLAGS_NO_EXPORT, "Data height."); // Generate source string for output modules. size_t sourceStringLength = (size_t) snprintf(NULL, 0, "#Source %" PRIu16 ": eDVS4337\r\n", moduleData->moduleID); char sourceString[sourceStringLength + 1]; snprintf(sourceString, sourceStringLength + 1, "#Source %" PRIu16 ": eDVS4337\r\n", moduleData->moduleID); sourceString[sourceStringLength] = '\0'; sshsNodeCreateString(sourceInfoNode, "sourceString", sourceString, sourceStringLength, sourceStringLength, SSHS_FLAGS_READ_ONLY | SSHS_FLAGS_NO_EXPORT, "Device source information."); // Ensure good defaults for data acquisition settings. // No blocking behavior due to mainloop notification, and no auto-start of // all producers to ensure cAER settings are respected. caerDeviceConfigSet( moduleData->moduleState, CAER_HOST_CONFIG_DATAEXCHANGE, CAER_HOST_CONFIG_DATAEXCHANGE_BLOCKING, false); caerDeviceConfigSet( moduleData->moduleState, CAER_HOST_CONFIG_DATAEXCHANGE, CAER_HOST_CONFIG_DATAEXCHANGE_START_PRODUCERS, false); caerDeviceConfigSet( moduleData->moduleState, CAER_HOST_CONFIG_DATAEXCHANGE, CAER_HOST_CONFIG_DATAEXCHANGE_STOP_PRODUCERS, true); // Create default settings and send them to the device. sendDefaultConfiguration(moduleData); // Start data acquisition. bool ret = caerDeviceDataStart(moduleData->moduleState, &caerMainloopDataNotifyIncrease, &caerMainloopDataNotifyDecrease, NULL, &moduleShutdownNotify, moduleData->moduleNode); if (!ret) { // Failed to start data acquisition, close device and exit. caerDeviceClose((caerDeviceHandle *) &moduleData->moduleState); return (false); } // Add config listeners last, to avoid having them dangling if Init doesn't succeed. sshsNode biasNode = sshsGetRelativeNode(moduleData->moduleNode, "bias/"); sshsNodeAddAttributeListener(biasNode, moduleData, &biasConfigListener); sshsNode dvsNode = sshsGetRelativeNode(moduleData->moduleNode, "dvs/"); sshsNodeAddAttributeListener(dvsNode, moduleData, &dvsConfigListener); sshsNode serialNode = sshsGetRelativeNode(moduleData->moduleNode, "serial/"); sshsNodeAddAttributeListener(serialNode, moduleData, &serialConfigListener); sshsNode sysNode = sshsGetRelativeNode(moduleData->moduleNode, "system/"); sshsNodeAddAttributeListener(sysNode, moduleData, &systemConfigListener); sshsNodeAddAttributeListener(moduleData->moduleNode, moduleData, &logLevelListener); return (true); }
static bool caerInputDAVISInit(caerModuleData moduleData) { caerModuleLog(moduleData, CAER_LOG_DEBUG, "Initializing module ..."); // Start data acquisition, and correctly notify mainloop of new data and module of exceptional // shutdown cases (device pulled, ...). char *serialNumber = sshsNodeGetString(moduleData->moduleNode, "serialNumber"); moduleData->moduleState = caerDeviceOpen(U16T(moduleData->moduleID), CAER_DEVICE_DAVIS, U8T(sshsNodeGetInt(moduleData->moduleNode, "busNumber")), U8T(sshsNodeGetInt(moduleData->moduleNode, "devAddress")), serialNumber); free(serialNumber); if (moduleData->moduleState == NULL) { // Failed to open device. return (false); } struct caer_davis_info devInfo = caerDavisInfoGet(moduleData->moduleState); caerInputDAVISCommonInit(moduleData, &devInfo); // Generate sub-system string for module. char *prevAdditionStart = strchr(moduleData->moduleSubSystemString, '['); if (prevAdditionStart != NULL) { *prevAdditionStart = '\0'; } size_t subSystemStringLength = (size_t) snprintf(NULL, 0, "%s[SN %s, %" PRIu8 ":%" PRIu8 "]", moduleData->moduleSubSystemString, devInfo.deviceSerialNumber, devInfo.deviceUSBBusNumber, devInfo.deviceUSBDeviceAddress); char subSystemString[subSystemStringLength + 1]; snprintf(subSystemString, subSystemStringLength + 1, "%s[SN %s, %" PRIu8 ":%" PRIu8 "]", moduleData->moduleSubSystemString, devInfo.deviceSerialNumber, devInfo.deviceUSBBusNumber, devInfo.deviceUSBDeviceAddress); subSystemString[subSystemStringLength] = '\0'; caerModuleSetSubSystemString(moduleData, subSystemString); // Create default settings and send them to the device. createDefaultBiasConfiguration(moduleData, chipIDToName(devInfo.chipID, true), devInfo.chipID); createDefaultLogicConfiguration(moduleData, chipIDToName(devInfo.chipID, true), &devInfo); createDefaultUSBConfiguration(moduleData, chipIDToName(devInfo.chipID, true)); sendDefaultConfiguration(moduleData, &devInfo); // Start data acquisition. bool ret = caerDeviceDataStart(moduleData->moduleState, &caerMainloopDataNotifyIncrease, &caerMainloopDataNotifyDecrease, NULL, &moduleShutdownNotify, moduleData->moduleNode); if (!ret) { // Failed to start data acquisition, close device and exit. caerDeviceClose((caerDeviceHandle *) &moduleData->moduleState); return (false); } // Device related configuration has its own sub-node. sshsNode deviceConfigNode = sshsGetRelativeNode(moduleData->moduleNode, chipIDToName(devInfo.chipID, true)); // Add config listeners last, to avoid having them dangling if Init doesn't succeed. sshsNode chipNode = sshsGetRelativeNode(deviceConfigNode, "chip/"); sshsNodeAddAttributeListener(chipNode, moduleData, &chipConfigListener); sshsNode muxNode = sshsGetRelativeNode(deviceConfigNode, "multiplexer/"); sshsNodeAddAttributeListener(muxNode, moduleData, &muxConfigListener); sshsNode dvsNode = sshsGetRelativeNode(deviceConfigNode, "dvs/"); sshsNodeAddAttributeListener(dvsNode, moduleData, &dvsConfigListener); sshsNode apsNode = sshsGetRelativeNode(deviceConfigNode, "aps/"); sshsNodeAddAttributeListener(apsNode, moduleData, &apsConfigListener); sshsNode imuNode = sshsGetRelativeNode(deviceConfigNode, "imu/"); sshsNodeAddAttributeListener(imuNode, moduleData, &imuConfigListener); sshsNode extNode = sshsGetRelativeNode(deviceConfigNode, "externalInput/"); sshsNodeAddAttributeListener(extNode, moduleData, &extInputConfigListener); sshsNode usbNode = sshsGetRelativeNode(deviceConfigNode, "usb/"); sshsNodeAddAttributeListener(usbNode, moduleData, &usbConfigListener); sshsNode sysNode = sshsGetRelativeNode(moduleData->moduleNode, "system/"); sshsNodeAddAttributeListener(sysNode, moduleData, &systemConfigListener); sshsNode biasNode = sshsGetRelativeNode(deviceConfigNode, "bias/"); size_t biasNodesLength = 0; sshsNode *biasNodes = sshsNodeGetChildren(biasNode, &biasNodesLength); if (biasNodes != NULL) { for (size_t i = 0; i < biasNodesLength; i++) { // Add listener for this particular bias. sshsNodeAddAttributeListener(biasNodes[i], moduleData, &biasConfigListener); } free(biasNodes); } sshsNodeAddAttributeListener(moduleData->moduleNode, moduleData, &logLevelListener); return (true); }