Exemple #1
0
bool initUartOutput(flowOutputState state, char* port, unsigned int baud, size_t bufferSize) {
	// Initialize UART communication
	int uartErrno = uart_open(port, baud);
	if (uartErrno) {
		caerLog(CAER_LOG_ALERT, SUBSYSTEM_UART,
				"Failed to identify serial communication, errno=%i.",uartErrno);
		return (false);
	}
	// Test message
	char* testMessage = "DVS128UART";
	if (uart_tx((int) strlen(testMessage), (unsigned char*) testMessage)) {
		caerLog(CAER_LOG_ALERT, SUBSYSTEM_UART,
				"Test transmission unsuccessful - connection closed.");
		uart_close();
		return(false);
	}

	// Start output handler thread
	state->thread = 0;
	if (thrd_create(&state->thread, &outputHandlerThread, state) != thrd_success) {
		caerLog(CAER_LOG_ALERT, SUBSYSTEM_UART,"Failed to start output handler thread");
		return (false);
	}
	state->buffer = ringBufferInit(bufferSize);
	if (state->buffer == NULL) {
		caerLog(CAER_LOG_ERROR, SUBSYSTEM_UART, "Failed to allocate transfer ring-buffer.");
		return (false);
	}
	atomic_store(&state->running, true);
	caerLog(CAER_LOG_NOTICE, SUBSYSTEM_UART, "Streaming flow events to port %s.",port);
	return (true);
}
Exemple #2
0
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]);
				}
			}
		}
	}
}
Exemple #3
0
void addPacketToTransferBuffer(flowOutputState state, FlowEventPacket packet,
		int32_t flowNumber) {
	if (packet == NULL) {
		// There was nothing in this mainloop run: return
		return;
	}

	caerEventPacketHeader header = &packet->packetHeader;

	// If no valid events are present, there is nothing to add
	if (caerEventPacketHeaderGetEventValid(header) == 0) {
		return;
	}
	if (flowNumber == 0) {
		return;
	}

	// Allocate memory for new flow event packet
	FlowEventPacket copy = malloc(sizeof(struct flow_event_packet));
	if (copy == NULL) {
			caerLog(CAER_LOG_ERROR, SUBSYSTEM_UART,"Failed to copy event packet.");
		return;
	}

	// Copy header information
	copy->packetHeader = packet->packetHeader;
	copy->packetHeader.eventNumber = flowNumber;
	copy->packetHeader.eventValid = flowNumber;
	copy->events = malloc(sizeof(struct flow_event) * (size_t) flowNumber);

	// Copy events
	int j = 0;
	for (int i = 0; i < header->eventNumber; i++) {
		FlowEvent e = &packet->events[i];
		if (e->hasFlow) {
			copy->events[j] = *e;
			j++;
		}
	}

	// Verify number of events copied
	if (j != flowNumber) {
		caerLog(CAER_LOG_ERROR, SUBSYSTEM_FILE,
				"Copied %d events while number of flow events is %d.",
				j, flowNumber);
		flowEventPacketFree(copy);
		return;
	}

	if (!ringBufferPut(state->buffer, copy)) {
		flowEventPacketFree(copy);
		caerLog(CAER_LOG_ALERT, SUBSYSTEM_UART,"Failed to add event packet to ring buffer.");
		return;
	}
}
Exemple #4
0
caerModuleData caerModuleInitialize(uint16_t moduleID, const char *moduleShortName, sshsNode mainloopNode) {
	// Generate short module name with ID, reused in all error messages and later code.
	size_t nameLength = (size_t) snprintf(NULL, 0, "%" PRIu16 "-%s", moduleID, moduleShortName);
	char nameString[nameLength + 1];
	snprintf(nameString, nameLength + 1, "%" PRIu16 "-%s", moduleID, moduleShortName);

	// Allocate memory for the module.
	caerModuleData moduleData = calloc(1, sizeof(struct caer_module_data));
	if (moduleData == NULL) {
		caerLog(CAER_LOG_ALERT, nameString, "Failed to allocate memory for module. Error: %d.", errno);
		thrd_exit(EXIT_FAILURE);
	}

	// Set module ID for later identification (hash-table key).
	moduleData->moduleID = moduleID;

	// Put module into startup state.
	moduleData->moduleStatus = STOPPED;
	atomic_store_explicit(&moduleData->running, true, memory_order_relaxed);

	// Determine SSHS module node. Use short name for better human recognition.
	char sshsString[nameLength + 2];
	strncpy(sshsString, nameString, nameLength);
	sshsString[nameLength] = '/';
	sshsString[nameLength + 1] = '\0';

	// Initialize configuration, shutdown hooks.
	moduleData->moduleNode = sshsGetRelativeNode(mainloopNode, sshsString);
	if (moduleData->moduleNode == NULL) {
		caerLog(CAER_LOG_ALERT, nameString, "Failed to allocate configuration node for module.");
		thrd_exit(EXIT_FAILURE);
	}

	sshsNodePutBool(moduleData->moduleNode, "shutdown", false); // Always reset to false.
	sshsNodeAddAttributeListener(moduleData->moduleNode, moduleData, &caerModuleShutdownListener);

	// Setup default full log string name.
	moduleData->moduleSubSystemString = malloc(nameLength + 1);
	if (moduleData->moduleSubSystemString == NULL) {
		caerLog(CAER_LOG_ALERT, nameString, "Failed to allocate subsystem string for module.");
		thrd_exit(EXIT_FAILURE);
	}

	strncpy(moduleData->moduleSubSystemString, nameString, nameLength);
	moduleData->moduleSubSystemString[nameLength] = '\0';

	atomic_thread_fence(memory_order_release);

	return (moduleData);
}
Exemple #5
0
void caerConfigServerStart(void) {
	// Enable the configuration server thread.
	atomic_store(&configServerThread.running, true);

	// Start the thread.
	if ((errno = thrd_create(&configServerThread.thread, &caerConfigServerRunner, NULL)) == thrd_success) {
		// Successfully started thread.
		caerLog(CAER_LOG_DEBUG, "Config Server", "Thread created successfully.");
	}
	else {
		// Failed to create thread.
		caerLog(CAER_LOG_EMERGENCY, "Config Server", "Failed to create thread. Error: %d.", errno);
		exit(EXIT_FAILURE);
	}
}
void posecalibration_destroy(PoseCalibration *calibClass) {
	try {
		delete calibClass;
	}
	catch (const std::exception& ex) {
		caerLog(CAER_LOG_ERROR, "PoseCalibration_destroy()", "Failed with C++ exception: %s", ex.what());
	}
}
Exemple #7
0
bool initFileOutput(flowOutputState state, char* fileName, size_t bufferSize) {
	// Initialize file communication
	state->file = fopen(fileName,"w+");
	if (state->file == NULL) {
		caerLog(CAER_LOG_ALERT, SUBSYSTEM_FILE,"Failed to open file");
		return (false);
	}
	// Write header as check for file output
	if (fprintf(state->file,"#cAER event data with optic flow values\n") < 0) {
		caerLog(CAER_LOG_ALERT, SUBSYSTEM_FILE,"Failed to write header");
		fclose(state->file);
		return (false);
	}
	// Write creation date
	time_t rawTime;
	struct tm * timeInfo;
	time (&rawTime);
	timeInfo = localtime(&rawTime);
	fprintf(state->file,"#Date created: %s\n",asctime(timeInfo));
	// Write column legend
	fprintf(state->file,"#x,y,t,p,u,v\n");

	state->fileLineNumber = 4;

	// Start output handler thread (ONLY IF NOT YET CALLED BY UART)
	if (state->mode != OF_OUT_BOTH ||
			!atomic_load_explicit(&state->running, memory_order_relaxed)) {
		state->mode = OF_OUT_FILE; // to ensure that only file output is performed in this case
		state->thread = 0;
		if (thrd_create(&state->thread, &outputHandlerThread, state) != thrd_success) {
			caerLog(CAER_LOG_ALERT, SUBSYSTEM_FILE,
					"Failed to start output handler thread");
			return (false);
		}
		state->buffer = ringBufferInit(bufferSize);
		if (state->buffer == NULL) {
			caerLog(CAER_LOG_ERROR, SUBSYSTEM_FILE,
					"Failed to allocate transfer ring-buffer.");
			return (false);
		}
		atomic_store(&state->running, true);
	}
	caerLog(CAER_LOG_NOTICE, SUBSYSTEM_FILE, "Logging events to fileName %s",fileName);

	return (true);
}
bool posecalibration_findMarkers(PoseCalibration *calibClass, caerFrameEvent frame) {
	try {
		return (calibClass->findMarkers(frame));
	}
	catch (const std::exception& ex) {
		caerLog(CAER_LOG_ERROR, "PoseCalibration_findMarkers()", "Failed with C++ exception: %s", ex.what());
		return (false);
	}
}
PoseCalibration *posecalibration_init(PoseCalibrationSettings settings) {
	try {
		return (new PoseCalibration(settings));
	}
	catch (const std::exception& ex) {
		caerLog(CAER_LOG_ERROR, "PoseCalibration()", "Failed with C++ exception: %s", ex.what());
		return (NULL);
	}
}
bool posecalibration_loadCalibrationFile(PoseCalibration *calibClass, PoseCalibrationSettings settings) {
	try {
		return (calibClass->loadCalibrationFile(settings));
	}
	catch (const std::exception& ex) {
		caerLog(CAER_LOG_ERROR, "PoseCalibration_loadCalibrationFile()", "Failed with C++ exception: %s", ex.what());
		return (false);
	}
}
Exemple #11
0
void closeUartOutput(flowOutputState state) {
	state->running = false;
	uart_close();

	if ((errno = thrd_join(state->thread, NULL)) != thrd_success) {
		// This should never happen!
		caerLog(CAER_LOG_CRITICAL, SUBSYSTEM_UART,
				"Failed to join output handling thread. Error: %d.", errno);
	}
	ringBufferFree(state->buffer);
}
Exemple #12
0
void caerConfigServerStop(void) {
	// Only execute if the server thread was actually started.
	if (!atomic_load_explicit(&configServerThread.running, memory_order_relaxed)) {
		return;
	}

	// Disable the configuration server thread first.
	atomic_store(&configServerThread.running, false);

	// Then wait on it to finish.
	if ((errno = thrd_join(configServerThread.thread, NULL)) == thrd_success) {
		// Successfully joined thread.
		caerLog(CAER_LOG_DEBUG, "Config Server", "Thread terminated successfully.");
	}
	else {
		// Failed to join thread.
		caerLog(CAER_LOG_EMERGENCY, "Config Server", "Failed to terminate thread. Error: %d.", errno);
		exit(EXIT_FAILURE);
	}
}
Exemple #13
0
static inline void caerConfigSendResponse(int connectedClientSocket, uint8_t action, uint8_t type, const uint8_t *msg,
	size_t msgLength) {
	size_t responseLength = 4 + msgLength;
	uint8_t response[responseLength];

	response[0] = action;
	response[1] = type;
	setMsgLen(response, (uint16_t) msgLength);
	memcpy(response + 4, msg, msgLength);
	// Msg must already be NUL terminated!

	if (!sendUntilDone(connectedClientSocket, response, responseLength)) {
		// Error on send. Log for now.
		caerLog(CAER_LOG_DEBUG, "Config Server", "Disconnected client on send (fd %d).", connectedClientSocket);
		return;
	}

	caerLog(CAER_LOG_DEBUG, "Config Server",
		"Sent back message to client: action=%" PRIu8 ", type=%" PRIu8 ", msgLength=%zu.", action, type, msgLength);
}
Exemple #14
0
static inline void caerConfigSendError(int connectedClientSocket, const char *errorMsg) {
	size_t errorMsgLength = strlen(errorMsg);

	size_t responseLength = 4 + errorMsgLength + 1; // +1 for terminating NUL byte.
	uint8_t response[responseLength];

	response[0] = ERROR;
	response[1] = STRING;
	setMsgLen(response, (uint16_t) (errorMsgLength + 1));
	memcpy(response + 4, errorMsg, errorMsgLength);
	response[4 + errorMsgLength] = '\0';

	if (!sendUntilDone(connectedClientSocket, response, responseLength)) {
		// Error on send. Log for now.
		caerLog(CAER_LOG_DEBUG, "Config Server", "Disconnected client on send (fd %d).", connectedClientSocket);
		return;
	}

	caerLog(CAER_LOG_DEBUG, "Config Server", "Sent back error message '%s' to client.", errorMsg);
}
Exemple #15
0
static inline bool writeFlowEventPacketFile(flowOutputState state,
		FlowEventPacket flow) {
	caerEventPacketHeader header = (caerEventPacketHeader) flow;

	if (caerEventPacketHeaderGetEventValid(header) == 0) {
		return (false);
	}

	for (int32_t i = 0; i < caerEventPacketHeaderGetEventNumber(header); i++) {
		FlowEvent e = &(flow->events[i]);
		if (e == NULL) {
			caerLog(CAER_LOG_ERROR,SUBSYSTEM_FILE,"Event null pointer found.");
			return (false);
		}
		if (!e->hasFlow) {continue;}

		// Get event data
		uint8_t x = (uint8_t) caerPolarityEventGetX((caerPolarityEvent) e);
		uint8_t y = (uint8_t) caerPolarityEventGetY((caerPolarityEvent) e);
		int32_t t = caerPolarityEventGetTimestamp((caerPolarityEvent) e);
		bool p = caerPolarityEventGetPolarity((caerPolarityEvent) e);
		double u = e->u;
		double v = e->v;

		// Write to file
		if (state->fileLineNumber < FILE_MAX_NUMBER_OF_LINES)  {
			fprintf(state->file,"%3i,%3i,%10i,%d,%4.3f,%4.3f\n",x,y,t,p,u,v);
			state->fileLineNumber++;
		}
		else {
			// If too many lines, stop logging to prevent overrun
			if (state->fileLineNumber == FILE_MAX_NUMBER_OF_LINES) {
				caerLog(CAER_LOG_NOTICE, SUBSYSTEM_FILE,
						"File log reached limit of %d lines - "
						"no more lines will be added.", FILE_MAX_NUMBER_OF_LINES);
				state->fileLineNumber++;
			}
		}
	}
	return (true);
}
Exemple #16
0
static inline bool sendFlowEventPacketUart(FlowEventPacket flow) {
	caerEventPacketHeader header = (caerEventPacketHeader) flow;
	uint32_t packetSize = (uint32_t) caerEventPacketHeaderGetEventNumber(header);
	if (packetSize == 0) {
		// No events to send - return
		return (false);
	}
	// Events are separated by unsigned ints of value 255. This value should
	// never occur as pixel location
	unsigned char eventSeparator = 255;

	// Iterate through packet and send events
	for (uint32_t i = 0; i < packetSize; i++) {
		FlowEvent e = &(flow->events[i]);
		if (e == NULL) {
			caerLog(CAER_LOG_ERROR,SUBSYSTEM_UART,"Event null pointer found.");
			return (false);
		}
		if (!e->hasFlow) {continue;}
		uint8_t x = (uint8_t) caerPolarityEventGetX((caerPolarityEvent) e);
		uint8_t y = (uint8_t) caerPolarityEventGetY((caerPolarityEvent) e);
		int32_t t = caerPolarityEventGetTimestamp((caerPolarityEvent) e);
		int16_t u = (int16_t) (e->u*100);
		int16_t v = (int16_t) (e->v*100);

		// Send data over UART
		if (uart_tx(sizeof(x),(unsigned char*) &x)
				|| uart_tx(sizeof(y),(unsigned char*) &y)
				|| uart_tx(sizeof(t),(unsigned char*) &t)
				|| uart_tx(sizeof(u),(unsigned char*) &u)
				|| uart_tx(sizeof(v),(unsigned char*) &v)
				|| uart_tx(sizeof(eventSeparator), &eventSeparator))  {
			caerLog(CAER_LOG_ERROR,SUBSYSTEM_UART,"Event info not fully sent.");
			return (false);
		}
	}
	return (true);
}
Exemple #17
0
static void orderAndSendEventPackets(outputCommonState state, caerEventPacketContainer currPacketContainer) {
	// Sort container by first timestamp (required) and by type ID (convenience).
	size_t currPacketContainerSize = (size_t) caerEventPacketContainerGetEventPacketsNumber(currPacketContainer);

	qsort(currPacketContainer->eventPackets, currPacketContainerSize, sizeof(caerEventPacketHeader),
		&packetsFirstTimestampThenTypeCmp);

	// Since we just got new data, let's first check that it does conform to our expectations.
	// This means the timestamp didn't slide back! So new smallest TS is >= than last highest TS.
	// These checks are needed to avoid illegal ordering. Normal operation will never trigger
	// these, as stated in the assumptions at the start of file, but erroneous usage or mixing
	// or reordering of packet containers is possible, and has to be caught here.
	int64_t highestTimestamp = 0;

	for (size_t cpIdx = 0; cpIdx < currPacketContainerSize; cpIdx++) {
		caerEventPacketHeader cpPacket = caerEventPacketContainerGetEventPacket(currPacketContainer, (int32_t) cpIdx);

		void *cpFirstEvent = caerGenericEventGetEvent(cpPacket, 0);
		int64_t cpFirstEventTimestamp = caerGenericEventGetTimestamp64(cpFirstEvent, cpPacket);

		if (cpFirstEventTimestamp < state->lastTimestamp) {
			// Smaller TS than already sent, illegal, ignore packet.
			caerLog(CAER_LOG_ERROR, state->parentModule->moduleSubSystemString,
				"Detected timestamp going back, expected at least %" PRIi64 " but got %" PRIi64 "."
				" Ignoring packet of type %" PRIi16 " from source %" PRIi16 ", with %" PRIi32 " events!",
				state->lastTimestamp, cpFirstEventTimestamp, caerEventPacketHeaderGetEventType(cpPacket),
				caerEventPacketHeaderGetEventSource(cpPacket), caerEventPacketHeaderGetEventNumber(cpPacket));
		}
		else {
			// Bigger or equal TS than already sent, this is good. Strict TS ordering ensures
			// that all other packets in this container are the same, so we can start sending
			// the packets from here on out to the file descriptor.
			sendEventPacket(state, cpPacket);

			// Update highest timestamp for this packet container, based upon its valid packets.
			void *cpLastEvent = caerGenericEventGetEvent(cpPacket, caerEventPacketHeaderGetEventNumber(cpPacket) - 1);
			int64_t cpLastEventTimestamp = caerGenericEventGetTimestamp64(cpLastEvent, cpPacket);

			if (cpLastEventTimestamp > highestTimestamp) {
				highestTimestamp = cpLastEventTimestamp;
			}
		}
	}

	// Remember highest timestamp for check in next iteration.
	state->lastTimestamp = highestTimestamp;
}
Exemple #18
0
static inline void writeBufferToAll(outputCommonState state, const uint8_t *buffer, size_t bufferSize) {
	for (size_t i = 0; i < state->fileDescriptors->fdsSize; i++) {
		int fd = state->fileDescriptors->fds[i];

		if (fd >= 0) {
			if (!writeUntilDone(fd, buffer, bufferSize)) {
				// Write failed, most of the reasons for that to happen are
				// not recoverable from, so we just disable this file descriptor.
				// This also detects client-side close() for TCP server connections.
				caerLog(CAER_LOG_INFO, state->parentModule->moduleSubSystemString,
					"Disconnect or error on fd %d, closing and removing. Error: %d.", fd, errno);

				close(fd);
				state->fileDescriptors->fds[i] = -1;
			}
		}
	}
}
Exemple #19
0
static void caerVisualizerEventHandlerNeuronMonitor(caerVisualizerPublicState state, const sf::Event &event) {
	// This only works with actual hardware.
	const std::string moduleLibrary = sshsNodeGetStdString(state->eventSourceConfigNode, "moduleLibrary");
	if (moduleLibrary != "caer_dynapse") {
		return;
	}

	// On release of left click.
	if (event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Button::Left) {
		float positionX = (float) event.mouseButton.x;
		float positionY = (float) event.mouseButton.y;

		// Adjust coordinates according to zoom factor.
		float currentZoomFactor = state->renderZoomFactor.load();
		if (currentZoomFactor > 1.0f) {
			positionX = floorf(positionX / currentZoomFactor);
			positionY = floorf(positionY / currentZoomFactor);
		}
		else if (currentZoomFactor < 1.0f) {
			positionX = floorf(positionX * currentZoomFactor);
			positionY = floorf(positionY * currentZoomFactor);
		}

		// Transform into chip ID, core ID and neuron ID.
		const struct caer_spike_event val = caerDynapseSpikeEventFromXY(U16T(positionX), U16T(positionY));

		uint8_t chipId    = caerSpikeEventGetChipID(&val);
		uint8_t coreId    = caerSpikeEventGetSourceCoreID(&val);
		uint32_t neuronId = caerSpikeEventGetNeuronID(&val);

		// Set value via SSHS.
		sshsNode neuronMonitorNode = sshsGetRelativeNode(state->eventSourceConfigNode, "NeuronMonitor/");

		char monitorKey[] = "Ux_Cy";
		monitorKey[1]     = (char) (48 + chipId);
		monitorKey[4]     = (char) (48 + coreId);

		sshsNodePutInt(neuronMonitorNode, monitorKey, I32T(neuronId));

		caerLog(CAER_LOG_NOTICE, "Visualizer", "Monitoring neuron - chip ID: %d, core ID: %d, neuron ID: %d.", chipId,
			coreId, neuronId);
	}
}
Exemple #20
0
bool caerModuleSetSubSystemString(caerModuleData moduleData, const char *subSystemString) {
	// Allocate new memory for new string.
	size_t subSystemStringLenght = strlen(subSystemString);

	char *newSubSystemString = malloc(subSystemStringLenght + 1);
	if (newSubSystemString == NULL) {
		// Failed to allocate memory. Log this and don't use the new string.
		caerLog(CAER_LOG_ERROR, moduleData->moduleSubSystemString,
			"Failed to allocate new sub-system string for module.");
		return (false);
	}

	// Copy new string into allocated memory.
	strncpy(newSubSystemString, subSystemString, subSystemStringLenght);
	newSubSystemString[subSystemStringLenght] = '\0';

	// Switch new string with old string and free old memory.
	free(moduleData->moduleSubSystemString);
	moduleData->moduleSubSystemString = newSubSystemString;

	return (true);
}
Exemple #21
0
static void caerConfigServerHandleRequest(int connectedClientSocket, uint8_t action, uint8_t type, const uint8_t *extra,
	size_t extraLength, const uint8_t *node, size_t nodeLength, const uint8_t *key, size_t keyLength,
	const uint8_t *value, size_t valueLength) {
	UNUSED_ARGUMENT(extra);

	caerLog(CAER_LOG_DEBUG, "Config Server",
		"Handling request: action=%" PRIu8 ", type=%" PRIu8 ", extraLength=%zu, nodeLength=%zu, keyLength=%zu, valueLength=%zu.",
		action, type, extraLength, nodeLength, keyLength, valueLength);

	// Interpretation of data is up to each action individually.
	sshs configStore = sshsGetGlobal();

	switch (action) {
		case NODE_EXISTS: {
			// We only need the node name here. Type is not used (ignored)!
			bool result = sshsExistsNode(configStore, (const char *) node);

			// Send back result to client. Format is the same as incoming data.
			const uint8_t *sendResult = (const uint8_t *) ((result) ? ("true") : ("false"));
			size_t sendResultLength = (result) ? (5) : (6);
			caerConfigSendResponse(connectedClientSocket, NODE_EXISTS, BOOL, sendResult, sendResultLength);

			break;
		}

		case ATTR_EXISTS: {
			bool nodeExists = sshsExistsNode(configStore, (const char *) node);

			// Only allow operations on existing nodes, this is for remote
			// control, so we only manipulate what's already there!
			if (!nodeExists) {
				// Send back error message to client.
				caerConfigSendError(connectedClientSocket,
					"Node doesn't exist. Operations are only allowed on existing data.");

				break;
			}

			// This cannot fail, since we know the node exists from above.
			sshsNode wantedNode = sshsGetNode(configStore, (const char *) node);

			// Check if attribute exists.
			bool result = sshsNodeAttributeExists(wantedNode, (const char *) key, type);

			// Send back result to client. Format is the same as incoming data.
			const uint8_t *sendResult = (const uint8_t *) ((result) ? ("true") : ("false"));
			size_t sendResultLength = (result) ? (5) : (6);
			caerConfigSendResponse(connectedClientSocket, ATTR_EXISTS, BOOL, sendResult, sendResultLength);

			break;
		}

		case GET: {
			bool nodeExists = sshsExistsNode(configStore, (const char *) node);

			// Only allow operations on existing nodes, this is for remote
			// control, so we only manipulate what's already there!
			if (!nodeExists) {
				// Send back error message to client.
				caerConfigSendError(connectedClientSocket,
					"Node doesn't exist. Operations are only allowed on existing data.");

				break;
			}

			// This cannot fail, since we know the node exists from above.
			sshsNode wantedNode = sshsGetNode(configStore, (const char *) node);

			// Check if attribute exists. Only allow operations on existing attributes!
			bool attrExists = sshsNodeAttributeExists(wantedNode, (const char *) key, type);

			if (!attrExists) {
				// Send back error message to client.
				caerConfigSendError(connectedClientSocket,
					"Attribute of given type doesn't exist. Operations are only allowed on existing data.");

				break;
			}

			union sshs_node_attr_value result = sshsNodeGetAttribute(wantedNode, (const char *) key, type);

			char *resultStr = sshsHelperValueToStringConverter(type, result);

			if (resultStr == NULL) {
				// Send back error message to client.
				caerConfigSendError(connectedClientSocket, "Failed to allocate memory for value string.");
			}
			else {
				caerConfigSendResponse(connectedClientSocket, GET, type, (const uint8_t *) resultStr,
					strlen(resultStr) + 1);

				free(resultStr);
			}

			// If this is a string, we must remember to free the original result.str
			// too, since it will also be a copy of the string coming from SSHS.
			if (type == STRING) {
				free(result.string);
			}

			break;
		}

		case PUT: {
			bool nodeExists = sshsExistsNode(configStore, (const char *) node);

			// Only allow operations on existing nodes, this is for remote
			// control, so we only manipulate what's already there!
			if (!nodeExists) {
				// Send back error message to client.
				caerConfigSendError(connectedClientSocket,
					"Node doesn't exist. Operations are only allowed on existing data.");

				break;
			}

			// This cannot fail, since we know the node exists from above.
			sshsNode wantedNode = sshsGetNode(configStore, (const char *) node);

			// Check if attribute exists. Only allow operations on existing attributes!
			bool attrExists = sshsNodeAttributeExists(wantedNode, (const char *) key, type);

			if (!attrExists) {
				// Send back error message to client.
				caerConfigSendError(connectedClientSocket,
					"Attribute of given type doesn't exist. Operations are only allowed on existing data.");

				break;
			}

			// Put given value into config node. Node, attr and type are already verified.
			const char *typeStr = sshsHelperTypeToStringConverter(type);
			if (!sshsNodeStringToNodeConverter(wantedNode, (const char *) key, typeStr, (const char *) value)) {
				// Send back error message to client.
				caerConfigSendError(connectedClientSocket, "Impossible to convert value according to type.");

				break;
			}

			// Send back confirmation to the client.
			caerConfigSendResponse(connectedClientSocket, PUT, BOOL, (const uint8_t *) "true", 5);

			break;
		}

		case GET_CHILDREN: {
			bool nodeExists = sshsExistsNode(configStore, (const char *) node);

			// Only allow operations on existing nodes, this is for remote
			// control, so we only manipulate what's already there!
			if (!nodeExists) {
				// Send back error message to client.
				caerConfigSendError(connectedClientSocket,
					"Node doesn't exist. Operations are only allowed on existing data.");

				break;
			}

			// This cannot fail, since we know the node exists from above.
			sshsNode wantedNode = sshsGetNode(configStore, (const char *) node);

			// Get the names of all the child nodes and return them.
			size_t numNames;
			const char **childNames = sshsNodeGetChildNames(wantedNode, &numNames);

			// No children at all, return empty.
			if (childNames == NULL) {
				// Send back error message to client.
				caerConfigSendError(connectedClientSocket, "Node has no children.");

				break;
			}

			// We need to return a big string with all of the child names,
			// separated by a NUL character.
			size_t namesLength = 0;

			for (size_t i = 0; i < numNames; i++) {
				namesLength += strlen(childNames[i]) + 1; // +1 for terminating NUL byte.
			}

			// Allocate a buffer for the names and copy them over.
			char namesBuffer[namesLength];

			for (size_t i = 0, acc = 0; i < numNames; i++) {
				size_t len = strlen(childNames[i]) + 1;
				memcpy(namesBuffer + acc, childNames[i], len);
				acc += len;
			}

			caerConfigSendResponse(connectedClientSocket, GET_CHILDREN, STRING, (const uint8_t *) namesBuffer,
				namesLength);

			break;
		}

		case GET_ATTRIBUTES: {
			bool nodeExists = sshsExistsNode(configStore, (const char *) node);

			// Only allow operations on existing nodes, this is for remote
			// control, so we only manipulate what's already there!
			if (!nodeExists) {
				// Send back error message to client.
				caerConfigSendError(connectedClientSocket,
					"Node doesn't exist. Operations are only allowed on existing data.");

				break;
			}

			// This cannot fail, since we know the node exists from above.
			sshsNode wantedNode = sshsGetNode(configStore, (const char *) node);

			// Get the keys of all the attributes and return them.
			size_t numKeys;
			const char **attrKeys = sshsNodeGetAttributeKeys(wantedNode, &numKeys);

			// No attributes at all, return empty.
			if (attrKeys == NULL) {
				// Send back error message to client.
				caerConfigSendError(connectedClientSocket, "Node has no attributes.");

				break;
			}

			// We need to return a big string with all of the attribute keys,
			// separated by a NUL character.
			size_t keysLength = 0;

			for (size_t i = 0; i < numKeys; i++) {
				keysLength += strlen(attrKeys[i]) + 1; // +1 for terminating NUL byte.
			}

			// Allocate a buffer for the keys and copy them over.
			char keysBuffer[keysLength];

			for (size_t i = 0, acc = 0; i < numKeys; i++) {
				size_t len = strlen(attrKeys[i]) + 1;
				memcpy(keysBuffer + acc, attrKeys[i], len);
				acc += len;
			}

			caerConfigSendResponse(connectedClientSocket, GET_ATTRIBUTES, STRING, (const uint8_t *) keysBuffer,
				keysLength);

			break;
		}

		case GET_TYPES: {
			bool nodeExists = sshsExistsNode(configStore, (const char *) node);

			// Only allow operations on existing nodes, this is for remote
			// control, so we only manipulate what's already there!
			if (!nodeExists) {
				// Send back error message to client.
				caerConfigSendError(connectedClientSocket,
					"Node doesn't exist. Operations are only allowed on existing data.");

				break;
			}

			// This cannot fail, since we know the node exists from above.
			sshsNode wantedNode = sshsGetNode(configStore, (const char *) node);

			// Check if any keys match the given one and return its types.
			size_t numTypes;
			enum sshs_node_attr_value_type *attrTypes = sshsNodeGetAttributeTypes(wantedNode, (const char *) key,
				&numTypes);

			// No attributes for specified key, return empty.
			if (attrTypes == NULL) {
				// Send back error message to client.
				caerConfigSendError(connectedClientSocket, "Node has no attributes with specified key.");

				break;
			}

			// We need to return a big string with all of the attribute types,
			// separated by a NUL character.
			size_t typesLength = 0;

			for (size_t i = 0; i < numTypes; i++) {
				const char *typeString = sshsHelperTypeToStringConverter(attrTypes[i]);
				typesLength += strlen(typeString) + 1; // +1 for terminating NUL byte.
			}

			// Allocate a buffer for the types and copy them over.
			char typesBuffer[typesLength];

			for (size_t i = 0, acc = 0; i < numTypes; i++) {
				const char *typeString = sshsHelperTypeToStringConverter(attrTypes[i]);
				size_t len = strlen(typeString) + 1;
				memcpy(typesBuffer + acc, typeString, len);
				acc += len;
			}

			caerConfigSendResponse(connectedClientSocket, GET_TYPES, STRING, (const uint8_t *) typesBuffer,
				typesLength);

			break;
		}

		default:
			// Unknown action, send error back to client.
			caerConfigSendError(connectedClientSocket, "Unknown action.");

			break;
	}
}
Exemple #22
0
static int outputHandlerThread(void *stateArg) {
	if (stateArg == NULL) {
		return (thrd_error);
	}
	flowOutputState state = stateArg;
	switch (state->mode) {
		case OF_OUT_UART:
			thrd_set_name(SUBSYSTEM_UART);
			break;
		case OF_OUT_FILE:
			thrd_set_name(SUBSYSTEM_FILE);
			break;
		case OF_OUT_BOTH:
			thrd_set_name("FLOW_OUTPUT");
			break;
		case OF_OUT_NONE:
			break;
	}

	struct timespec sleepTime = { .tv_sec = 0, .tv_nsec = 500000 };

	// Wait until the buffer is initialized
	while (!atomic_load_explicit(&state->running, memory_order_relaxed)) {
		thrd_sleep(&sleepTime, NULL);
	}

	// Main thread loop
	while (atomic_load_explicit(&state->running, memory_order_relaxed)) {
		FlowEventPacket packet = getPacketFromTransferBuffer(state->buffer);
		if (packet == NULL) { // no data: sleep for a while
			thrd_sleep(&sleepTime, NULL);
		}
		else {
			if (state->mode == OF_OUT_UART || state->mode == OF_OUT_BOTH) {
				if (!sendFlowEventPacketUart(packet)) {
					caerLog(CAER_LOG_ALERT, SUBSYSTEM_UART, "A flow packet was not sent.");
				}
			}
			if (state->mode == OF_OUT_FILE || state->mode == OF_OUT_BOTH) {
				if (!writeFlowEventPacketFile(state,packet)) {
					caerLog(CAER_LOG_ALERT, SUBSYSTEM_FILE, "A flow packet was not written.");
				}
			}
			flowEventPacketFree(packet);
		}
	}

	// If shutdown: empty buffer before closing thread
	FlowEventPacket packet;
	while ((packet = getPacketFromTransferBuffer(state->buffer)) != NULL) {
		if (state->mode == OF_OUT_UART || state->mode == OF_OUT_BOTH) {
			if (!sendFlowEventPacketUart(packet)) {
				caerLog(CAER_LOG_ALERT, SUBSYSTEM_UART, "A flow packet was not sent.");
			}
		}
		if (state->mode == OF_OUT_FILE || state->mode == OF_OUT_BOTH) {
			if (!writeFlowEventPacketFile(state,packet)) {
				caerLog(CAER_LOG_ALERT, SUBSYSTEM_FILE, "A flow packet was not written.");
			}
		}
		flowEventPacketFree(packet);
	}

	return (thrd_success);
}
Exemple #23
0
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);
}
Exemple #24
0
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);
}
Exemple #25
0
int main(void) {
// Install signal handler for global shutdown.
#if defined(_WIN32)
	if (signal(SIGTERM, &globalShutdownSignalHandler) == SIG_ERR) {
		caerLog(CAER_LOG_CRITICAL, "ShutdownAction", "Failed to set signal handler for SIGTERM. Error: %d.", errno);
		return (EXIT_FAILURE);
	}

	if (signal(SIGINT, &globalShutdownSignalHandler) == SIG_ERR) {
		caerLog(CAER_LOG_CRITICAL, "ShutdownAction", "Failed to set signal handler for SIGINT. Error: %d.", errno);
		return (EXIT_FAILURE);
	}
#else
	struct sigaction shutdownAction;

	shutdownAction.sa_handler = &globalShutdownSignalHandler;
	shutdownAction.sa_flags   = 0;
	sigemptyset(&shutdownAction.sa_mask);
	sigaddset(&shutdownAction.sa_mask, SIGTERM);
	sigaddset(&shutdownAction.sa_mask, SIGINT);

	if (sigaction(SIGTERM, &shutdownAction, NULL) == -1) {
		caerLog(CAER_LOG_CRITICAL, "ShutdownAction", "Failed to set signal handler for SIGTERM. Error: %d.", errno);
		return (EXIT_FAILURE);
	}

	if (sigaction(SIGINT, &shutdownAction, NULL) == -1) {
		caerLog(CAER_LOG_CRITICAL, "ShutdownAction", "Failed to set signal handler for SIGINT. Error: %d.", errno);
		return (EXIT_FAILURE);
	}
#endif

	// Open a DVS128, give it a device ID of 1, and don't care about USB bus or SN restrictions.
	caerDeviceHandle dvs128_handle = caerDeviceOpen(1, CAER_DEVICE_DVS128, 0, 0, NULL);
	if (dvs128_handle == NULL) {
		return (EXIT_FAILURE);
	}

	// Let's take a look at the information we have on the device.
	struct caer_dvs128_info dvs128_info = caerDVS128InfoGet(dvs128_handle);

	printf("%s --- ID: %d, Master: %d, DVS X: %d, DVS Y: %d, Logic: %d.\n", dvs128_info.deviceString,
		dvs128_info.deviceID, dvs128_info.deviceIsMaster, dvs128_info.dvsSizeX, dvs128_info.dvsSizeY,
		dvs128_info.logicVersion);

	// Send the default configuration before using the device.
	// No configuration is sent automatically!
	caerDeviceSendDefaultConfig(dvs128_handle);

	// Tweak some biases, to increase bandwidth in this case.
	caerDeviceConfigSet(dvs128_handle, DVS128_CONFIG_BIAS, DVS128_CONFIG_BIAS_PR, 695);
	caerDeviceConfigSet(dvs128_handle, DVS128_CONFIG_BIAS, DVS128_CONFIG_BIAS_FOLL, 867);

	// Let's verify they really changed!
	uint32_t prBias, follBias;
	caerDeviceConfigGet(dvs128_handle, DVS128_CONFIG_BIAS, DVS128_CONFIG_BIAS_PR, &prBias);
	caerDeviceConfigGet(dvs128_handle, DVS128_CONFIG_BIAS, DVS128_CONFIG_BIAS_FOLL, &follBias);

	printf("New bias values --- PR: %d, FOLL: %d.\n", prBias, follBias);

	// Now let's get start getting some data from the device. We just loop in blocking mode,
	// no notification needed regarding new events. The shutdown notification, for example if
	// the device is disconnected, should be listened to.
	caerDeviceDataStart(dvs128_handle, NULL, NULL, NULL, &usbShutdownHandler, NULL);

	// Let's turn on blocking data-get mode to avoid wasting resources.
	caerDeviceConfigSet(dvs128_handle, CAER_HOST_CONFIG_DATAEXCHANGE, CAER_HOST_CONFIG_DATAEXCHANGE_BLOCKING, true);

	while (!atomic_load_explicit(&globalShutdown, memory_order_relaxed)) {
		caerEventPacketContainer packetContainer = caerDeviceDataGet(dvs128_handle);
		if (packetContainer == NULL) {
			continue; // Skip if nothing there.
		}

		int32_t packetNum = caerEventPacketContainerGetEventPacketsNumber(packetContainer);

		printf("\nGot event container with %d packets (allocated).\n", packetNum);

		for (int32_t i = 0; i < packetNum; i++) {
			caerEventPacketHeader packetHeader = caerEventPacketContainerGetEventPacket(packetContainer, i);
			if (packetHeader == NULL) {
				printf("Packet %d is empty (not present).\n", i);
				continue; // Skip if nothing there.
			}

			printf("Packet %d of type %d -> size is %d.\n", i, caerEventPacketHeaderGetEventType(packetHeader),
				caerEventPacketHeaderGetEventNumber(packetHeader));

			// Packet 0 is always the special events packet for DVS128, while packet is the polarity events packet.
			if (i == POLARITY_EVENT) {
				caerPolarityEventPacket polarity = (caerPolarityEventPacket) packetHeader;

				// Get full timestamp and addresses of first event.
				caerPolarityEvent firstEvent = caerPolarityEventPacketGetEvent(polarity, 0);

				int32_t ts = caerPolarityEventGetTimestamp(firstEvent);
				uint16_t x = caerPolarityEventGetX(firstEvent);
				uint16_t y = caerPolarityEventGetY(firstEvent);
				bool pol   = caerPolarityEventGetPolarity(firstEvent);

				printf("First polarity event - ts: %d, x: %d, y: %d, pol: %d.\n", ts, x, y, pol);
			}
		}

		caerEventPacketContainerFree(packetContainer);
	}

	caerDeviceDataStop(dvs128_handle);

	caerDeviceClose(&dvs128_handle);

	printf("Shutdown successful.\n");

	return (EXIT_SUCCESS);
}
Exemple #26
0
/**
 * Copy event packets to the ring buffer for transfer to the output handler thread.
 *
 * @param state output module state.
 * @param packetsListSize the length of the variable-length argument list of event packets.
 * @param packetsList a variable-length argument list of event packets.
 */
static void copyPacketsToTransferRing(outputCommonState state, size_t packetsListSize, va_list packetsList) {
	caerEventPacketHeader packets[packetsListSize];
	size_t packetsSize = 0;

	// Count how many packets are really there, skipping empty event packets.
	for (size_t i = 0; i < packetsListSize; i++) {
		caerEventPacketHeader packetHeader = va_arg(packetsList, caerEventPacketHeader);

		// Found non-empty event packet.
		if (packetHeader != NULL) {
			// Get source information from the event packet.
			int16_t eventSource = caerEventPacketHeaderGetEventSource(packetHeader);

			// Check that source is unique.
			int16_t sourceID = I16T(atomic_load_explicit(&state->sourceID, memory_order_relaxed));

			if (sourceID == -1) {
				state->sourceInfoNode = caerMainloopGetSourceInfo(U16T(eventSource));
				if (state->sourceInfoNode == NULL) {
					// This should never happen, but we handle it gracefully.
					caerLog(CAER_LOG_ERROR, state->parentModule->moduleSubSystemString,
						"Failed to get source info to setup output module.");
					return;
				}

				atomic_store(&state->sourceID, eventSource); // Remember this!
			}
			else if (sourceID != eventSource) {
				caerLog(CAER_LOG_ERROR, state->parentModule->moduleSubSystemString,
					"An output module can only handle packets from the same source! "
						"A packet with source %" PRIi16 " was sent, but this output module expects only packets from source %" PRIi16 ".",
					eventSource, sourceID);
				continue;
			}

			// Source ID is correct, packet is not empty, we got it!
			packets[packetsSize++] = packetHeader;
		}
	}

	// There was nothing in this mainloop run!
	if (packetsSize == 0) {
		return;
	}

	// Allocate memory for event packet array structure that will get passed to output handler thread.
	caerEventPacketContainer eventPackets = caerEventPacketContainerAllocate((int32_t) packetsSize);
	if (eventPackets == NULL) {
		return;
	}

	// Handle the valid only flag here, that way we don't have to do another copy and
	// process it in the output handling thread. We get the value once here, so we do
	// the same for all packets from the same mainloop run, avoiding mid-way changes.
	bool validOnly = atomic_load_explicit(&state->validOnly, memory_order_relaxed);

	// Now copy each event packet and send the array out. Track how many packets there are.
	size_t idx = 0;

	for (size_t i = 0; i < packetsSize; i++) {
		if (validOnly) {
			caerEventPacketContainerSetEventPacket(eventPackets, (int32_t) idx,
				caerCopyEventPacketOnlyValidEvents(packets[i]));
		}
		else {
			caerEventPacketContainerSetEventPacket(eventPackets, (int32_t) idx,
				caerCopyEventPacketOnlyEvents(packets[i]));
		}

		if (caerEventPacketContainerGetEventPacket(eventPackets, (int32_t) idx) == NULL) {
			// Failed to copy packet. Signal but try to continue anyway.
			if ((validOnly && (caerEventPacketHeaderGetEventValid(packets[i]) == 0))
				|| (!validOnly && (caerEventPacketHeaderGetEventNumber(packets[i]) == 0))) {
				caerLog(CAER_LOG_NOTICE, state->parentModule->moduleSubSystemString,
					"Submitted empty event packet to output. Ignoring empty event packet.");
			}
			else {
				caerLog(CAER_LOG_ERROR, state->parentModule->moduleSubSystemString,
					"Failed to copy event packet to output.");
			}
		}
		else {
			idx++;
		}
	}

	// We might have failed to copy all packets (unlikely).
	if (idx == 0) {
		caerEventPacketContainerFree(eventPackets);

		return;
	}

	// Reset packet container size so we only consider the packets we managed
	// to successfully copy.
	caerEventPacketContainerSetEventPacketsNumber(eventPackets, (int32_t) idx);

	retry: if (!ringBufferPut(state->transferRing, eventPackets)) {
		if (atomic_load_explicit(&state->keepPackets, memory_order_relaxed)) {
			// Retry forever if requested.
			goto retry;
		}

		caerEventPacketContainerFree(eventPackets);

		caerLog(CAER_LOG_INFO, state->parentModule->moduleSubSystemString,
			"Failed to put packet's array copy on transfer ring-buffer: full.");
		return;
	}
}
Exemple #27
0
static inline bool caerFrameEventPNGCompress(uint8_t **outBuffer, size_t *outSize, uint16_t *inBuffer, int32_t xSize,
	int32_t ySize, enum caer_frame_event_color_channels channels) {
	png_structp png_ptr = NULL;
	png_infop info_ptr = NULL;
	png_byte **row_pointers = NULL;

	// Initialize the write struct.
	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (png_ptr == NULL) {
		return (false);
	}

	// Initialize the info struct.
	info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL) {
		png_destroy_write_struct(&png_ptr, NULL);
		return (false);
	}

	// Set up error handling.
	if (setjmp(png_jmpbuf(png_ptr))) {
		if (row_pointers != NULL) {
			png_free(png_ptr, row_pointers);
		}
		png_destroy_write_struct(&png_ptr, &info_ptr);
		return (false);
	}

	// Set image attributes.
	png_set_IHDR(png_ptr, info_ptr, (png_uint_32) xSize, (png_uint_32) ySize, 16, caerFrameEventColorToLibPNG(channels),
	PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

	// Handle endianness of 16-bit depth pixels correctly.
	// PNG assumes big-endian, our Frame Event is always little-endian.
	png_set_swap(png_ptr);

	// Initialize rows of PNG.
	row_pointers = png_malloc(png_ptr, (size_t) ySize * sizeof(png_byte *));
	if (row_pointers == NULL) {
		png_destroy_write_struct(&png_ptr, &info_ptr);
		return (false);
	}

	for (size_t y = 0; y < (size_t) ySize; y++) {
		row_pointers[y] = (png_byte *) &inBuffer[y * (size_t) xSize * channels];
	}

	// Set write function to buffer one.
	struct mem_encode state = { .buffer = NULL, .size = 0 };
	png_set_write_fn(png_ptr, &state, &caerLibPNGWriteBuffer, NULL);

	// Actually write the image data.
	png_set_rows(png_ptr, info_ptr, row_pointers);
	png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);

	// Free allocated memory for rows.
	png_free(png_ptr, row_pointers);

	// Destroy main structs.
	png_destroy_write_struct(&png_ptr, &info_ptr);

	// Pass out buffer with resulting PNG image.
	*outBuffer = state.buffer;
	*outSize = state.size;

	return (true);
}
#endif

static size_t compressEventPacket(outputCommonState state, caerEventPacketHeader packet, size_t packetSize) {
	// Data compression technique 1: serialize timestamps for event types that tend to repeat them a lot.
	// Currently, this means polarity events.
	if ((state->format & 0x01) && caerEventPacketHeaderGetEventType(packet) == POLARITY_EVENT) {
		// Search for runs of at least 3 events with the same timestamp, and convert them to a special
		// sequence: leave first event unchanged, but mark its timestamp as special by setting the
		// highest bit (bit 31) to one (it is forbidden for timestamps in memory to have that bit set for
		// signed-integer-only language compatibility). Then, for the second event, change its timestamp
		// to a 4-byte integer saying how many more events will follow afterwards with this same timestamp
		// (this is used for decoding), so only their data portion will be given. Then follow with those
		// event's data, back to back, with their timestamps removed.
		// So let's assume there are 6 events with TS=1234. In memory this looks like this:
		// E1(data,ts), E2(data,ts), E3(data,ts), E4(data,ts), E5(data,ts), E6(data,ts)
		// After timestamp serialization compression step:
		// E1(data,ts|0x80000000), E2(data,4), E3(data), E4(data), E5(data), E5(data)
		// This change is only in the data itself, not in the packet headers, so that we can still use the
		// eventCapacity and eventSize fields to calculate memory allocation when doing decompression.
		// As such, to correctly interpret this data, the Format flags must be correctly set. All current
		// file or network formats do specify those as mandatory in their headers, so we can rely on that.
		// Also all event types where this kind of thing makes any sense do have the timestamp as their last
		// data member in their struct, so we can use that information, stored in tsOffset header field,
		// together with eventSize, to come up with a generic implementation applicable to all other event
		// types that satisfy this condition of TS-as-last-member (so we can use that offset as event size).
		// When this is enabled, it requires full iteration thorough the whole event packet, both at
		// compression and at decompression time.
		size_t currPacketOffset = CAER_EVENT_PACKET_HEADER_SIZE; // Start here, no change to header.
		int32_t eventSize = caerEventPacketHeaderGetEventSize(packet);
		int32_t eventTSOffset = caerEventPacketHeaderGetEventTSOffset(packet);

		int32_t lastTS = -1;
		int32_t currTS = -1;
		size_t tsRun = 0;
		bool doMemMove = false; // Initially don't move memory, until we actually shrink the size.

		for (int32_t caerIteratorCounter = 0; caerIteratorCounter <= caerEventPacketHeaderGetEventNumber(packet);
			caerIteratorCounter++) {
			// Iterate until one element past the end, to flush the last run. In that particular case,
			// we don't get a new element or TS, as we'd be past the end of the array.
			if (caerIteratorCounter < caerEventPacketHeaderGetEventNumber(packet)) {
				void *caerIteratorElement = caerGenericEventGetEvent(packet, caerIteratorCounter);

				currTS = caerGenericEventGetTimestamp(caerIteratorElement, packet);
				if (currTS == lastTS) {
					// Increase size of run of same TS events currently being seen.
					tsRun++;
					continue;
				}
			}

			// TS are different, at this point look if the last run was long enough
			// and if it makes sense to compress. It does starting with 3 events.
			if (tsRun >= 3) {
				// First event to remains there, we set its TS highest bit.
				uint8_t *firstEvent = caerGenericEventGetEvent(packet, caerIteratorCounter - (int32_t) tsRun--);
				caerGenericEventSetTimestamp(firstEvent, packet,
					caerGenericEventGetTimestamp(firstEvent, packet) | I32T(0x80000000));

				// Now use second event's timestamp for storing how many further events.
				uint8_t *secondEvent = caerGenericEventGetEvent(packet, caerIteratorCounter - (int32_t) tsRun--);
				caerGenericEventSetTimestamp(secondEvent, packet, I32T(tsRun)); // Is at least 1.

				// Finally move modified memory where it needs to go.
				if (doMemMove) {
					memmove(((uint8_t *) packet) + currPacketOffset, firstEvent, (size_t) eventSize * 2);
				}
				else {
					doMemMove = true; // After first shrink always move memory.
				}
				currPacketOffset += (size_t) eventSize * 2;

				// Now go through remaining events and move their data close together.
				while (tsRun > 0) {
					uint8_t *thirdEvent = caerGenericEventGetEvent(packet, caerIteratorCounter - (int32_t) tsRun--);
					memmove(((uint8_t *) packet) + currPacketOffset, thirdEvent, (size_t) eventTSOffset);
					currPacketOffset += (size_t) eventTSOffset;
				}
			}
			else {
				// Just copy data unchanged if no compression is possible.
				if (doMemMove) {
					uint8_t *startEvent = caerGenericEventGetEvent(packet, caerIteratorCounter - (int32_t) tsRun);
					memmove(((uint8_t *) packet) + currPacketOffset, startEvent, (size_t) eventSize * tsRun);
				}
				currPacketOffset += (size_t) eventSize * tsRun;
			}

			// Reset values for next iteration.
			lastTS = currTS;
			tsRun = 1;
		}

		return (currPacketOffset);
	}

#ifdef ENABLE_INOUT_PNG_COMPRESSION
	// Data compression technique 2: do PNG compression on frames, Grayscale and RGB(A).
	if ((state->format & 0x02) && caerEventPacketHeaderGetEventType(packet) == FRAME_EVENT) {
		size_t currPacketOffset = CAER_EVENT_PACKET_HEADER_SIZE; // Start here, no change to header.
		size_t frameHeaderSize = sizeof(struct caer_frame_event);

		CAER_FRAME_ITERATOR_ALL_START((caerFrameEventPacket) packet)
			size_t pixelSize = caerFrameEventGetPixelsSize(caerFrameIteratorElement);

			// Keep frame event header intact, compress image data, move memory close together.
			memmove(((uint8_t *) packet) + currPacketOffset, caerFrameIteratorElement, frameHeaderSize);
			currPacketOffset += frameHeaderSize;

			uint8_t *outBuffer;
			size_t outSize;
			if (!caerFrameEventPNGCompress(&outBuffer, &outSize,
				caerFrameEventGetPixelArrayUnsafe(caerFrameIteratorElement),
				caerFrameEventGetLengthX(caerFrameIteratorElement), caerFrameEventGetLengthY(caerFrameIteratorElement),
				caerFrameEventGetChannelNumber(caerFrameIteratorElement))) {
				// Failed to generate PNG.
				// Discard this frame event.
				currPacketOffset -= frameHeaderSize;
				continue;
			}

			// Check that the image didn't actually grow.
			// Add integer needed for storing PNG block length.
			if ((outSize + sizeof(int32_t)) > pixelSize) {
				caerLog(CAER_LOG_ERROR, state->parentModule->moduleSubSystemString, "Failed to compress frame event. "
					"Image actually grew by %zu bytes to a total of %zu bytes.", (outSize - pixelSize), outSize);

				free(outBuffer);
				currPacketOffset -= frameHeaderSize;
				continue;
			}

			// Store size of PNG image block as 4 byte integer.
			int32_t outSizeInt = I32T(outSize);
			memcpy(((uint8_t *) packet) + currPacketOffset, &outSizeInt, sizeof(int32_t));
			currPacketOffset += sizeof(int32_t);

			memcpy(((uint8_t *) packet) + currPacketOffset, outBuffer, outSize);
			currPacketOffset += outSize;

			// Free allocated PNG block memory.
			free(outBuffer);
		}

		return (currPacketOffset);
	}
#endif

	return (packetSize);
}
Exemple #28
0
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;
		}
Exemple #29
0
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);
}
Exemple #30
0
int main(int argc, char *argv[]) {
	// Install signal handler for global shutdown.
	struct sigaction shutdownAction;

	shutdownAction.sa_handler = &globalShutdownSignalHandler;
	shutdownAction.sa_flags = 0;
	sigemptyset(&shutdownAction.sa_mask);
	sigaddset(&shutdownAction.sa_mask, SIGTERM);
	sigaddset(&shutdownAction.sa_mask, SIGINT);

	if (sigaction(SIGTERM, &shutdownAction, NULL) == -1) {
		caerLog(CAER_LOG_CRITICAL, "ShutdownAction", "Failed to set signal handler for SIGTERM. Error: %d.", errno);
		return (EXIT_FAILURE);
	}

	if (sigaction(SIGINT, &shutdownAction, NULL) == -1) {
		caerLog(CAER_LOG_CRITICAL, "ShutdownAction", "Failed to set signal handler for SIGINT. Error: %d.", errno);
		return (EXIT_FAILURE);
	}

	// First of all, parse the IP:Port we need to listen on.
	// Those are for now also the only two parameters permitted.
	// If none passed, attempt to connect to default TCP IP:Port.
	const char *ipAddress = "127.0.0.1";
	uint16_t portNumber = 7777;

	if (argc != 1 && argc != 3) {
		fprintf(stderr, "Incorrect argument number. Either pass none for default IP:Port"
			"combination of 127.0.0.1:7777, or pass the IP followed by the Port.\n");
		return (EXIT_FAILURE);
	}

	// If explicitly passed, parse arguments.
	if (argc == 3) {
		ipAddress = argv[1];
		sscanf(argv[2], "%" SCNu16, &portNumber);
	}

	// Create listening socket for TCP data.
	int listenTCPSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (listenTCPSocket < 0) {
		fprintf(stderr, "Failed to create TCP socket.\n");
		return (EXIT_FAILURE);
	}

	struct sockaddr_in listenTCPAddress;
	memset(&listenTCPAddress, 0, sizeof(struct sockaddr_in));

	listenTCPAddress.sin_family = AF_INET;
	listenTCPAddress.sin_port = htons(portNumber);
	inet_aton(ipAddress, &listenTCPAddress.sin_addr); // htonl() is implicit here.

	if (connect(listenTCPSocket, (struct sockaddr *) &listenTCPAddress, sizeof(struct sockaddr_in)) < 0) {
		fprintf(stderr, "Failed to connect to remote TCP data server.\n");
		return (EXIT_FAILURE);
	}

	// 64K data buffer should be enough for the TCP event packets.
	size_t dataBufferLength = 1024 * 64;
	uint8_t *dataBuffer = malloc(dataBufferLength);

	while (!atomic_load_explicit(&globalShutdown, memory_order_relaxed)) {
		// Get packet header, to calculate packet size.
		if (!recvUntilDone(listenTCPSocket, dataBuffer, sizeof(struct caer_event_packet_header))) {
			fprintf(stderr, "Error in header recv() call: %d\n", errno);
			continue;
		}

		// Decode successfully received data.
		caerEventPacketHeader header = (caerEventPacketHeader) dataBuffer;

		int16_t eventType = caerEventPacketHeaderGetEventType(header);
		int16_t eventSource = caerEventPacketHeaderGetEventSource(header);
		int32_t eventSize = caerEventPacketHeaderGetEventSize(header);
		int32_t eventTSOffset = caerEventPacketHeaderGetEventTSOffset(header);
		int32_t eventCapacity = caerEventPacketHeaderGetEventCapacity(header);
		int32_t eventNumber = caerEventPacketHeaderGetEventNumber(header);
		int32_t eventValid = caerEventPacketHeaderGetEventValid(header);

		printf(
			"type = %" PRIi16 ", source = %" PRIi16 ", size = %" PRIi32 ", tsOffset = %" PRIi32 ", capacity = %" PRIi32 ", number = %" PRIi32 ", valid = %" PRIi32 ".\n",
			eventType, eventSource, eventSize, eventTSOffset, eventCapacity, eventNumber, eventValid);

		// Get rest of event packet, the part with the events themselves.
		if (!recvUntilDone(listenTCPSocket, dataBuffer + sizeof(struct caer_event_packet_header),
			(size_t) (eventCapacity * eventSize))) {
			fprintf(stderr, "Error in data recv() call: %d\n", errno);
			continue;
		}

		if (eventValid > 0) {
			void *firstEvent = caerGenericEventGetEvent(header, 0);
			void *lastEvent = caerGenericEventGetEvent(header, eventValid - 1);

			int32_t firstTS = caerGenericEventGetTimestamp(firstEvent, header);
			int32_t lastTS = caerGenericEventGetTimestamp(lastEvent, header);

			int32_t tsDifference = lastTS - firstTS;

			printf("Time difference in packet: %" PRIi32 " (first = %" PRIi32 ", last = %" PRIi32 ").\n", tsDifference,
				firstTS, lastTS);
		}

		printf("\n\n");
	}

	// Close connection.
	close(listenTCPSocket);

	free(dataBuffer);

	return (EXIT_SUCCESS);
}