174int main(
int argc,
char* argv[]) {
175 std::string config_path =
"config/config.toml";
176 std::optional<std::string> cli_elodin_host, cli_broadcast_ip;
177 std::optional<int> cli_elodin_port, cli_interval_ms, cli_broadcast_port;
179 for (
int i = 1; i < argc; ++i) {
180 std::string arg = argv[i];
181 if (arg ==
"--config" && i + 1 < argc) {
182 config_path = argv[++i];
183 }
else if (arg ==
"--elodin-host" && i + 1 < argc) {
184 cli_elodin_host = argv[++i];
185 }
else if (arg ==
"--elodin-port" && i + 1 < argc) {
186 cli_elodin_port = std::atoi(argv[++i]);
187 }
else if (arg ==
"--interval-ms" && i + 1 < argc) {
188 cli_interval_ms = std::atoi(argv[++i]);
189 }
else if (arg ==
"--broadcast-ip" && i + 1 < argc) {
190 cli_broadcast_ip = argv[++i];
191 }
else if (arg ==
"--broadcast-port" && i + 1 < argc) {
192 cli_broadcast_port = std::atoi(argv[++i]);
193 }
else if (arg ==
"--help" || arg ==
"-h") {
194 std::cout <<
"Usage: " << argv[0]
195 <<
" [--config PATH] [--elodin-host HOST] [--elodin-port PORT]\n"
196 " [--interval-ms MS] [--broadcast-ip IP] [--broadcast-port PORT]\n";
203 std::ifstream f(config_path);
205 for (
const auto& fp : {
"config/config.toml",
"../config/config.toml"}) {
218 std::string elodin_host = cli_elodin_host.value_or(cfg.heartbeat_service.elodin_host);
219 uint16_t elodin_port = cli_elodin_port ?
static_cast<uint16_t
>(*cli_elodin_port)
220 : cfg.heartbeat_service.elodin_port;
222 std::max(100, cli_interval_ms ? *cli_interval_ms
223 :
static_cast<int>(cfg.heartbeat_service.interval_ms));
224 std::string broadcast_ip = cli_broadcast_ip.value_or(cfg.heartbeat_service.broadcast_ip);
225 uint16_t broadcast_port = cli_broadcast_port ?
static_cast<uint16_t
>(*cli_broadcast_port)
226 : cfg.heartbeat_service.broadcast_port;
232 int sock = socket(AF_INET, SOCK_DGRAM, 0);
234 std::cerr <<
"[HeartbeatService] socket() failed" << std::endl;
239 setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &opt,
sizeof(opt));
255 struct sockaddr_in dest;
256 memset(&dest, 0,
sizeof(dest));
257 dest.sin_family = AF_INET;
258 dest.sin_port = htons(broadcast_port);
259 if (inet_pton(AF_INET, broadcast_ip.c_str(), &dest.sin_addr) != 1) {
260 std::cerr <<
"[HeartbeatService] Invalid broadcast IP: " << broadcast_ip << std::endl;
265 std::cout <<
"[HeartbeatService] Started — interval=" << interval_ms
266 <<
"ms broadcast=" << broadcast_ip <<
":" << broadcast_port << std::endl;
267 std::cout <<
"[HeartbeatService] State from Elodin at " << elodin_host <<
":" << elodin_port
268 <<
" [0x5000]" << std::endl;
271 std::thread elodin_thread(
elodinThread, elodin_host, elodin_port);
273 unsigned long count = 0;
274 auto last_log = std::chrono::steady_clock::now();
279 ssize_t sent = sendto(sock, pkt.data(), pkt.size(), 0,
280 reinterpret_cast<struct sockaddr*
>(&dest),
sizeof(dest));
281 if (sent ==
static_cast<ssize_t
>(pkt.size()))
284 auto now = std::chrono::steady_clock::now();
285 if (std::chrono::duration<double>(now - last_log).
count() >= 10.0) {
286 std::cout <<
"[HeartbeatService] Sent " <<
count
287 <<
" heartbeats (engine_state=" << (int)engine_state <<
")" << std::endl;
291 for (
int i = 0;
g_running && i < interval_ms; i += 100)
296 elodin_thread.join();
297 std::cout <<
"[HeartbeatService] Stopped." << std::endl;
static void signalHandler(int)
static std::atomic< bool > g_running
void elodinThread(std::string host, uint16_t port)
static constexpr uint8_t stateToEngine(uint8_t s)
constexpr int kElodinRecvTimeoutMs
std::atomic< uint8_t > g_engine_state
constexpr uint8_t SERVER_HEARTBEAT_TYPE
std::vector< uint8_t > buildHeartbeatPacket(uint8_t engine_state)
std::atomic< bool > g_running
constexpr uint8_t DIABLO_VERSION