obu-firmware builds against the vanetza-idf C-ITS library, which until now came from the colleague's microbu-esp32c5 tree beside the repository and was not tracked here, so a clone of this repository could not build the firmware it ships. The library alone is now part of obu-firmware, as obu-firmware/external/vanetza-idf: their external/vanetza-idf at commit cf4b99f, unchanged (9775 files; see its PROVENANCE.md). CMake takes it from there by default; -DVANETZA_IDF_DIR still points the build elsewhere. The rest of the colleague's tree (their own VAM firmware, PKI tooling, station-link Python tools, the V2X2MAP bridge) stays out of this repository and gitignored; nothing is pushed to their repository. NOTES.md, docs/06, TODO.md and the pcap verifier's usage line point at the new location.
200 lines
7.6 KiB
C++
200 lines
7.6 KiB
C++
#include "ethernet_device.hpp"
|
|
#include "benchmark_application.hpp"
|
|
#include "cam_application.hpp"
|
|
#include "hello_application.hpp"
|
|
#include "link_layer.hpp"
|
|
#include "positioning.hpp"
|
|
#include "router_context.hpp"
|
|
#include "security.hpp"
|
|
#include "time_trigger.hpp"
|
|
#include <boost/asio/io_context.hpp>
|
|
#include <boost/asio/signal_set.hpp>
|
|
#include <boost/program_options.hpp>
|
|
#include <vanetza/common/annotation.hpp>
|
|
#include <iostream>
|
|
|
|
#ifdef SOCKTAP_WITH_CUBE_EVK
|
|
#include "nfiniity_cube_evk.hpp"
|
|
#endif
|
|
|
|
#ifdef SOCKTAP_WITH_RPC
|
|
#include "rpc_link.hpp"
|
|
#endif
|
|
|
|
namespace asio = boost::asio;
|
|
namespace gn = vanetza::geonet;
|
|
namespace po = boost::program_options;
|
|
using namespace vanetza;
|
|
|
|
int main(int argc, const char** argv)
|
|
{
|
|
po::options_description options("Allowed options");
|
|
options.add_options()
|
|
("help", "Print out available options.")
|
|
("link-layer,l", po::value<std::string>()->default_value("ethernet"), "Link layer type")
|
|
("interface,i", po::value<std::string>()->default_value("lo"), "Network interface to use.")
|
|
("mac-address", po::value<std::string>(), "Override the network interface's MAC address.")
|
|
("require-gnss-fix", "Suppress transmissions while GNSS position fix is missing")
|
|
("gn-version", po::value<unsigned>()->default_value(1), "GeoNetworking protocol version to use.")
|
|
("cam-interval", po::value<unsigned>()->default_value(1000), "CAM sending interval in milliseconds.")
|
|
("station-id", po::value<unsigned>()->default_value(1), "CAM station ID used in generated messages.")
|
|
("print-rx-cam", "Print received CAMs")
|
|
("print-tx-cam", "Print generated CAMs")
|
|
("benchmark", "Enable benchmarking")
|
|
("applications,a", po::value<std::vector<std::string>>()->default_value({"ca"}, "ca")->multitoken(), "Run applications [ca,hello,benchmark]")
|
|
("non-strict", "Set MIB parameter ItsGnSnDecapResultHandling to NON_STRICT")
|
|
;
|
|
add_positioning_options(options);
|
|
add_security_options(options);
|
|
add_link_layer_options(options);
|
|
|
|
#ifdef SOCKTAP_WITH_CUBE_EVK
|
|
nfiniity::add_cube_evk_options(options);
|
|
#endif
|
|
|
|
#ifdef SOCKTAP_WITH_RPC
|
|
RpcLinkLayer::add_options(options);
|
|
#endif
|
|
|
|
po::positional_options_description positional_options;
|
|
positional_options.add("interface", 1);
|
|
|
|
po::variables_map vm;
|
|
|
|
try {
|
|
po::store(
|
|
po::command_line_parser(argc, argv)
|
|
.options(options)
|
|
.positional(positional_options)
|
|
.run(),
|
|
vm
|
|
);
|
|
po::notify(vm);
|
|
} catch (po::error& e) {
|
|
std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
|
|
std::cerr << options << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
if (vm.count("help")) {
|
|
std::cout << options << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
try {
|
|
asio::io_context io_context;
|
|
TimeTrigger trigger(io_context);
|
|
|
|
const char* device_name = vm["interface"].as<std::string>().c_str();
|
|
EthernetDevice device(device_name);
|
|
vanetza::MacAddress mac_address = device.address();
|
|
|
|
if (vm.count("mac-address")) {
|
|
std::cout << "Using MAC address: " << vm["mac-address"].as<std::string>() << "." << std::endl;
|
|
|
|
if (!parse_mac_address(vm["mac-address"].as<std::string>().c_str(), mac_address)) {
|
|
std::cerr << "The specified MAC address is invalid." << std::endl;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
const std::string link_layer_name = vm["link-layer"].as<std::string>();
|
|
auto link_layer = create_link_layer(io_context, device, link_layer_name, vm);
|
|
if (!link_layer) {
|
|
std::cerr << "No link layer '" << link_layer_name << "' found." << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
auto signal_handler = [&io_context](const boost::system::error_code& ec, int signal_number) {
|
|
mark_unused(signal_number);
|
|
if (!ec) {
|
|
std::cout << "Termination requested." << std::endl;
|
|
io_context.stop();
|
|
}
|
|
};
|
|
asio::signal_set signals(io_context, SIGINT, SIGTERM);
|
|
signals.async_wait(signal_handler);
|
|
|
|
// configure management information base
|
|
// TODO: make more MIB options configurable by command line flags
|
|
gn::MIB mib;
|
|
mib.itsGnLocalGnAddr.mid(mac_address);
|
|
mib.itsGnLocalGnAddr.is_manually_configured(true);
|
|
mib.itsGnLocalAddrConfMethod = geonet::AddrConfMethod::Managed;
|
|
mib.itsGnSecurity = false;
|
|
if (vm.count("non-strict")) {
|
|
mib.itsGnSnDecapResultHandling = vanetza::geonet::SecurityDecapHandling::Non_Strict;
|
|
}
|
|
mib.itsGnProtocolVersion = vm["gn-version"].as<unsigned>();
|
|
|
|
if (mib.itsGnProtocolVersion != 0 && mib.itsGnProtocolVersion != 1) {
|
|
throw std::runtime_error("Unsupported GeoNetworking version, only version 0 and 1 are supported.");
|
|
}
|
|
|
|
auto positioning = create_position_provider(io_context, vm, trigger.runtime());
|
|
if (!positioning) {
|
|
std::cerr << "Requested positioning method is not available\n";
|
|
return 1;
|
|
}
|
|
|
|
auto security = create_security_entity(vm, trigger.runtime(), *positioning);
|
|
if (security) {
|
|
mib.itsGnSecurity = true;
|
|
}
|
|
|
|
RouterContext context(mib, trigger, *positioning, security.get());
|
|
context.require_position_fix(vm.count("require-gnss-fix") > 0);
|
|
context.set_link_layer(link_layer.get());
|
|
|
|
std::map<std::string, std::unique_ptr<Application>> apps;
|
|
for (const std::string& app_name : vm["applications"].as<std::vector<std::string>>()) {
|
|
if (apps.find(app_name) != apps.end()) {
|
|
std::cerr << "application '" << app_name << "' requested multiple times, skip\n";
|
|
continue;
|
|
}
|
|
|
|
if (app_name == "ca") {
|
|
std::unique_ptr<CamApplication> ca {
|
|
new CamApplication(*positioning, trigger.runtime())
|
|
};
|
|
ca->set_interval(std::chrono::milliseconds(vm["cam-interval"].as<unsigned>()));
|
|
ca->set_station_id(vm["station-id"].as<unsigned>());
|
|
ca->print_received_message(vm.count("print-rx-cam") > 0);
|
|
ca->print_generated_message(vm.count("print-tx-cam") > 0);
|
|
apps.emplace(app_name, std::move(ca));
|
|
} else if (app_name == "hello") {
|
|
std::unique_ptr<HelloApplication> hello {
|
|
new HelloApplication(io_context, std::chrono::milliseconds(800))
|
|
};
|
|
apps.emplace(app_name, std::move(hello));
|
|
} else if (app_name == "benchmark") {
|
|
std::unique_ptr<BenchmarkApplication> benchmark {
|
|
new BenchmarkApplication(io_context)
|
|
};
|
|
apps.emplace(app_name, std::move(benchmark));
|
|
} else {
|
|
std::cerr << "skip unknown application '" << app_name << "'\n";
|
|
}
|
|
}
|
|
|
|
if (apps.empty()) {
|
|
std::cerr << "Warning: No applications are configured, only GN beacons will be exchanged\n";
|
|
}
|
|
|
|
for (const auto& app : apps) {
|
|
std::cout << "Enable application '" << app.first << "'...\n";
|
|
context.enable(app.second.get());
|
|
}
|
|
|
|
io_context.run();
|
|
} catch (PositioningException& e) {
|
|
std::cerr << "Exit because of positioning error: " << e.what() << std::endl;
|
|
return 1;
|
|
} catch (std::exception& e) {
|
|
std::cerr << "Exit: " << e.what() << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|