diff options
author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2022-06-08 10:31:05 +0200 |
---|---|---|
committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2022-06-08 10:31:05 +0200 |
commit | 0054b36059daa009d2249285446b466afbe002e2 (patch) | |
tree | cdb276fe1c369307b4f265c25362f975eb436471 | |
parent | 33f07f59eaa7feb49e77044ff10c0400a009b85e (diff) | |
download | ODR-EDI2EDI-next.tar.gz ODR-EDI2EDI-next.tar.bz2 ODR-EDI2EDI-next.zip |
Autoconf: add check to handle -latomicnext
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile.am | 18 | ||||
-rw-r--r-- | configure.ac | 34 | ||||
-rw-r--r-- | src/EDIReceiver.cpp | 98 | ||||
-rw-r--r-- | src/EDIReceiver.hpp | 63 | ||||
-rw-r--r-- | src/edimcast2edi.cpp | 242 |
6 files changed, 446 insertions, 11 deletions
@@ -1,3 +1,5 @@ +libedi2edi.a + *.o *.Po diff --git a/Makefile.am b/Makefile.am index 0440179..4d59bc4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,15 +6,25 @@ else GITVERSION_FLAGS = endif -bin_PROGRAMS=odr-edi2edi +bin_PROGRAMS=odr-edi2edi odr-edimcast2edi +noinst_LIBRARIES = libedi2edi.a INCLUDE=-Ilib odr_edi2edi_CFLAGS =-Wall $(INCLUDE) $(GITVERSION_FLAGS) odr_edi2edi_CXXFLAGS =-Wall -std=c++14 $(INCLUDE) $(GITVERSION_FLAGS) -odr_edi2edi_LDADD = -lpthread -odr_edi2edi_SOURCES = src/main.h src/main.cpp \ - src/receiver.h src/receiver.cpp \ +odr_edi2edi_LDADD = -lpthread libedi2edi.a $(LIBATOMIC_LIBS) +odr_edi2edi_SOURCES = src/main.h src/main.cpp + +odr_edimcast2edi_CFLAGS =-Wall $(INCLUDE) $(GITVERSION_FLAGS) +odr_edimcast2edi_CXXFLAGS =-Wall -std=c++14 $(INCLUDE) $(GITVERSION_FLAGS) +odr_edimcast2edi_LDADD = -lpthread libedi2edi.a $(LIBATOMIC_LIBS) +odr_edimcast2edi_SOURCES = src/edimcast2edi.cpp \ + src/EDIReceiver.hpp src/EDIReceiver.cpp + +libedi2edi_a_CFLAGS =-Wall $(INCLUDE) $(GITVERSION_FLAGS) +libedi2edi_a_CXXFLAGS =-Wall -std=c++14 $(INCLUDE) $(GITVERSION_FLAGS) +libedi2edi_a_SOURCES = src/receiver.h src/receiver.cpp \ src/EDISender.h src/EDISender.cpp \ lib/crc.h lib/crc.c \ lib/Globals.cpp \ diff --git a/configure.ac b/configure.ac index 2ec6d11..8f3723d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,20 +1,21 @@ -# Copyright (C) 2021 Matthias P. Braendli, http://opendigitalradio.org +# Copyright (C) 2022 Matthias P. Braendli, http://opendigitalradio.org - -AC_PREREQ(2.69) -AC_INIT([ODR-EDI2EDI], [0.4.0], [matthias.braendli@mpb.li]) +AC_PREREQ([2.69]) +AC_INIT([ODR-EDI2EDI],[0.4.0],[matthias.braendli@mpb.li]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) -AC_CANONICAL_SYSTEM +AC_CANONICAL_TARGET AM_INIT_AUTOMAKE([-Wall foreign subdir-objects]) AC_CONFIG_SRCDIR([src/main.cpp]) -AC_CONFIG_HEADER([config.h]) +AC_CONFIG_HEADERS([config.h]) AM_SILENT_RULES([yes]) # Checks for programs. +AM_PROG_AR AC_PROG_CXX AC_PROG_CC AM_PROG_CC_C_O +AC_PROG_RANLIB AC_PROG_INSTALL AX_CXX_COMPILE_STDCXX(14,noext,mandatory) @@ -23,7 +24,9 @@ AX_CXX_COMPILE_STDCXX(14,noext,mandatory) AX_PTHREAD([], AC_MSG_ERROR([requires pthread])) # Checks for header files. -AC_HEADER_STDC +AC_CHECK_INCLUDES_DEFAULT +AC_PROG_EGREP + AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h unistd.h]) AC_LANG_PUSH([C++]) @@ -53,8 +56,25 @@ AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ AC_DEFINE(HAVE_SO_NOSIGPIPE, 1, [Define this symbol if you have SO_NOSIGPIPE]) ], [ AC_MSG_RESULT(no) ]) +# On some platforms, std::atomic needs a helper library +AC_MSG_CHECKING(whether -latomic is needed) +AC_LINK_IFELSE([AC_LANG_SOURCE([[ + #include <atomic> + #include <cstdint> + std::atomic<std::int64_t> v; + int main() { + return v; + } +]])], STD_ATOMIC_NEED_LIBATOMIC=no, STD_ATOMIC_NEED_LIBATOMIC=yes) +AC_MSG_RESULT($STD_ATOMIC_NEED_LIBATOMIC) +if test "x$STD_ATOMIC_NEED_LIBATOMIC" = xyes; then + LIBATOMIC_LIBS="-latomic" +fi +AC_SUBST([LIBATOMIC_LIBS]) + AC_LANG_POP([C++]) + AM_CONDITIONAL([IS_GIT_REPO], [test -d '.git']) AC_CONFIG_FILES([Makefile]) diff --git a/src/EDIReceiver.cpp b/src/EDIReceiver.cpp new file mode 100644 index 0000000..99be5d3 --- /dev/null +++ b/src/EDIReceiver.cpp @@ -0,0 +1,98 @@ +/* + Copyright (C) 2022 + Matthias P. Braendli, matthias.braendli@mpb.li + + http://opendigitalradio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "EDIReceiver.hpp" +#include "Log.h" +#include <cstdio> +#include <cassert> +#include <sstream> + +namespace EdiDecoder { + +using namespace std; + +EDIReceiver::EDIReceiver() : + m_dispatcher(std::bind(&EDIReceiver::packet_completed, this)) +{ + using std::placeholders::_1; + using std::placeholders::_2; + m_dispatcher.register_tag("*ptr", + std::bind(&EDIReceiver::decode_starptr, this, _1, _2)); + m_dispatcher.register_tag("*dmy", + std::bind(&EDIReceiver::decode_stardmy, this, _1, _2)); + + m_dispatcher.register_tagpacket_handler(std::bind(&EDIReceiver::tagpacket_handler, this, _1)); +} + +void EDIReceiver::set_verbose(bool verbose) +{ + m_dispatcher.set_verbose(verbose); +} + + +void EDIReceiver::push_packet(Packet &pack) +{ + m_dispatcher.push_packet(pack); +} + +void EDIReceiver::setMaxDelay(int num_af_packets) +{ + m_dispatcher.setMaxDelay(num_af_packets); +} + +bool EDIReceiver::decode_starptr(const std::vector<uint8_t>& value, const tag_name_t&) +{ + if (value.size() != 0x40 / 8) { + etiLog.log(warn, "Incorrect length %02lx for *PTR", value.size()); + return false; + } + + char protocol_sz[5]; + protocol_sz[4] = '\0'; + copy(value.begin(), value.begin() + 4, protocol_sz); + string protocol(protocol_sz); + m_protocol = protocol; + + /* + uint16_t major = read_16b(value.begin() + 4); + uint16_t minor = read_16b(value.begin() + 6); + */ + + return true; +} + +bool EDIReceiver::decode_stardmy(const std::vector<uint8_t>&, const tag_name_t&) +{ + return true; +} + +void EDIReceiver::packet_completed() +{ + if (m_protocol != "DETI") { + etiLog.level(info) << "Received frame with unknown protocol " << m_protocol; + } +} + +void EDIReceiver::tagpacket_handler(const std::vector<uint8_t>& tagpacket) +{ + move(tagpacket.begin(), tagpacket.end(), received_tagpackets.end()); +} + +} diff --git a/src/EDIReceiver.hpp b/src/EDIReceiver.hpp new file mode 100644 index 0000000..150a5c0 --- /dev/null +++ b/src/EDIReceiver.hpp @@ -0,0 +1,63 @@ +/* + Copyright (C) 2022 + Matthias P. Braendli, matthias.braendli@mpb.li + + http://opendigitalradio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#pragma once + +#include "edi/common.hpp" +#include <cstdint> +#include <deque> +#include <string> +#include <vector> + +namespace EdiDecoder { + +class EDIReceiver { + public: + using subchannel_handler = std::function<void(std::vector<uint8_t>&&, frame_timestamp_t, uint16_t /*dlfc*/)>; + + EDIReceiver(); + + void set_verbose(bool verbose); + + /* Push a complete packet into the decoder. Useful for UDP and other + * datagram-oriented protocols. + */ + void push_packet(Packet &pack); + + /* Set the maximum delay in number of AF Packets before we + * abandon decoding a given pseq. + */ + void setMaxDelay(int num_af_packets); + + std::vector<uint8_t> received_tagpackets; + + private: + void packet_completed(); + void tagpacket_handler(const std::vector<uint8_t>& tagpacket); + + std::string m_protocol = ""; + + bool decode_starptr(const std::vector<uint8_t>& value, const tag_name_t& n); + bool decode_stardmy(const std::vector<uint8_t>&, const tag_name_t&); + + TagDispatcher m_dispatcher; +}; + +} diff --git a/src/edimcast2edi.cpp b/src/edimcast2edi.cpp new file mode 100644 index 0000000..8a73d71 --- /dev/null +++ b/src/edimcast2edi.cpp @@ -0,0 +1,242 @@ +/* + Copyright (C) 2022 + Matthias P. Braendli, matthias.braendli@mpb.li + + http://www.opendigitalradio.org + */ +/* + This file is part of the ODR-mmbTools. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <algorithm> +#include <chrono> +#include <iostream> +#include <iomanip> +#include <iterator> +#include <memory> +#include <thread> +#include <vector> +#include <unordered_map> +#include <cmath> +#include <cstring> +#include <fcntl.h> +#include <getopt.h> +#include <poll.h> +#include <signal.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include "Log.h" +#include "main.h" +#include "edioutput/TagItems.h" +#include "edioutput/TagPacket.h" +#include "edioutput/Transport.h" +#include "EDIReceiver.hpp" + +using namespace std; + +volatile sig_atomic_t running = 1; + +void signal_handler(int signum) +{ + if (signum == SIGTERM) { + fprintf(stderr, "Received SIGTERM\n"); + exit(0); + } + //killpg(0, SIGPIPE); + running = 0; +} + +static void usage() +{ + cerr << "\nUsage:\n"; + cerr << "odr-edimcast2edi [options]\n\n"; + cerr << "Receive EDI over multicast, remove PFT layer and make AF layer available as TCP server\n\n"; + + cerr << " -v Increase verbosity (Can be given more than once).\n"; + cerr << " --version the version and quit.\n\n"; + + cerr << "Input settings\n"; + cerr << " -p PORT Receive UDP on PORT\n"; + cerr << " -b BINDTO Bind receive socket to BINDTO address\n"; + cerr << " -m ADDRESS Receive from multicast ADDRESS\n\n"; + + cerr << "Output settings\n"; + cerr << " -l PORT Listen on port PORT\n\n"; + + cerr << "It is best practice to run this tool under a process supervisor that will restart it automatically.\n"; +} + +static const struct option longopts[] = { + {"live-stats-port", required_argument, 0, 2}, + {0, 0, 0, 0} +}; + +int main(int argc, char **argv) +{ + // Version handling is done very early to ensure nothing else but the version gets printed out + if (argc == 2 and strcmp(argv[1], "--version") == 0) { + fprintf(stdout, "%s\n", +#if defined(GITVERSION) + GITVERSION +#else + PACKAGE_VERSION +#endif + ); + return 0; + } + + cerr << "ODR-EDIMCAST2EDI " << +#if defined(GITVERSION) + GITVERSION << +#else + PACKAGE_VERSION << +#endif + " starting up" << endl; + + if (argc == 1) { + usage(); + return 1; + } + + int verbosity = 0; + + edi::configuration_t edi_conf; + edi_conf.enable_pft = false; + + unsigned int rx_port = 0; + string rx_bindto; + string rx_mcastaddr; + + int ch = 0; + int index = 0; + while (ch != -1) { + ch = getopt_long(argc, argv, "b:l:m:p:v", longopts, &index); + switch (ch) { + case 2: // --live-stats-port + //int live_stats_port = stoi(optarg); + break; + case 'b': + rx_bindto = optarg; + break; + case 'l': + { + auto edi_destination = make_shared<edi::tcp_server_t>(); + edi_destination->listen_port = stoi(optarg); + edi_conf.destinations.push_back(move(edi_destination)); + } + break; + case 'm': + rx_mcastaddr = optarg; + break; + case 'p': + rx_port = stoi(optarg); + break; + case 'v': + verbosity++; + break; + case 'h': + default: + usage(); + return 1; + } + } + + edi_conf.verbose = verbosity > 1; + + if (edi_conf.destinations.empty()) { + etiLog.level(error) << "No EDI destinations set"; + return 1; + } + + int ret = 1; + + struct sigaction sa; + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = &signal_handler; + + const int sigs[] = {SIGHUP, SIGQUIT, SIGINT, SIGTERM}; + for (int sig : sigs) { + if (sigaction(sig, &sa, nullptr) == -1) { + perror("sigaction"); + return EXIT_FAILURE; + } + } + + try { + edi::Sender edi_sender(edi_conf); + + Socket::UDPReceiver rx; + + EdiDecoder::EDIReceiver edi_rx; + + rx.add_receive_port(rx_port, rx_bindto, rx_mcastaddr); + + while (running) { + vector<Socket::UDPReceiver::ReceivedPacket> rx_packets; + try { + rx_packets = rx.receive(100); + } + catch (const Socket::UDPReceiver::Interrupted&) { + running = false; + } + catch (const Socket::UDPReceiver::Timeout&) { + } + + for (auto& rp : rx_packets) { + auto received_from = rp.received_from; + EdiDecoder::Packet p; + p.buf = move(rp.packetdata); + p.received_on_port = rp.port_received_on; + edi_rx.push_packet(p); + } + } + + /* + if (edi_conf.enabled()) { + edi::TagPacket edi_tagpacket(0); + + if (tp.seq.seq_valid) { + _edi_sender->override_af_sequence(tp.seq.seq); + } + + if (tp.seq.pseq_valid) { + _edi_sender->override_pft_sequence(tp.seq.pseq); + } + else if (tp.seq.seq_valid) { + // If the source isn't using PFT, set PSEQ = SEQ so that multihoming + // with several EDI2EDI instances could work. + _edi_sender->override_pft_sequence(tp.seq.seq); + } + + edi_tagpacket.raw_tagpacket = move(tp.tagpacket); + _edi_sender->write(edi_tagpacket); + num_frames.fetch_add(1); + } + */ + // To make sure things get printed to stderr + this_thread::sleep_for(chrono::milliseconds(300)); + } + catch (const runtime_error &e) { + etiLog.level(error) << "Runtime error: " << e.what(); + } + catch (const logic_error &e) { + etiLog.level(error) << "Logic error! " << e.what(); + } + + return ret; +} + |