// WatchDog.h
#pragma once
#include <string>
#include <chrono>
#include <thread>
#include <functional>
#include <atomic>
class Watchdog
{
public:
using WatchDogLogCallback = std::function<void(const std::string&)>;
// 模式
enum Mode
{
Master,
Slave
};
// 配置结构体
struct Config
{
std::chrono::milliseconds timeout; // 超时时间
std::chrono::milliseconds checkInterval; // 检查间隔
std::string heartbeatFile; // 心跳文件路径
std::string mainProgram; // 主程序路径
};
// 构造函数
Watchdog(const Config& config);
// 启动看门狗
void Start(Mode mode = Watchdog::Master);
// 停止看门狗
void Stop();
// 设置超时回调函数(可选)
void SetTimeoutCallback(std::function<void()> callback);
// 设置打印信息回调函数(可选)
void SetLogCallback(WatchDogLogCallback callback);
// 打印信息
void log(const std::string& msg);
// 获取当前毫秒级时间戳
static long long CurrentMillisecondsEpoch();
private:
Config m_config; // 看门狗配置
std::thread m_watchdogThread; // 看门狗线程
std::atomic<bool> m_running; // 看门狗运行状态
std::function<void()> m_callback; // 回调函数
WatchDogLogCallback m_logCallback; // 输出回调
// 获取心跳文件的时间戳
long long GetFileHeartbeatTimestamp();
// 重启主程序
void RestartMainProgram();
// 看门狗任务
void WatchdogMasterTask();
void WatchdogSlaveTask();
};
// WatchDog.cpp
#include "WatchDog.h"
#include <filesystem>
#include <iostream>
#include <fstream>
Watchdog::Watchdog(const Config& config) : m_config(config), m_running(false)
{ }
void Watchdog::Start(Mode mode)
{
if (m_running)
{
log("Watchdog is already running.");
return;
}
else
{
log("Watchdog is running.");
}
m_running = true;
if (mode == Watchdog::Master)
{
m_watchdogThread = std::thread(&Watchdog::WatchdogMasterTask, this);
}
else if (mode == Watchdog::Slave)
{
m_watchdogThread = std::thread(&Watchdog::WatchdogSlaveTask, this);
}
if (m_watchdogThread.joinable())
{
m_watchdogThread.join();
}
}
void Watchdog::Stop()
{
m_running = false;
}
void Watchdog::SetTimeoutCallback(std::function<void()> callback)
{
m_callback = callback;
}
void Watchdog::SetLogCallback(WatchDogLogCallback callback)
{
m_logCallback = callback;
}
void Watchdog::log(const std::string& msg)
{
if (m_logCallback)
{
m_logCallback(msg);
}
else
{
std::cerr << msg;
}
}
long long Watchdog::CurrentMillisecondsEpoch()
{
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
long long Watchdog::GetFileHeartbeatTimestamp()
{
long long timestamp = 0;
if (!std::filesystem::exists(m_config.heartbeatFile))
{
log("Heartbeat file not found.");
return timestamp;
}
std::ifstream heartbeat(m_config.heartbeatFile);
if (!heartbeat.is_open())
{
log("Failed to open heartbeat file.");
return timestamp;
}
heartbeat >> timestamp;
return timestamp;
}
void Watchdog::RestartMainProgram()
{
std::system(m_config.mainProgram.c_str());
#ifdef _WIN32
std::string stopCmd = "taskkill /f /im " + m_config.mainProgram;
std::string startCmd = "start \"\" /d \"\" \"" + m_config.mainProgram + "\"";
#else
std::string stopCmd = "pkill " + m_config.mainProgram;
std::string startCmd = m_config.mainProgram + " &";
#endif
log("stopping " + m_config.mainProgram + " program...");
std::system(stopCmd.c_str());
log("Restarting " + m_config.mainProgram + " program...");
if (std::system(startCmd.c_str()))
{
log(m_config.mainProgram + " failed to run.");
}
else
{
log(m_config.mainProgram + " runs successfully!");
}
}
void Watchdog::WatchdogSlaveTask()
{
while (m_running)
{
std::this_thread::sleep_for(m_config.checkInterval);
std::ofstream heartbeat(m_config.heartbeatFile);
if (heartbeat.is_open())
{
heartbeat << CurrentMillisecondsEpoch();
heartbeat.close();
}
else
{
log("Failed to update heartbeat file.");
}
}
}
void Watchdog::WatchdogMasterTask()
{
while (m_running)
{
std::this_thread::sleep_for(m_config.checkInterval);
try
{
auto lastHeartbeat = GetFileHeartbeatTimestamp();
auto now = CurrentMillisecondsEpoch();
log(std::to_string(now - lastHeartbeat) + " " + std::to_string(m_config.timeout.count()));
if (now - lastHeartbeat > m_config.timeout.count())
{
log("Main program is not responding. Restarting...");
if (m_callback)
{
m_callback();
}
else
{
RestartMainProgram();
}
}
}
catch (const std::exception& e)
{
log("Watchdog error: " + std::string(e.what()));
if (m_callback)
{
m_callback();
}
else
{
RestartMainProgram();
}
}
}
}
以下为Master模式的代码示例:
// example: Master
#include "WatchDog.h"
#include "Logger.hpp"
static std::string logFile = "watchdogmaster.log";
static void OnTimeout()
{
// 引用Logger的日志输出
LogToFile(LogLevel::Info, logFile, "Slave timeout.");
}
static void WatchDogLog(const std::string& msg)
{
LogToFile(LogLevel::Info, logFile, msg.c_str());
}
int main()
{
Watchdog::Config config = {
std::chrono::milliseconds(10 * 1000), // 超时时间为 10 秒
std::chrono::milliseconds(5 * 1000), // 每 5 秒检查一次
"heartbeat.txt", // 心跳文件路径
"WatchDogSlaveTest.exe" // 主程序路径
};
Watchdog watchdog(config);
watchdog.SetLogCallback(WatchDogLog);
watchdog.SetTimeoutCallback(OnTimeout);
watchdog.Start(Watchdog::Master);
return 0;
}
以下为Slave模式的示例:
// example: Slave
#include "WatchDog.h"
#include "Logger.hpp"
static std::string logFile = "watchdogmaster.log";
static void WatchDogLog(const std::string& msg)
{
LogToFile(LogLevel::Info, logFile, msg.c_str());
}
int main()
{
Watchdog::Config config = {
std::chrono::milliseconds(10 * 1000), // 无效
std::chrono::milliseconds(2 * 1000), // 每 2 秒写一次
"heartbeat.txt", // 心跳文件路径
"WatchDogSlaveTest.exe" // 无效
};
Watchdog watchdog(config);
watchdog.SetLogCallback(WatchDogLog);
watchdog.Start(Watchdog::Slave);
return 0;
}
Logger引用的此前的日志记录 Logger