|
|
@@ -32,7 +32,6 @@
|
|
|
#include "relay_ctrl.h"
|
|
|
#include "web_server.h"
|
|
|
#include "alarm_history.h"
|
|
|
-#include "config_loader.h"
|
|
|
#include "rfid_client.h"
|
|
|
#include "modbus_client.h"
|
|
|
#include "bucket_manager.h"
|
|
|
@@ -44,6 +43,12 @@ using namespace std;
|
|
|
using json = nlohmann::json;
|
|
|
|
|
|
static atomic<bool> g_running{true};
|
|
|
+// 主循环心跳(毫秒): 用于看门狗检测主线程是否卡死(NPU挂起等)
|
|
|
+static atomic<int64_t> g_loop_beat_ms{0};
|
|
|
+static int64_t nowMs() {
|
|
|
+ return chrono::duration_cast<chrono::milliseconds>(
|
|
|
+ chrono::steady_clock::now().time_since_epoch()).count();
|
|
|
+}
|
|
|
static void sigHandler(int sig) {
|
|
|
// 第一次: 优雅退出; 第二次: 强制退出(防止清理流程卡死)
|
|
|
if (!g_running.exchange(false)) {
|
|
|
@@ -90,6 +95,7 @@ struct ChannelState {
|
|
|
PoseDetector pose_model;
|
|
|
bool det_loaded = false;
|
|
|
bool pose_loaded = false;
|
|
|
+ int npu_core = 0; // 分配到的NPU核掩码
|
|
|
// 每路独立推理线程: 主循环提交RGB, worker在独立核上推理
|
|
|
std::thread infer_thread;
|
|
|
std::atomic<bool> infer_running{false};
|
|
|
@@ -684,6 +690,8 @@ int main(int argc, char* argv[]) {
|
|
|
if (ch->infer_thread.joinable()) ch->infer_thread.join();
|
|
|
};
|
|
|
|
|
|
+ // NPU核轮询分配: 按创建顺序轮转, 删加通道后仍保持三核均衡
|
|
|
+ std::atomic<int> core_rr{0};
|
|
|
auto makeChannel = [&](int id, const string& name, const string& url) {
|
|
|
auto ch = make_shared<ChannelState>();
|
|
|
ch->id = id;
|
|
|
@@ -691,9 +699,10 @@ int main(int argc, char* argv[]) {
|
|
|
ch->pose_running = pose_ok; // 姿态识别默认开启(模型加载成功时)
|
|
|
ch->rga.init(input_size, input_size);
|
|
|
ch->capture = make_shared<RtspCapture>(id, url, name);
|
|
|
- // 每路独立加载模型: 按通道id轮转分配NPU核(1=Core0 2=Core1 4=Core2)
|
|
|
+ // 每路独立加载模型: 轮询分配NPU核(1=Core0 2=Core1 4=Core2)
|
|
|
static const int CORE_TABLE[3] = {1, 2, 4};
|
|
|
- int core = CORE_TABLE[id % 3];
|
|
|
+ int core = CORE_TABLE[(core_rr.fetch_add(1) % 3)];
|
|
|
+ ch->npu_core = core;
|
|
|
if (target_enabled) {
|
|
|
ch->det_loaded = ch->det_model.load(model_path, input_size,
|
|
|
conf_thresh, nms_thresh, core);
|
|
|
@@ -1876,6 +1885,31 @@ int main(int argc, char* argv[]) {
|
|
|
hdmi_fullscreen, 100, 2);
|
|
|
cout << "[Main] Ctrl+C 退出" << endl;
|
|
|
|
|
|
+ // ========== 看门狗: 主循环卡死(如rknn_run挂起)时自动退出, 由systemd重启 ==========
|
|
|
+ g_loop_beat_ms.store(nowMs(), std::memory_order_relaxed);
|
|
|
+ int watchdog_sec = cfg.value("watchdog_sec", 120);
|
|
|
+ if (watchdog_sec > 0) {
|
|
|
+ std::thread([watchdog_sec]() {
|
|
|
+ while (g_running.load()) {
|
|
|
+ this_thread::sleep_for(chrono::seconds(10));
|
|
|
+ if (!g_running.load()) break;
|
|
|
+ int64_t beat = g_loop_beat_ms.load(std::memory_order_relaxed);
|
|
|
+ int64_t stall = nowMs() - beat;
|
|
|
+ if (stall > (int64_t)watchdog_sec * 1000) {
|
|
|
+ cerr << "[Watchdog] 主循环卡死 " << (stall / 1000)
|
|
|
+ << "秒(阈值" << watchdog_sec << "秒), 强制退出等待systemd重启"
|
|
|
+ << endl;
|
|
|
+ cerr.flush();
|
|
|
+ // 先尝试优雅退出, 3秒后强制
|
|
|
+ g_running.store(false);
|
|
|
+ std::this_thread::sleep_for(std::chrono::seconds(3));
|
|
|
+ _exit(1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }).detach();
|
|
|
+ cout << "[System] 看门狗: 主循环卡死" << watchdog_sec << "秒自动重启" << endl;
|
|
|
+ }
|
|
|
+
|
|
|
// 自动激活第一个工位(如果有配置)
|
|
|
if (enable_bucket) {
|
|
|
auto bs = bucketManager.getBuckets();
|
|
|
@@ -1910,6 +1944,7 @@ int main(int argc, char* argv[]) {
|
|
|
vector<InferSlot> infer_slots;
|
|
|
|
|
|
while (g_running.load()) {
|
|
|
+ g_loop_beat_ms.store(nowMs(), std::memory_order_relaxed);
|
|
|
this_thread::sleep_for(chrono::milliseconds(1));
|
|
|
|
|
|
auto now_tc = chrono::steady_clock::now();
|