Files
Jungfraujoch/receiver/LinuxSocketDevice.cpp

137 lines
4.4 KiB
C++

// Copyright (2019-2023) Paul Scherrer Institute
#include "LinuxSocketDevice.h"
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <linux/if_packet.h>
#include "../common/NetworkAddressConvert.h"
LinuxSocketDevice::LinuxSocketDevice(uint32_t in_ipv4_addr, uint16_t in_udp_port,
uint16_t data_stream, size_t in_frame_buffer_size_modules,
int32_t in_rcv_buf_size, int16_t in_numa_node) :
AcquisitionDevice(data_stream), udp_port(in_udp_port),
numa_node(in_numa_node), rcv_buf_size(in_rcv_buf_size) {
ipv4_addr = in_ipv4_addr;
max_modules = 16;
MapBuffersStandard(in_frame_buffer_size_modules, 1, numa_node);
mac_addr = 0;
FindMACAddress();
}
void LinuxSocketDevice::MeasureThread(int fd) {
jf_udp_payload jf{};
work_completion_queue.Put(Completion{
.type = Completion::Type::Start
});
try {
ProcessJFPacket process(work_completion_queue, work_request_queue, max_modules);
while (!cancel) {
auto count = recv(fd, &jf, sizeof(jf_udp_payload), 0);
if (count == sizeof(jf_udp_payload)) {
process.ProcessPacket(&jf);
} else if ((count == -1) && (errno != EAGAIN) && (errno != EWOULDBLOCK))
throw JFJochException(JFJochExceptionCategory::UDPError, "Error in UDP receiving");
}
} catch (const JFJochException &e) {
if (logger)
logger->ErrorException(e);
}
// End message should be sent always
work_completion_queue.Put(Completion{
.type = Completion::Type::End
});
close(fd);
}
void LinuxSocketDevice::Start(const DiffractionExperiment& experiment) {
if (experiment.GetConversionOnFPGA())
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Conversion on CPU flag has to be enabled for Raw Ethernet device");
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
throw JFJochException(JFJochExceptionCategory::UDPError, "Cannot create UDP socket");
sockaddr_in server_addr{
.sin_family = AF_INET,
.sin_port = htons(udp_port),
.sin_addr = {.s_addr = ipv4_addr}
};
timeval timeout{
.tv_sec = 0,
.tv_usec = 10000 // 10 ms
};
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0)
throw JFJochException(JFJochExceptionCategory::UDPError, "Cannot set socket timeout");
if (rcv_buf_size > 0) {
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_buf_size, sizeof(rcv_buf_size)) != 0)
throw JFJochException(JFJochExceptionCategory::UDPError, "Cannot set receive buffer size");
}
if (bind(fd, (struct sockaddr *) &server_addr, sizeof(server_addr)) != 0)
throw JFJochException(JFJochExceptionCategory::UDPError, "Cannot bind to UDP port");
cancel = false;
measure = std::async(std::launch::async, &LinuxSocketDevice::MeasureThread, this, fd);
}
void LinuxSocketDevice::Cancel() {
cancel = true;
}
void LinuxSocketDevice::Finalize() {
if (measure.valid())
measure.get();
}
int32_t LinuxSocketDevice::GetNUMANode() const {
return numa_node;
}
uint16_t LinuxSocketDevice::GetUDPPort() const {
return udp_port;
}
void LinuxSocketDevice::FindMACAddress() {
std::string ifname;
bool found = false;
ifaddrs *ifaddr;
if (getifaddrs(&ifaddr) == -1)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "getifaddrs error");
for (ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family == AF_INET) {
if (((sockaddr_in *) ifa->ifa_addr)->sin_addr.s_addr == ipv4_addr) {
ifname = std::string(ifa->ifa_name);
found = true;
}
}
}
if (!found)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "IPv4 address not found");
found = false;
for (ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if ((std::string(ifa->ifa_name) == ifname) && ((ifa->ifa_addr->sa_family == AF_PACKET))) {
memcpy(&mac_addr, ((sockaddr_ll *) ifa->ifa_addr)->sll_addr, sizeof(uint64_t));
found = true;
}
}
if (!found)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "MAC address not found");
freeifaddrs(ifaddr);
}