/** * common.h — 公共类型定义、枚举、结构体、全局变量声明 * v43.2 模块化拆分 (编译修复版) */ #ifndef COMMON_H #define COMMON_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "opencv2/opencv.hpp" #include #include extern "C" { #include #include #include #include #include #include #include } // HyperLPR3 SDK #include "hyper_lpr_sdk.h" // httplib 前向声明(避免在所有模块中包含大头文件) namespace httplib { class Server; } // ==================== 全局常量定义 ==================== // ✅ fix24-v16: Web服务端口从config.ini [server] ip字段解析,不再硬编码 #define WEB_SERVER_PORT_DEFAULT 8080 #define MAX_DB_RECORDS 10000 #define INTERVAL_SECONDS (5 * 60) #define KEYVALLEN 100 extern int g_max_photo_groups; const int MAX_UPLOAD_FAIL = 5; const int BLOCK_TIMEOUT_SEC = 300; const int PLATE_DUPLICATE_INTERVAL_MS = 2000; const int REQUIRED_SUCCESS_GROUPS = 2; const int PHOTO_RETRY_COUNT = 2; const int HTTP_STATUS_OK = 200; const int BUSINESS_ERROR_CODE = 257; const int DB_CLEANUP_THRESHOLD_MB = 500; const long long DB_CLEANUP_THRESHOLD_BYTES = (long long)DB_CLEANUP_THRESHOLD_MB * 1024 * 1024; const int TOKEN_EXPIRE_SECONDS = 7200; const int TOKEN_REFRESH_BUFFER_SECONDS = 300; const int MAX_BILL_COUNT_PER_WINDOW = 1; const int MAX_RETRY_COUNT = 5; const int MAX_UPLOAD_PER_CYCLE = 2; const int PLATE_CLEANUP_TIMEOUT_SEC = 60; const int DAILY_CLEANUP_HOUR = 23; const int DAILY_CLEANUP_MINUTE = 10; const int STATUS_CLEANUP_INTERVAL_SEC = 600; const int MAIN_LOOP_BLOCK_THRESHOLD_MS = 500; const int DB_INSERT_RETRY_COUNT = 1; const int DB_BUSY_TIMEOUT_MS = 100; const int DB_HEALTH_CHECK_INTERVAL_SEC = 60; // ==================== 每日文件清理配置(fix19修改) ==================== const int FILE_CLEANUP_HOUR = 2; // 凌晨2点执行文件清理 const int FILE_CLEANUP_MINUTE = 0; // 照片清理:按容量阈值(MB),默认1GB,可通过config.ini配置 extern long long g_photo_max_capacity_mb; // 日志清理:按天数,默认30天,可通过config.ini配置 extern int g_log_retention_days; // ==================== 错误码定义 ==================== enum class ErrorCode { SUCCESS = 0, NETWORK_ERROR = -1, BUSINESS_ERROR = -2, PARSE_ERROR = -3, INVALID_PARAM = -4, BLOCKED = -5, QUOTA_EXCEEDED = -6, DB_ERROR = -7, PERMANENT_FAILURE = -8 }; // ==================== 照片上传结果结构体 ==================== struct PhotoUploadResult { bool lo_success; bool hi_success; int success_groups; PhotoUploadResult() : lo_success(false), hi_success(false), success_groups(0) {} }; // ==================== 核心:进站/出站完全独立的状态管理 ==================== struct ScenePlateStatus { private: std::unordered_set blocked_plates; std::unordered_map fail_count; std::unordered_map block_time; mutable std::mutex mtx; public: bool is_blocked(const std::string& plate) { std::lock_guard lock(mtx); auto it = blocked_plates.find(plate); if (it == blocked_plates.end()) return false; if (time(nullptr) >= block_time[plate]) { blocked_plates.erase(it); fail_count.erase(plate); block_time.erase(plate); return false; } return true; } void add_fail(const std::string& plate) { std::lock_guard lock(mtx); fail_count[plate]++; if (fail_count[plate] >= MAX_UPLOAD_FAIL) { blocked_plates.insert(plate); block_time[plate] = time(nullptr) + BLOCK_TIMEOUT_SEC; } } void reset(const std::string& plate) { std::lock_guard lock(mtx); blocked_plates.erase(plate); fail_count.erase(plate); block_time.erase(plate); } void cleanup_expired() { std::lock_guard lock(mtx); time_t now = time(nullptr); for (auto it = block_time.begin(); it != block_time.end(); ) { if (now >= it->second) { blocked_plates.erase(it->first); fail_count.erase(it->first); it = block_time.erase(it); } else ++it; } } }; extern ScenePlateStatus g_in_plate_status; extern ScenePlateStatus g_out_plate_status; // ==================== ROI配置结构体(v43新增) ==================== struct ROIConfig { int x = 0; int y = 0; int width = 0; int height = 0; bool enabled = false; bool isFullFrame() const { return x == 0 && y == 0 && width == 0 && height == 0; } cv::Rect getRect(int img_w, int img_h) const { return cv::Rect( std::max(0, std::min(x, img_w - 1)), std::max(0, std::min(y, img_h - 1)), std::min(width > 0 ? width : img_w, img_w - std::max(0, std::min(x, img_w - 1))), std::min(height > 0 ? height : img_h, img_h - std::max(0, std::min(y, img_h - 1))) ); } std::string toString() const { return "[" + std::to_string(x) + "," + std::to_string(y) + "," + std::to_string(width) + "," + std::to_string(height) + "]" + (enabled ? " ON" : " OFF"); } }; // ==================== 解码模式枚举(v43新增) ==================== enum class DecodeMode { AUTO = 0, SOFT = 1, DRM = 2 }; // ==================== 进站/出站交替锁定机制 ==================== enum class StationMode : int { INBOUND_ALLOWED = 0, OUTBOUND_ALLOWED = 1 }; struct PlateStationState { std::string plate; time_t last_in_time; time_t last_out_time; StationMode current_mode; PlateStationState() : last_in_time(0), last_out_time(0), current_mode(StationMode::INBOUND_ALLOWED) {} }; // ==================== 联单缓存结构 ==================== struct InBillCacheEntry { std::string tb_num; time_t create_time; int hit_count; int photo_count; bool bill_created; }; struct OutBillCacheEntry { std::string tb_num; time_t create_time; int hit_count; int photo_count; bool bill_created; }; // ==================== 错误恢复策略 ==================== enum class UploadError { SUCCESS = 0, NETWORK_TIMEOUT, NETWORK_CONNECTION_FAILED, SERVER_ERROR_4XX, SERVER_ERROR_5XX, AUTH_FAILED, INVALID_RESPONSE, IMAGE_TOO_LARGE, UNKNOWN_ERROR }; struct RecoveryStrategy { int max_retries; int retry_delay_ms; bool should_retry; }; // ==================== 场景上传记录(TIME_WINDOW持久化) ==================== struct SceneUploadRecord { int bill_count; time_t last_time; time_t trigger_time; bool bill_created; int photo_success_count; bool feishu_sent; int generation; time_t create_time; std::string tb_num; SceneUploadRecord() : bill_count(0), last_time(0), trigger_time(0), bill_created(false), photo_success_count(0), feishu_sent(false), generation(0), create_time(time(NULL)) {} }; // ==================== 监控指标 ==================== struct MonitorMetrics { std::atomic createbill_total{ 0 }; std::atomic createbill_success{ 0 }; std::atomic createbill_business_error{ 0 }; std::atomic createbill_quota_exceeded{ 0 }; std::deque> upload_time_records; std::mutex upload_time_mtx; std::atomic time_window_hits{ 0 }; std::atomic in_cache_hits{ 0 }; std::atomic in_cache_misses{ 0 }; std::atomic out_cache_hits{ 0 }; std::atomic out_cache_misses{ 0 }; std::atomic upload_errors{ 0 }; std::atomic network_timeouts{ 0 }; std::atomic server_errors{ 0 }; std::atomic in_station_count{ 0 }; std::atomic out_station_count{ 0 }; std::atomic feishu_success_count{ 0 }; std::deque five_min_upload_times; std::mutex five_min_mtx; time_t start_time{ 0 }; std::atomic main_loop_block_count{ 0 }; std::atomic main_loop_max_block_ms{ 0 }; std::atomic photo_lo_success{ 0 }; std::atomic photo_hi_success{ 0 }; std::atomic photo_group_success{ 0 }; std::atomic db_duplicate_skip_count{ 0 }; std::atomic dir_create_error_count{ 0 }; std::atomic db_insert_error_count{ 0 }; std::atomic db_insert_retry_success{ 0 }; std::atomic permanent_failure_count{ 0 }; std::atomic alert_sent_count{ 0 }; std::atomic db_health_check_count{ 0 }; // v42新增:交替锁定监控指标 std::atomic station_lock_in_count{ 0 }; std::atomic station_lock_out_count{ 0 }; std::atomic station_in_block_count{ 0 }; std::atomic station_out_block_count{ 0 }; std::atomic station_timeout_cleanup_count{ 0 }; // v43新增:ROI监控指标 std::atomic roi_in_crop_count{ 0 }; std::atomic roi_out_crop_count{ 0 }; std::atomic roi_in_fullframe_count{ 0 }; std::atomic roi_out_fullframe_count{ 0 }; // v43新增:飞书/解码/Web监控指标 std::atomic feishu_skip_count{ 0 }; std::atomic drm_decode_count{ 0 }; std::atomic soft_decode_count{ 0 }; std::atomic web_retry_count{ 0 }; std::atomic web_retry_success_count{ 0 }; // fix20新增:推理耗时追踪 std::deque> infer_time_records; std::mutex infer_time_mtx; void record_roi_crop(bool is_in) { is_in ? roi_in_crop_count++ : roi_out_crop_count++; } void record_roi_fullframe(bool is_in) { is_in ? roi_in_fullframe_count++ : roi_out_fullframe_count++; } void record_feishu_skip() { feishu_skip_count++; } void record_drm_decode() { drm_decode_count++; } void record_soft_decode() { soft_decode_count++; } void record_web_retry(bool success) { web_retry_count++; if (success) web_retry_success_count++; } void record_station_lock(bool is_in) { is_in ? station_lock_in_count++ : station_lock_out_count++; } void record_station_block(bool is_in) { is_in ? station_in_block_count++ : station_out_block_count++; } void record_station_timeout_cleanup() { station_timeout_cleanup_count++; } void record_upload_time(int duration_ms) { std::lock_guard lock(upload_time_mtx); upload_time_records.push_back({ time(NULL), duration_ms }); if (upload_time_records.size() > 10) { upload_time_records.pop_front(); } } void record_createbill(bool success, bool is_business_error = false, bool is_quota_exceeded = false) { createbill_total++; if (success) createbill_success++; if (is_business_error) createbill_business_error++; if (is_quota_exceeded) createbill_quota_exceeded++; } void record_cache_hit(bool is_in, bool hit) { if (is_in) hit ? in_cache_hits++ : in_cache_misses++; else hit ? out_cache_hits++ : out_cache_misses++; } void record_error(UploadError error) { upload_errors++; switch (error) { case UploadError::NETWORK_TIMEOUT: network_timeouts++; break; case UploadError::SERVER_ERROR_5XX: server_errors++; break; default: break; } } void record_station(bool is_in) { is_in ? in_station_count++ : out_station_count++; } void record_feishu_success() { feishu_success_count++; } void record_photo_upload(bool lo_success, bool hi_success, bool group_success) { if (lo_success) photo_lo_success++; if (hi_success) photo_hi_success++; if (group_success) photo_group_success++; } void record_main_loop_block(long long duration_ms) { main_loop_block_count++; if (duration_ms > main_loop_max_block_ms.load()) { main_loop_max_block_ms.store(duration_ms); } if (duration_ms > MAIN_LOOP_BLOCK_THRESHOLD_MS) { std::cerr << "[告警] 主循环阻塞 " << duration_ms << "ms,超过阈值 " << MAIN_LOOP_BLOCK_THRESHOLD_MS << "ms" << std::endl; } } void record_db_duplicate_skip() { db_duplicate_skip_count++; } void record_dir_create_error() { dir_create_error_count++; } void record_db_insert_error() { db_insert_error_count++; } void record_db_insert_retry_success() { db_insert_retry_success++; } void record_permanent_failure() { permanent_failure_count++; } void record_alert_sent() { alert_sent_count++; } void record_db_health_check() { db_health_check_count++; } void record_inference_time(int duration_ms) { std::lock_guard lock(infer_time_mtx); infer_time_records.push_back({ time(NULL), duration_ms }); if (infer_time_records.size() > 20) { infer_time_records.pop_front(); } } int get_avg_inference_time() { std::lock_guard lock(infer_time_mtx); if (infer_time_records.empty()) return 0; int total = 0; for (auto& p : infer_time_records) total += p.second; return total / (int)infer_time_records.size(); } int get_last_inference_time() { std::lock_guard lock(infer_time_mtx); if (infer_time_records.empty()) return 0; return infer_time_records.back().second; } int get_avg_upload_time() { std::lock_guard lock(upload_time_mtx); if (upload_time_records.empty()) return 0; int total = 0; for (auto& p : upload_time_records) total += p.second; return total / upload_time_records.size(); } float get_createbill_success_rate() { int total = createbill_total.load(); return total > 0 ? (float)createbill_success.load() / total : 0.0f; } float get_cache_hit_rate(bool is_in) { if (is_in) { int total = in_cache_hits.load() + in_cache_misses.load(); return total > 0 ? (float)in_cache_hits.load() / total : 0.0f; } else { int total = out_cache_hits.load() + out_cache_misses.load(); return total > 0 ? (float)out_cache_hits.load() / total : 0.0f; } } std::string get_uptime_string() { if (start_time == 0) return "0h00m00s"; auto secs = time(NULL) - start_time; int h = secs / 3600; int m = (secs % 3600) / 60; int s = secs % 60; std::ostringstream oss; oss << h << "h" << std::setfill('0') << std::setw(2) << m << "m" << std::setw(2) << s << "s"; return oss.str(); } void print_report(); }; extern MonitorMetrics g_metrics; // ==================== C风格枚举和结构体 ==================== typedef enum { BUILDING = 1, DOCK = 2, BACKFILL = 3 } WTYPE; typedef enum { STATION_IN = 1, STATION_OUT = 2 } DTYPE; typedef enum { POSITION_LO = 1, POSITION_HI = 2 } GTYPE; typedef enum { CAPTURE_NORMAL = 1, CAPTURE_CAMERA_OFFLINE, CAPTURE_STORAGE_ERROR, CAPTURE_LENS_OBSTRUCTED, CAPTURE_ANGLE_ABNORMAL } CaptureStatus; typedef struct { WTYPE wtype; DTYPE dtype; GTYPE gtype; CaptureStatus status; time_t capture_time = 0; // ✅ fix23: 抓拍时间戳,保存帧被捕获时的时间 } CaptureInfo; typedef struct { std::string tb_num; float confidence = 0.0f; std::string type; std::string code; cv::Mat image_front; cv::Mat image_side; int count = 0; bool is_report = false; std::string pic_path_front; std::string pic_path_side; int retry_count = 0; std::unique_ptr cap_info_copy; time_t first_seen_time = 0; int db_id = 0; bool is_inbound = false; bool is_outbound = false; bool photo_saved = false; time_t last_station_blocked_time = 0; // ✅ fix11: 交替锁定拦截时间戳,防止被拦截后空转 bool blocked_by_station_lock = false; // ✅ fix13: 区分BLOCKED来源(true=交替锁定, false=is_blocked) std::atomic upload_in_progress{false}; // ✅ fix18-P0: 上传进行中标志,防止peek与try_lock竞态 time_t upload_complete_time = 0; // ✅ fix24-v6: 上传完成时间戳,防止刚上传完就被peek重置导致重复保存 time_t db_capture_time = 0; // ✅ fix24-v18: 照片保存时固定的capture_time,防止识别线程竞态覆盖导致DB时间不一致 } CarPlateInfo; // ==================== RTSPCapture 前向声明 ==================== class RTSPCapture; // ==================== PlateRecApp 结构体 ==================== typedef struct { P_HLPR_Context hlpr_ctx; RTSPCapture* capture_front_in = nullptr; RTSPCapture* capture_front_out = nullptr; RTSPCapture* capture_side_in = nullptr; RTSPCapture* capture_side_out = nullptr; CaptureInfo front_info_in; CaptureInfo front_info_out; CaptureInfo side_info_in; CaptureInfo side_info_out; time_t timer_front_in; time_t timer_front_out; std::mutex map_mtx; std::unordered_map> map; } PlateRecApp; // ==================== 全局变量 extern 声明 ==================== // 路径 extern std::string pathComm; extern std::string model_path; // 配置变量 extern std::string project_name; extern std::string point_number; extern std::string throughway; extern std::string rtsp_url_front_in; extern std::string rtsp_url_front_out; extern std::string rtsp_url_side_in; extern std::string rtsp_url_side_out; extern std::string app_api; extern std::string app_key; extern std::string app_secret; extern int DEBUG_LOG; extern std::atomic TestFlag; extern int g_wType; extern int g_time_window_min; extern double g_PLATE_CONFIDENCE_THRESHOLD; extern double g_PLATE_LOG_THRESHOLD; // v43新增全局变量 extern bool g_alternating_merge_enabled; extern ROIConfig g_roi_in; extern ROIConfig g_roi_out; extern bool g_roi_debug_enabled; extern DecodeMode g_hw_decode_mode; extern std::string g_hw_decode_device; extern std::string g_rtsp_transport; extern bool g_suppress_emergency_alert; extern int g_in_out_interval_sec; // fix24-v8: 共享摄像头模式(进站和出站共用同一个前向摄像头) // 启用后,根据交替锁定状态自动判断检测到的车牌是进站还是出站 extern bool g_shared_capture_mode; // fix24-v14: 本地时区偏移(秒),用于SQLite中create_time字符串转Unix时间戳的校正 extern int g_tz_offset_seconds; // fix18: config.ini路径(用于ROI写回) extern std::string g_config_ini_path; // fix18: 称重系统全局变量(config.ini key映射已在config.cpp中匹配) extern bool g_weight_enabled; extern bool g_flag_weight; // fix27: 飞书消息是否包含重量信息(config.ini: flagWeight=1) extern double g_weight_threshold_in; extern double g_weight_threshold_out; extern double g_weight_default_entry; extern double g_weight_default_exit; extern int g_weight_detection_time; extern int g_weight_tcp_connect_timeout; extern std::string g_weight_server_ip; extern int g_weight_server_port; extern double g_weight_max_kg; extern double g_weight_min_kg; extern int g_weight_stable_samples; extern double g_weight_stable_threshold; extern int g_weight_candidate_count; extern int g_weight_max_retries; extern int g_weight_retry_delay_ms; // fix18: MQTT全局变量 extern bool g_mqtt_enabled; extern std::string g_mqtt_host; extern int g_mqtt_port; extern std::string g_mqtt_username; extern std::string g_mqtt_password; extern std::string g_mqtt_topic; extern std::string g_mqtt_client_id; // MQTT消息附加字段(从[MQTT]节点读取) extern std::string g_plate_color; extern std::string g_vehicle_type; // ==================== fix24 v43: [sync] 并行同步配置 ==================== extern bool g_sync_parallel_enabled; // [sync] enabled (0=退回v41串行, 1=启用v43并行) extern int g_sync_reporter_timeout_sec; // Reporter 等待两路结果超时(秒) extern int g_sync_weight_retry_max; // 重量独立重试最大次数 extern int g_sync_weight_retry_interval_sec; // 重量重试间隔(秒) extern int g_sync_tb_num_dedup_sec; // tb_num 去重窗口(秒) extern int g_sync_weight_snapshot_max_age_sec; // fix24 v43 Bug#18: 重量快照最大年龄(秒) // ==================== fix24 v44 P0: 预检测锁定配置 ==================== // 地磅场景:车辆整体开上秤台,重量一次性加载,不存在半上秤误判 // TCP线程每200ms采样,连续 prelock_stable_samples 个样本稳定且 > prelock_threshold_kg 即锁定 // 车牌检测后优先消费预锁定值(0等待),未命中再走快照→阻塞等待 extern double g_weight_prelock_threshold_kg; // 预检测触发阈值(kg),0=禁用 extern int g_weight_prelock_stable_samples; // 预检测锁定时需要的连续稳定样本数 // 交替锁定 extern std::map g_plate_station_cache; extern std::mutex g_station_cache_mtx; extern const int STATION_CLEANUP_INTERVAL_SEC; const int STATION_LOCK_EXPIRE_SEC = 7200; // ✅ fix18: 交替锁定记录2小时过期,超时清零 // ✅ fix16: 移除OUT_NO_RECORD_TIMEOUT_SEC和g_out_no_record_first_seen // 出站无进站记录永远拦截,不再需要超时放行机制 // 帧级别去重 extern std::unordered_map g_in_plate_last_processed; extern std::unordered_map g_out_plate_last_processed; extern std::mutex g_in_plate_last_processed_mtx; extern std::mutex g_out_plate_last_processed_mtx; // 飞书 extern bool g_feishu_enabled; // fix27: 飞书消息独立开关(config.ini: [feishu] ENABLED=1) extern std::string g_feishu_app_id; extern std::string g_feishu_app_secret; extern std::string g_feishu_chat_id; extern long g_feishu_timeout_second; // 联单缓存 extern std::unordered_map g_in_bill_cache; extern std::mutex g_in_bill_cache_mtx; extern std::unordered_map g_out_bill_cache; extern std::mutex g_out_bill_cache_mtx; extern std::unordered_map g_in_record; extern std::mutex g_in_record_mtx; extern std::unordered_map g_out_record; extern std::mutex g_out_record_mtx; // 场景上传记录 extern std::unordered_map g_in_upload_records; extern std::unordered_map g_out_upload_records; // 特殊车牌 extern std::vector g_special_plates; extern std::mutex g_special_plates_mtx; // 重试队列 extern std::deque> g_retry_queue; extern std::mutex g_retry_queue_mtx; // 数据库 extern sqlite3* g_db; extern std::mutex g_db_mtx; extern std::string g_db_path; // Token extern std::string g_tenant_token; extern std::atomic g_token_expire_time; extern std::mutex g_token_mtx; // 全局状态 extern volatile std::sig_atomic_t g_running; extern std::atomic g_last_data_activity_time; // PlateRecApp实例 extern PlateRecApp plate_rec_app; extern uint32_t count; extern std::atomic g_active_detach_threads; // Web服务器 extern std::unique_ptr g_web_server_thread; extern std::unique_ptr g_web_server; extern std::atomic g_web_server_running; extern std::atomic g_web_server_initialized; // ✅ fix24-v19: HTTP重定向服务器(HTTP->HTTPS自动跳转) extern std::unique_ptr g_http_redirect_thread; extern std::unique_ptr g_http_redirect_server; // fix24-v12: HTTPS/SSL配置 extern bool g_ssl_enabled; extern std::string g_ssl_cert_path; extern std::string g_ssl_key_path; // ✅ fix24-v16: Web服务端口(从config.ini [server] ip字段解析) extern int g_web_server_port; // ✅ fix24 功能一:登录认证系统全局对象 // 必须在 httplib.h 被 include 之前定义 OpenSSL 宏(auth_middleware.h 会触发 httplib.h 包含) #ifndef CPPHTTPLIB_OPENSSL_SUPPORT #define CPPHTTPLIB_OPENSSL_SUPPORT #endif #include "auth_db.h" #include "auth_session.h" #include "auth_rate_limiter.h" #include "auth_middleware.h" extern auth::AuthDB *g_auth_db; extern auth::SessionManager *g_session_mgr; extern auth::RateLimiter *g_rate_limiter; extern auth::AuthMiddleware *g_auth_middleware; extern bool g_auth_enabled; // 是否启用认证(config.ini [auth] enabled) extern std::string g_auth_db_path; // 认证数据库路径(config.ini [auth] db_path) // ==================== 系统监控配置 (config.ini [system]) ==================== extern int g_sys_monitor_interval; // 监控采样间隔(秒) extern int g_sys_temp_alert_threshold; // 温度告警阈值(°C) extern int g_sys_cpu_alert_threshold; // CPU告警阈值(%) extern std::string g_sys_db_path; // 系统监控数据库路径 extern int g_sys_history_retention_days; // 全量数据保留天数 extern int g_sys_aggregation_retention_days; // 聚合数据保留天数 extern std::string g_sys_flush_time; // 每日刷盘时间(HH:MM,默认23:20) // ==================== frpc 管理配置 (config.ini [frpc]) ==================== extern std::string g_frpc_config_path; // frpc.toml 路径 extern std::string g_frpc_backup_path; // 备份路径 extern std::string g_frpc_protected_tunnels; // 受保护隧道列表 extern int g_frpc_restart_check_timeout; // 重启后等待检测时间(秒) extern bool g_frpc_auto_rollback; // 重启失败自动回滚 extern bool g_frpc_web_enabled; // Web管理页面开关 extern std::string g_frpc_admin_addr; // frpc admin API 地址 extern int g_frpc_admin_port; // frpc admin API 端口 extern std::string g_frpc_admin_user; // frpc admin 用户名 extern std::string g_frpc_admin_password; // frpc admin 密码 // ==================== 终端/SSH 配置 (config.ini [terminal]) ==================== extern int g_ttyd_port; // ttyd 端口 extern std::string g_ttyd_credential; // ttyd 凭证 (user:pass) extern int g_ttyd_max_clients; // ttyd 最大客户端数 extern std::string g_ssh_keys_path; // authorized_keys 路径 // ==================== 日志管理配置 (config.ini [log]) ==================== extern int g_log_buffer_size_mb; // 日志缓冲区大小(MB) extern std::string g_log_flush_time; // 日志写回时间(HH:MM) extern std::string g_log_file; // 日志文件路径 // g_log_retention_days 已在 fix19 中声明(第106行),此处不重复声明 extern bool g_log_redirect_stdout; // 是否重定向 stdout/stderr // ✅ fix24-v36: 内存日志缓冲新增配置 extern bool g_log_enabled; // 是否启用内存日志模式(1=内存, 0=直写文件) extern bool g_log_auto_flush_on_exit; // 程序退出时是否自动刷盘 // 数据库记录结构 struct DbRecord { int id; std::string create_time; time_t capture_time = 0; // ✅ fix24-v13: 抓拍时刻Unix时间戳 std::string plate_number; std::string tb_num; int station_type; std::string lo_photo_path; std::string hi_photo_path; int lo_upload_status; int hi_upload_status; int feishu_status; int retry_count; }; // 重试结果 struct RetryResult { bool success = false; std::string message; std::string tb_num; int uploaded_photos = 0; }; #endif // COMMON_H