Переглянути джерело

继续优化: 看门狗自愈 + NPU核轮询均衡 + 清理死代码

1. 主循环看门狗(watchdog_sec默认120): 主线程卡死(如rknn_run挂起)超过阈值
   自动退出并由systemd重启, 解决偶发NPU挂起导致画面冻结需人工介入的问题
2. NPU核分配改为创建顺序轮询: 删加通道后仍保持三核均衡(原按id%3易不均)
3. 删除死代码config_loader(从未使用)
4. 实测: 3核均衡(57%/81%/53%), 6路在线, 重启3.5秒
dev 16 годин тому
батько
коміт
84dd850d1e
5 змінених файлів з 41 додано та 156 видалено
  1. 0 1
      CMakeLists.txt
  2. 3 2
      config/config.json
  3. 0 106
      src/config_loader.cpp
  4. 0 44
      src/config_loader.h
  5. 38 3
      src/main.cpp

+ 0 - 1
CMakeLists.txt

@@ -29,7 +29,6 @@ add_executable(zone_alarm
     src/relay_ctrl.cpp
     src/web_server.cpp
     src/alarm_history.cpp
-    src/config_loader.cpp
     src/rfid_client.cpp
     src/modbus_client.cpp
     src/bucket_manager.cpp

+ 3 - 2
config/config.json

@@ -201,5 +201,6 @@
         "password": "admin",
         "username": "admin"
     },
-    "web_port": 8080
-}
+    "web_port": 8080,
+    "watchdog_sec": 120
+}

+ 0 - 106
src/config_loader.cpp

@@ -1,106 +0,0 @@
-#include "config_loader.h"
-#include <iostream>
-#include <iomanip>
-
-using namespace std;
-
-json ConfigLoader::loadJson(const string& path) {
-    ifstream ifs(path);
-    if (!ifs.is_open()) return json::object();
-    try {
-        json j;
-        ifs >> j;
-        return j;
-    } catch (...) {
-        return json::object();
-    }
-}
-
-void ConfigLoader::saveJson(const string& path, const json& j) {
-    ofstream ofs(path);
-    ofs << j.dump(4) << endl;
-}
-
-AppConfig ConfigLoader::loadConfig(const string& path) {
-    json j = loadJson(path);
-    AppConfig cfg;
-
-    cfg.app_name = j.value("app_name", "钢包工位视频监测系统");
-    cfg.module_type = j.value("module_type", "RFID");
-    cfg.rtsp_timeout = j.value("rtsp_timeout", 10.0f);
-    cfg.idle_timeout = j.value("idle_timeout", 5);
-
-    // RFID
-    if (j.contains("rfid") && j["rfid"].is_object()) {
-        auto& r = j["rfid"];
-        cfg.rfid.ip = r.value("ip", "");
-        cfg.rfid.port = r.value("port", 60000);
-        cfg.rfid.stability_time = r.value("stability_time", 5);
-        cfg.rfid.stability_count = r.value("stability_count", 3);
-    }
-
-    // Modbus
-    if (j.contains("modbus") && j["modbus"].is_object()) {
-        auto& m = j["modbus"];
-        cfg.modbus.ip = m.value("ip", "");
-        cfg.modbus.port = m.value("port", 502);
-        cfg.modbus.frequency = m.value("frequency", 100);
-        cfg.modbus.stability_count = m.value("stability_count", 3);
-    }
-
-    // 自动重启
-    if (j.contains("auto_reboot") && j["auto_reboot"].is_object()) {
-        auto& ar = j["auto_reboot"];
-        cfg.auto_reboot_enabled = ar.value("enabled", false);
-        cfg.auto_reboot_cycle = ar.value("cycle", 1);
-        cfg.auto_reboot_time = ar.value("time", "03:30");
-    }
-
-    // Web
-    cfg.web_port = j.value("web_port", 8080);
-    if (j.contains("web_auth") && j["web_auth"].is_object()) {
-        cfg.web_username = j["web_auth"].value("username", "admin");
-        cfg.web_password = j["web_auth"].value("password", "admin");
-    }
-
-    return cfg;
-}
-
-void ConfigLoader::saveConfig(const string& path, const AppConfig& cfg, const json& buckets) {
-    json j = loadJson(path);
-
-    j["app_name"] = cfg.app_name;
-    j["module_type"] = cfg.module_type;
-    j["rtsp_timeout"] = cfg.rtsp_timeout;
-    j["idle_timeout"] = cfg.idle_timeout;
-
-    j["rfid"] = {
-        {"ip", cfg.rfid.ip},
-        {"port", cfg.rfid.port},
-        {"stability_time", cfg.rfid.stability_time},
-        {"stability_count", cfg.rfid.stability_count}
-    };
-
-    j["modbus"] = {
-        {"ip", cfg.modbus.ip},
-        {"port", cfg.modbus.port},
-        {"frequency", cfg.modbus.frequency},
-        {"stability_count", cfg.modbus.stability_count}
-    };
-
-    j["auto_reboot"] = {
-        {"enabled", cfg.auto_reboot_enabled},
-        {"cycle", cfg.auto_reboot_cycle},
-        {"time", cfg.auto_reboot_time}
-    };
-
-    j["web_port"] = cfg.web_port;
-    j["web_auth"] = {
-        {"username", cfg.web_username},
-        {"password", cfg.web_password}
-    };
-
-    j["buckets"] = buckets;
-
-    saveJson(path, j);
-}

+ 0 - 44
src/config_loader.h

@@ -1,44 +0,0 @@
-#pragma once
-#include <string>
-#include <fstream>
-#include "json.hpp"
-#include "rfid_client.h"
-#include "modbus_client.h"
-#include "bucket_manager.h"
-
-using json = nlohmann::json;
-
-struct AppConfig {
-    // 应用信息
-    std::string app_name = "钢包工位视频监测系统";
-
-    // 通讯模块
-    std::string module_type = "RFID";  // "RFID" 或 "Modbus"
-    RfidConfig rfid;
-    ModbusConfig modbus;
-
-    // 视频
-    float rtsp_timeout = 10.0f;
-
-    // 空闲超时
-    int idle_timeout = 5;
-
-    // 自动重启
-    bool auto_reboot_enabled = false;
-    int auto_reboot_cycle = 1;
-    std::string auto_reboot_time = "03:30";
-
-    // Web
-    int web_port = 8080;
-    std::string web_username = "admin";
-    std::string web_password = "admin";
-};
-
-class ConfigLoader {
-public:
-    static json loadJson(const std::string& path);
-    static void saveJson(const std::string& path, const json& j);
-
-    static AppConfig loadConfig(const std::string& path);
-    static void saveConfig(const std::string& path, const AppConfig& cfg, const json& buckets);
-};

+ 38 - 3
src/main.cpp

@@ -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();