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; }
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); }
/** * 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; } }