1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
#include <malloc.h> // for malloc_trim(3)
#include <boost/asio.hpp>
import std;
import routemon;
namespace chrono = std::chrono;
namespace net = boost::asio;
enum class exit_status {
failure,
bad_usage,
};
auto real_main(std::span<char const*> args) -> exit_status {
auto sink = routemon::log::make_sink(routemon::log::level::info);
auto l = routemon::log::logger{sink};
if (args.size() != 2) {
l.error("Fatal: expected exactly one argument (the configuration file location), got {}", args.size() - 1);
return exit_status::bad_usage;
}
auto const* config_filename = args[1];
auto lgen = routemon::locale::make_generator();
auto default_locale = lgen->generate("en_US.UTF-8");
auto locales = {
default_locale,
lgen->generate("nl_NL.UTF-8"),
lgen->generate("de_DE.UTF-8"),
lgen->generate("en_GB.UTF-8"),
};
auto lsel = routemon::locale::selector{locales, default_locale, lgen};
auto ioc = net::io_context{1 /* concurrency hint */};
auto config = routemon::config::app{};
try {
config = routemon::config::load_file(config_filename);
} catch (std::exception const& e) {
l.with("filename", std::string_view{config_filename}).
error("Failed to load configuration: {}", e.what());
return exit_status::failure;
}
sink->set_level(config.logger.level);
// auto rwgps_client = routemon::rwgps::client{ioc, l, config.rwgps.api_key, config.rwgps.auth_token};
// for (auto route : rwgps_client.get_all_routes()) {
// l.info("Route {} (user {}): {} @ {}", route.id, route.user_id, route.name, route.url);
// }
auto dbc = std::shared_ptr<routemon::database::connection>{};
try {
dbc = routemon::database::open(config.database.sqlite3_filename);
} catch (std::exception const& e) {
l.with("filename", config.database.sqlite3_filename).
error("Failed to open database: {}", e.what());
return exit_status::failure;
}
l.with("filename", config.situations.datex2_filename).
info("Loading situations");
auto const before_load = chrono::steady_clock::now();
auto d2loader = routemon::datex2::loader{};
auto pub = routemon::datex2::situation_publication{};
try {
pub = d2loader.load_situation_publication(config.situations.datex2_filename);
} catch (std::exception const& e) {
l.error("Failed to load DATEX II situations publication: {}", e.what());
return exit_status::failure;
}
if (!d2loader.warnings().empty()) {
auto const& warns = d2loader.warnings();
l.warn("Encountered {} unique warnings while loading DATEX II situations publication", warns.size());
auto i = 0uz;
for (auto it = warns.begin(); it != warns.end(); it = warns.upper_bound(*it)) {
l.warn("Warning {} (appeared {}×): {}", ++i, warns.count(*it), *it);
}
}
// Processing the feed is by far the most memory-intensive operation
// during the run time of this application (at the moment), the
// resident set will likely never be this big again. So we ask libc
// to return as much memory as possible to the OS.
malloc_trim(0);
auto const after_load = chrono::steady_clock::now();
auto const dur_load = chrono::duration_cast<chrono::milliseconds>(after_load - before_load);
l.info("Loading situations finished in {}", dur_load);
auto handler = routemon::api::handler{l, std::move(pub)};
auto http_server = routemon::srv::server{l, std::move(lsel), std::move(handler)};
http_server.spawn(ioc);
ioc.run();
l.error("I/O context stopped");
return exit_status::failure;
}
auto main(int argc, char* argv[]) -> int {
if (argc < 0) {
std::cout << "Fatal: argument count below zero" << std::endl;
return EXIT_FAILURE;
}
auto res = real_main(std::span{const_cast<char const**>(argv), static_cast<std::size_t>(argc)});
switch (res) {
case exit_status::failure:
return EXIT_FAILURE;
case exit_status::bad_usage:
return 2;
}
}
|