fix recall
This commit is contained in:
@@ -242,6 +242,29 @@ bool is_blank(const std::string& str)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取本地当天 00:00:00 的毫秒时间戳
|
||||||
|
static inline uint64_t get_local_midnight_ms_today() {
|
||||||
|
std::time_t now = std::time(nullptr);
|
||||||
|
std::tm local_tm{};
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
localtime_s(&local_tm, &now);
|
||||||
|
#else
|
||||||
|
local_tm = *std::localtime(&now);
|
||||||
|
#endif
|
||||||
|
local_tm.tm_hour = 0;
|
||||||
|
local_tm.tm_min = 0;
|
||||||
|
local_tm.tm_sec = 0;
|
||||||
|
std::time_t midnight_sec = std::mktime(&local_tm); // 本地时区
|
||||||
|
return static_cast<uint64_t>(midnight_sec) * 1000ULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取“本地前一天 00:00:00”的毫秒时间戳
|
||||||
|
static inline uint64_t get_local_midnight_ms_prev_day() {
|
||||||
|
uint64_t today_ms = get_local_midnight_ms_today();
|
||||||
|
const uint64_t one_day_ms = 24ULL * 60ULL * 60ULL * 1000ULL;
|
||||||
|
return today_ms - one_day_ms;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////配置文件读取
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////配置文件读取
|
||||||
|
|
||||||
//将 G_TEST_LIST 按逗号分割,填充 TESTARRAY
|
//将 G_TEST_LIST 按逗号分割,填充 TESTARRAY
|
||||||
@@ -1147,12 +1170,22 @@ static std::vector<std::string> build_direct_filenames(const std::string& monito
|
|||||||
}
|
}
|
||||||
|
|
||||||
static long long parse_time_to_epoch(const std::string& time_str) {
|
static long long parse_time_to_epoch(const std::string& time_str) {
|
||||||
|
std::string s = time_str;
|
||||||
|
|
||||||
|
// ★去掉可能存在的微秒部分(例如 .000000)
|
||||||
|
size_t dot_pos = s.find('.');
|
||||||
|
if (dot_pos != std::string::npos) {
|
||||||
|
s = s.substr(0, dot_pos);
|
||||||
|
}
|
||||||
|
|
||||||
std::tm tm = {};
|
std::tm tm = {};
|
||||||
std::istringstream ss(time_str);
|
std::istringstream ss(s);
|
||||||
ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
|
ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
|
||||||
if (ss.fail()) {
|
if (ss.fail()) {
|
||||||
return 0;
|
std::cerr << "[parse_time_to_epoch] failed to parse time string: " << s << std::endl;
|
||||||
|
return -1; // ★返回 -1 表示失败(便于判断)
|
||||||
}
|
}
|
||||||
|
|
||||||
return static_cast<long long>(std::mktime(&tm));
|
return static_cast<long long>(std::mktime(&tm));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1308,8 +1341,55 @@ int recall_json_handle_from_mq(const std::string& body)
|
|||||||
std::string digits = get_monitor_digits_from_terminal_list(terminalId, monId);//有锁
|
std::string digits = get_monitor_digits_from_terminal_list(terminalId, monId);//有锁
|
||||||
if (digits.empty()) { std::cout << "monitorId数字提取失败: " << monId << std::endl; continue; }
|
if (digits.empty()) { std::cout << "monitorId数字提取失败: " << monId << std::endl; continue; }
|
||||||
|
|
||||||
|
// 根据 monitorId 和提取的数字初始化补招记录
|
||||||
|
init_recall_record_file(guid, terminalId, monId, "", "");
|
||||||
|
|
||||||
|
//根据时间戳单独补招事件
|
||||||
|
// ★新增(dt==2 同步生成“按小时”的事件补招到 recall_list,与 dt==1 逻辑一致)——开始
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock2(ledgermtx); // 复用与 dt==1 相同的加锁粒度
|
||||||
|
// 找终端
|
||||||
|
terminal_dev* dev_nc = NULL;
|
||||||
|
for (auto& d : terminal_devlist) if (d.terminal_id == terminalId) { dev_nc = &d; break; }
|
||||||
|
if (!dev_nc) { /* 理论上前面已校验,这里兜底 */ continue; }
|
||||||
|
|
||||||
|
// 找监测点
|
||||||
|
ledger_monitor* lm = NULL;
|
||||||
|
for (auto itLm = dev_nc->line.begin(); itLm != dev_nc->line.end(); ++itLm) {
|
||||||
|
if (!itLm->monitor_id.empty() && itLm->monitor_id == monId) { lm = &(*itLm); break; }
|
||||||
|
}
|
||||||
|
if (!lm) { std::cout << "monitorId未在terminal内找到(直下事件回灌): " << monId << " @ " << terminalId << std::endl; continue; }
|
||||||
|
|
||||||
|
// 将 timeList 的每个时间点映射为 [该整点, 下一整点) 的一小时窗口
|
||||||
for (const auto& t : rec["timeList"]) {
|
for (const auto& t : rec["timeList"]) {
|
||||||
if (!t.is_string()) continue;
|
if (!t.is_string()) continue;
|
||||||
|
std::string tstr = t.get<std::string>();
|
||||||
|
|
||||||
|
long long ep = parse_time_to_epoch(tstr); // 需与系统时间解析格式一致(已有同名函数)
|
||||||
|
if (ep < 0) {
|
||||||
|
std::cout << "[direct->event] parse_time_to_epoch fail: " << tstr << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
long long hour_start = ep - (ep % 3600);
|
||||||
|
long long hour_end = hour_start + 3600 - 1; // 与 Get_Recall_Time_Char 产物风格保持一致(闭区间)
|
||||||
|
|
||||||
|
RecallMonitor rm;
|
||||||
|
rm.recall_guid = guid;
|
||||||
|
rm.recall_status = 0;
|
||||||
|
rm.StartTime = epoch_to_datetime_str(hour_start);
|
||||||
|
rm.EndTime = epoch_to_datetime_str(hour_end);
|
||||||
|
rm.STEADY = "0"; // 与 dt==1 一致:事件(波形)
|
||||||
|
rm.VOLTAGE = "1"; // 与 dt==1 一致
|
||||||
|
|
||||||
|
lm->recall_list.push_back(rm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ★新增(dt==2 同步生成“按小时”的事件补招到 recall_list,与 dt==1 逻辑一致)——结束
|
||||||
|
//根据时间戳单独补招事件
|
||||||
|
|
||||||
|
for (const auto& t : rec["timeList"]) {
|
||||||
|
if (!t.is_string()) continue;
|
||||||
|
|
||||||
std::string ts_compact = compact_ts_for_filename(t.get<std::string>());
|
std::string ts_compact = compact_ts_for_filename(t.get<std::string>());
|
||||||
if (ts_compact.empty()) { std::cout << "时间解析失败: " << t << std::endl; continue; }
|
if (ts_compact.empty()) { std::cout << "时间解析失败: " << t << std::endl; continue; }
|
||||||
|
|
||||||
@@ -1324,8 +1404,6 @@ int recall_json_handle_from_mq(const std::string& body)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init_recall_record_file(guid, terminalId, monId, "", "");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} else if (dt == 0 || dt == 1) { //一个装置对应一个guid对应多个监测点的多个时间段
|
} else if (dt == 0 || dt == 1) { //一个装置对应一个guid对应多个监测点的多个时间段
|
||||||
// ▲保持老逻辑(与“对象+data”一致):timeInterval 数组
|
// ▲保持老逻辑(与“对象+data”一致):timeInterval 数组
|
||||||
@@ -2831,7 +2909,7 @@ bool extract_timestamp_from_cfg_file(const std::string& cfg_path, long long& sta
|
|||||||
return start_tm > 0 && trig_tm > 0;
|
return start_tm > 0 && trig_tm > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool compare_qvvr_and_file(const std::string& cfg_path, const std::vector<qvvr_data>& data_list,qvvr_data& matched_data) {
|
bool compare_qvvr_and_file(const std::string& cfg_path, std::vector<qvvr_data>& data_list,qvvr_data& matched_data) {
|
||||||
long long start_tm = 0;
|
long long start_tm = 0;
|
||||||
long long trig_tm = 0;
|
long long trig_tm = 0;
|
||||||
|
|
||||||
@@ -2845,9 +2923,12 @@ bool compare_qvvr_and_file(const std::string& cfg_path, const std::vector<qvvr_d
|
|||||||
std::cout << "[调试] 提取到的起始时间戳: " << start_tm << ", 触发时间戳: " << trig_tm << "\n";
|
std::cout << "[调试] 提取到的起始时间戳: " << start_tm << ", 触发时间戳: " << trig_tm << "\n";
|
||||||
|
|
||||||
// 遍历所有暂态事件,查找与 trig_tm 匹配的
|
// 遍历所有暂态事件,查找与 trig_tm 匹配的
|
||||||
for (const auto& data : data_list) {
|
for (auto& data : data_list) {
|
||||||
long long diff = static_cast<long long>(data.QVVR_time) - trig_tm;
|
long long diff = static_cast<long long>(data.QVVR_time) - trig_tm;
|
||||||
if (std::abs(diff) <= 1) {
|
if (std::abs(diff) <= 1) {
|
||||||
|
|
||||||
|
data.is_pair = true; // 标记为已匹配
|
||||||
|
|
||||||
matched_data = data; // 返回匹配到的事件
|
matched_data = data; // 返回匹配到的事件
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -3683,6 +3764,41 @@ void check_and_backup_qvvr_files() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////定时清理内存的暂态事件
|
||||||
|
// 每天执行一次;删除“is_pair=false 且 QVVR_time < 昨天 00:00:00(毫秒)”的 qvvr_data
|
||||||
|
void cleanup_old_unpaired_qvvr_events() {
|
||||||
|
const uint64_t cutoff_ms = get_local_midnight_ms_prev_day();
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lk(ledgermtx); // ★新增:加锁保护台账
|
||||||
|
|
||||||
|
size_t total_removed = 0;
|
||||||
|
for (auto& dev : terminal_devlist) {
|
||||||
|
for (auto& lm : dev.line) {
|
||||||
|
auto& vec = lm.qvvrevent.qvvrdata;
|
||||||
|
const size_t before = vec.size();
|
||||||
|
|
||||||
|
// 删除条件:未配对 且 发生时间 < 昨天 00:00:00(本地时区毫秒)
|
||||||
|
vec.erase(std::remove_if(vec.begin(), vec.end(),
|
||||||
|
[&](const qvvr_data& d){
|
||||||
|
return (d.is_pair == false) && (d.QVVR_time < cutoff_ms);
|
||||||
|
}),
|
||||||
|
vec.end());
|
||||||
|
|
||||||
|
const size_t removed = before - vec.size();
|
||||||
|
if (removed > 0) {
|
||||||
|
total_removed += removed;
|
||||||
|
std::cout << "[cleanup_qvvr] dev=" << dev.terminal_id
|
||||||
|
<< " mon=" << lm.monitor_id
|
||||||
|
<< " removed=" << removed
|
||||||
|
<< " cutoff_ms=" << cutoff_ms
|
||||||
|
<< " (prev 00:00 local)" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "[cleanup_qvvr] total_removed=" << total_removed << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////定值信息发送接口函数
|
/////////////////////////////////////////////////////////////////////////////////////////定值信息发送接口函数
|
||||||
bool save_set_value(const std::string &dev_id, unsigned char mp_index, const std::vector<float> &fabsf) {
|
bool save_set_value(const std::string &dev_id, unsigned char mp_index, const std::vector<float> &fabsf) {
|
||||||
std::lock_guard<std::mutex> lock(ledgermtx);
|
std::lock_guard<std::mutex> lock(ledgermtx);
|
||||||
@@ -4551,16 +4667,71 @@ static bool make_target_key_from_filename(const std::string& fname, std::string&
|
|||||||
out_key.append(monitor).append("_").append(ymd).append("_").append(hms);
|
out_key.append(monitor).append("_").append(ymd).append("_").append(hms);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////补招文件匹配事件
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////补招文件逻辑
|
||||||
// ====== ★修改:check_recall_stat —— 加入“两步法”状态机 ======
|
// ====== ★修改:check_recall_stat —— 加入“两步法”状态机 ======
|
||||||
void check_recall_file() {
|
void check_recall_file() {
|
||||||
|
|
||||||
std::vector<RecallTask> tasks; // 本轮要发送的“每终端一条”(目录请求 或 文件下载 请求)
|
std::vector<RecallTask> tasks; // 本轮要发送的“每终端一条”(目录请求 或 文件下载 请求)
|
||||||
|
|
||||||
|
// ★修改开始:新增“待上传动作”容器与两个小工具(局部作用域,函数私有)
|
||||||
|
struct PendingUpload {
|
||||||
|
std::string terminal_id;
|
||||||
|
unsigned short logical_seq = 0;
|
||||||
|
std::vector<std::string> files_to_send; // 完整路径(含MAC目录)
|
||||||
|
qvvr_data matched{}; // 与 .cfg 匹配到的事件(如有)
|
||||||
|
bool has_matched = false;
|
||||||
|
// 用于回锁后在台账中定位并删掉对应 qvvr_file 组
|
||||||
|
std::set<std::string> sig_names; // 仅文件名集合
|
||||||
|
std::set<std::string> sig_downs; // 完整路径集合(与 files_to_send 相同内容的 set)
|
||||||
|
};
|
||||||
|
|
||||||
|
auto ExtractFileNames = [](const std::vector<std::string>& full_paths) {
|
||||||
|
std::vector<std::string> names;
|
||||||
|
names.reserve(full_paths.size());
|
||||||
|
for (const auto& p : full_paths) {
|
||||||
|
auto s = sanitize(p);
|
||||||
|
size_t pos = s.find_last_of("/\\");
|
||||||
|
std::string name = (pos == std::string::npos) ? s : s.substr(pos + 1);
|
||||||
|
names.push_back(sanitize(name));
|
||||||
|
}
|
||||||
|
std::sort(names.begin(), names.end());
|
||||||
|
names.erase(std::unique(names.begin(), names.end()), names.end());
|
||||||
|
return names;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto MakeSigNames = [&](const std::vector<std::string>& full_paths){
|
||||||
|
auto names = ExtractFileNames(full_paths);
|
||||||
|
return std::set<std::string>(names.begin(), names.end());
|
||||||
|
};
|
||||||
|
// ★修改结束
|
||||||
|
|
||||||
|
std::vector<PendingUpload> pending_uploads; // ★修改:锁外执行上传与清理
|
||||||
|
|
||||||
|
|
||||||
{ //锁作用域
|
{ //锁作用域
|
||||||
std::lock_guard<std::mutex> lock(ledgermtx);
|
std::lock_guard<std::mutex> lock(ledgermtx);
|
||||||
|
|
||||||
|
// ★优先级开始:事件优先 —— 若任一测点存在事件补招(recall_list)尚未清空,则本轮跳过文件补招
|
||||||
|
{
|
||||||
|
bool has_event_pending = false;
|
||||||
|
for (const auto& dev : terminal_devlist) {
|
||||||
|
for (const auto& lm : dev.line) {
|
||||||
|
if (!lm.recall_list.empty()) { // 只要存在事件待处理就让位
|
||||||
|
has_event_pending = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has_event_pending) break;
|
||||||
|
}
|
||||||
|
if (has_event_pending) {
|
||||||
|
std::cout << "[check_recall_file] skip this round: event recall pending, give priority to check_recall_event()\n";
|
||||||
|
return; // 直接让位给 check_recall_event,本轮不处理文件补招
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ★优先级结束
|
||||||
|
|
||||||
for (auto& dev : terminal_devlist) {
|
for (auto& dev : terminal_devlist) {
|
||||||
// 仅处理“正在补招/空闲”终端,与你原逻辑一致
|
// 仅处理“正在补招/空闲”终端,与你原逻辑一致
|
||||||
if (dev.busytype != static_cast<int>(DeviceState::READING_STATSFILE) &&
|
if (dev.busytype != static_cast<int>(DeviceState::READING_STATSFILE) &&
|
||||||
@@ -4778,6 +4949,19 @@ void check_recall_file() {
|
|||||||
} else {
|
} else {
|
||||||
// 无目录可查
|
// 无目录可查
|
||||||
front.recall_status = static_cast<int>(RecallStatus::FAILED);
|
front.recall_status = static_cast<int>(RecallStatus::FAILED);
|
||||||
|
|
||||||
|
std::string msg_fail;
|
||||||
|
if (front.direct_mode) {
|
||||||
|
msg_fail = std::string("监测点:") + lm.monitor_name
|
||||||
|
+ " 补招波形文件未找到,目标时标:"
|
||||||
|
+ front.target_filetimes;
|
||||||
|
} else {
|
||||||
|
msg_fail = std::string("监测点:") + lm.monitor_name
|
||||||
|
+ " 补招波形文件未找到,时间范围:"
|
||||||
|
+ front.StartTime + " ~ " + front.EndTime;
|
||||||
|
}
|
||||||
|
append_recall_record_line(dev.guid, lm.monitor_id, msg_fail);
|
||||||
|
|
||||||
std::cout << "[check_recall_stat] no dir candidates, FAIL dev=" << dev.terminal_id
|
std::cout << "[check_recall_stat] no dir candidates, FAIL dev=" << dev.terminal_id
|
||||||
<< " monitor=" << lm.monitor_id << std::endl;
|
<< " monitor=" << lm.monitor_id << std::endl;
|
||||||
}
|
}
|
||||||
@@ -4812,7 +4996,17 @@ void check_recall_file() {
|
|||||||
// 所有目录都失败
|
// 所有目录都失败
|
||||||
front.recall_status = static_cast<int>(RecallStatus::FAILED);
|
front.recall_status = static_cast<int>(RecallStatus::FAILED);
|
||||||
|
|
||||||
|
std::string msg_fail;
|
||||||
|
if (front.direct_mode) {
|
||||||
|
msg_fail = std::string("监测点:") + lm.monitor_name
|
||||||
|
+ " 补招波形文件未找到,目标时标:"
|
||||||
|
+ front.target_filetimes;
|
||||||
|
} else {
|
||||||
|
msg_fail = std::string("监测点:") + lm.monitor_name
|
||||||
|
+ " 补招波形文件未找到,时间范围:"
|
||||||
|
+ front.StartTime + " ~ " + front.EndTime;
|
||||||
|
}
|
||||||
|
append_recall_record_line(dev.guid, lm.monitor_id, msg_fail);
|
||||||
|
|
||||||
std::cout << "[check_recall_stat] all dir failed, FAIL dev=" << dev.terminal_id
|
std::cout << "[check_recall_stat] all dir failed, FAIL dev=" << dev.terminal_id
|
||||||
<< " monitor=" << lm.monitor_id << std::endl;
|
<< " monitor=" << lm.monitor_id << std::endl;
|
||||||
@@ -4945,7 +5139,21 @@ void check_recall_file() {
|
|||||||
// 进入下载阶段
|
// 进入下载阶段
|
||||||
|
|
||||||
front.required_files.clear();
|
front.required_files.clear();
|
||||||
for (const auto& p : front.download_queue) front.required_files.insert(p);
|
//for (const auto& p : front.download_queue) front.required_files.insert(p);
|
||||||
|
|
||||||
|
//转成本地保存路径 download/<dev.addr_str>/<fname>
|
||||||
|
front.required_files.clear();
|
||||||
|
{
|
||||||
|
const std::string base_dir = std::string("download/") + sanitize(dev.addr_str);
|
||||||
|
for (const auto& p : front.download_queue) {
|
||||||
|
// p 形如 "<cur_dir>/<fname>" —— 提取纯文件名
|
||||||
|
std::string fname = sanitize(extract_filename1(p));
|
||||||
|
if (!fname.empty()) {
|
||||||
|
front.required_files.insert(base_dir + "/" + fname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
front.file_success.clear();
|
front.file_success.clear();
|
||||||
|
|
||||||
front.phase = RecallPhase::DOWNLOADING;
|
front.phase = RecallPhase::DOWNLOADING;
|
||||||
@@ -4971,7 +5179,80 @@ void check_recall_file() {
|
|||||||
<< std::endl;
|
<< std::endl;
|
||||||
if (!front.required_files.empty()
|
if (!front.required_files.empty()
|
||||||
&& front.file_success.size() == front.required_files.size()) {
|
&& front.file_success.size() == front.required_files.size()) {
|
||||||
front.recall_status = static_cast<int>(RecallStatus::DONE);
|
|
||||||
|
front.recall_status = static_cast<int>(RecallStatus::DONE);//两个文件都下好了标记为成功
|
||||||
|
|
||||||
|
//更新事件
|
||||||
|
// ★修改开始:替换“assign_qvvr_file_list + update_qvvr_file_download(有锁)”
|
||||||
|
// 组装完整路径列表
|
||||||
|
std::vector<std::string> fullFilenames;
|
||||||
|
fullFilenames.reserve(front.required_files.size());
|
||||||
|
for (const auto& f : front.required_files) fullFilenames.push_back(f);
|
||||||
|
|
||||||
|
// 仅对“当前监测点”添加一组 qvvr_file(file_name=仅文件名,下载标记留到下行)
|
||||||
|
{
|
||||||
|
qvvr_file qfile;
|
||||||
|
auto names = ExtractFileNames(fullFilenames);
|
||||||
|
qfile.file_name.assign(names.begin(), names.end());
|
||||||
|
qfile.is_download = false;
|
||||||
|
qfile.is_pair = false;
|
||||||
|
qfile.file_time_count = 0;
|
||||||
|
qfile.used_status = true;
|
||||||
|
lm.qvvrevent.qvvrfile.push_back(std::move(qfile));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 把这组标记为“已全部下载”,并尝试用 .cfg 匹配事件;准备锁外上传动作
|
||||||
|
{
|
||||||
|
// 找到刚刚 append 的那组:file_name 集合 == fullFilenames 的文件名集合
|
||||||
|
auto want_names = MakeSigNames(fullFilenames);
|
||||||
|
auto it_qf = std::find_if(lm.qvvrevent.qvvrfile.begin(),
|
||||||
|
lm.qvvrevent.qvvrfile.end(),
|
||||||
|
[&](const qvvr_file& x){
|
||||||
|
std::set<std::string> s(x.file_name.begin(), x.file_name.end());
|
||||||
|
return s == want_names;
|
||||||
|
});
|
||||||
|
if (it_qf != lm.qvvrevent.qvvrfile.end()) {
|
||||||
|
// 原:it_qf->file_download = fullFilenames;
|
||||||
|
it_qf->file_download.clear();
|
||||||
|
it_qf->file_download.assign(fullFilenames.begin(), fullFilenames.end()); // ★修复:vector -> list
|
||||||
|
it_qf->is_download = true;
|
||||||
|
|
||||||
|
// 寻找第一个 .cfg 做匹配
|
||||||
|
qvvr_data matched{};
|
||||||
|
bool has_matched = false;
|
||||||
|
for (const auto& p : it_qf->file_download) {
|
||||||
|
auto s = sanitize(p);
|
||||||
|
auto dot = s.find_last_of('.');
|
||||||
|
if (dot != std::string::npos) {
|
||||||
|
std::string ext = s.substr(dot);
|
||||||
|
for (auto& c : ext) c = (char)std::tolower((unsigned char)c);
|
||||||
|
if (ext == ".cfg") {
|
||||||
|
if (compare_qvvr_and_file(p, lm.qvvrevent.qvvrdata, matched)) {
|
||||||
|
it_qf->is_pair = true;
|
||||||
|
has_matched = true;
|
||||||
|
}
|
||||||
|
break; // 只看一个 .cfg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录一个“待上传动作”,锁外执行上传、回写 JSON、删文件与回锁清理
|
||||||
|
PendingUpload pu;
|
||||||
|
pu.terminal_id = dev.terminal_id;
|
||||||
|
try { pu.logical_seq = static_cast<unsigned short>(std::stoi(lm.logical_device_seq)); }
|
||||||
|
catch (...) { pu.logical_seq = 0; }
|
||||||
|
// 原:pu.files_to_send = it_qf->file_download;
|
||||||
|
pu.files_to_send.clear();
|
||||||
|
pu.files_to_send.assign(it_qf->file_download.begin(), it_qf->file_download.end()); // ★修复:list -> vector
|
||||||
|
pu.has_matched = has_matched;
|
||||||
|
pu.matched = matched;
|
||||||
|
pu.sig_names = want_names;
|
||||||
|
pu.sig_downs = std::set<std::string>(pu.files_to_send.begin(), pu.files_to_send.end());
|
||||||
|
pending_uploads.push_back(std::move(pu));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ★修改结束
|
||||||
|
|
||||||
std::cout << "[check_recall_stat] DONE dev=" << dev.terminal_id
|
std::cout << "[check_recall_stat] DONE dev=" << dev.terminal_id
|
||||||
<< " monitor=" << lm.monitor_id
|
<< " monitor=" << lm.monitor_id
|
||||||
<< " ok=" << front.file_success.size()
|
<< " ok=" << front.file_success.size()
|
||||||
@@ -5079,6 +5360,19 @@ void check_recall_file() {
|
|||||||
<< " end=" << front.EndTime << std::endl;
|
<< " end=" << front.EndTime << std::endl;
|
||||||
} else {
|
} else {
|
||||||
front.recall_status = static_cast<int>(RecallStatus::FAILED); //目录列表空,失败
|
front.recall_status = static_cast<int>(RecallStatus::FAILED); //目录列表空,失败
|
||||||
|
|
||||||
|
std::string msg_fail;
|
||||||
|
if (front.direct_mode) {
|
||||||
|
msg_fail = std::string("监测点:") + lm.monitor_name
|
||||||
|
+ " 补招波形文件未找到,目标时标:"
|
||||||
|
+ front.target_filetimes;
|
||||||
|
} else {
|
||||||
|
msg_fail = std::string("监测点:") + lm.monitor_name
|
||||||
|
+ " 补招波形文件未找到,时间范围:"
|
||||||
|
+ front.StartTime + " ~ " + front.EndTime;
|
||||||
|
}
|
||||||
|
append_recall_record_line(dev.guid, lm.monitor_id, msg_fail);
|
||||||
|
|
||||||
std::cout << "[check_recall_stat] empty dir_candidates, FAIL dev=" << dev.terminal_id
|
std::cout << "[check_recall_stat] empty dir_candidates, FAIL dev=" << dev.terminal_id
|
||||||
<< " monitor=" << lm.monitor_id << std::endl;
|
<< " monitor=" << lm.monitor_id << std::endl;
|
||||||
}
|
}
|
||||||
@@ -5090,6 +5384,82 @@ void check_recall_file() {
|
|||||||
} // end for dev
|
} // end for dev
|
||||||
} // 解锁
|
} // 解锁
|
||||||
|
|
||||||
|
// ★修改开始:锁外执行上传、回写 JSON、删除本地文件,并回锁做台账清理
|
||||||
|
for (auto& pu : pending_uploads) {
|
||||||
|
// 1) 上传所有文件:构造临时 qvvr_file 给现有 SendAllQvvrFiles 使用
|
||||||
|
qvvr_file tmp;
|
||||||
|
// 原:tmp.file_download = pu.files_to_send;
|
||||||
|
tmp.file_download.clear();
|
||||||
|
tmp.file_download.assign(pu.files_to_send.begin(), pu.files_to_send.end()); // ★修复:vector -> list
|
||||||
|
|
||||||
|
std::string wavepath;
|
||||||
|
bool sent_ok = SendAllQvvrFiles(tmp, wavepath);
|
||||||
|
|
||||||
|
if (sent_ok) {
|
||||||
|
// 2) 回写 JSON(若匹配到事件)
|
||||||
|
if (pu.has_matched) {
|
||||||
|
transfer_json_qvvr_data(pu.terminal_id,
|
||||||
|
pu.logical_seq,
|
||||||
|
pu.matched.QVVR_Amg,
|
||||||
|
pu.matched.QVVR_PerTime,
|
||||||
|
pu.matched.QVVR_time,
|
||||||
|
pu.matched.QVVR_type,
|
||||||
|
pu.matched.phase,
|
||||||
|
wavepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) 删除本地文件
|
||||||
|
for (const auto& f : pu.files_to_send) {
|
||||||
|
if (std::remove(f.c_str()) != 0) {
|
||||||
|
std::cerr << "[Cleanup] Failed to delete file: " << f << "\n";
|
||||||
|
} else {
|
||||||
|
std::cout << "[Cleanup] Deleted uploaded file: " << f << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) 回锁做台账清理:删除对应 qvvr_file 组与匹配到的事件
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(ledgermtx);
|
||||||
|
for (auto& dev : terminal_devlist) {
|
||||||
|
if (dev.terminal_id != pu.terminal_id) continue;
|
||||||
|
for (auto& lm : dev.line) {
|
||||||
|
unsigned short seq = 0;
|
||||||
|
try { seq = static_cast<unsigned short>(std::stoi(lm.logical_device_seq)); } catch (...) { seq = 0; }
|
||||||
|
if (seq != pu.logical_seq) continue;
|
||||||
|
|
||||||
|
// 定位并删除相同签名的组
|
||||||
|
auto it_qf = std::find_if(lm.qvvrevent.qvvrfile.begin(),
|
||||||
|
lm.qvvrevent.qvvrfile.end(),
|
||||||
|
[&](const qvvr_file& x){
|
||||||
|
std::set<std::string> n(x.file_name.begin(), x.file_name.end());
|
||||||
|
std::set<std::string> d(x.file_download.begin(), x.file_download.end());
|
||||||
|
return n==pu.sig_names && d==pu.sig_downs;
|
||||||
|
});
|
||||||
|
if (it_qf != lm.qvvrevent.qvvrfile.end()) {
|
||||||
|
lm.qvvrevent.qvvrfile.erase(it_qf);
|
||||||
|
} else {
|
||||||
|
std::cerr << "[Cleanup] qvvrfile changed; target group not found, skip erase\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如有匹配事件,按时间删除
|
||||||
|
if (pu.has_matched) {
|
||||||
|
auto it_evt = std::find_if(lm.qvvrevent.qvvrdata.begin(),
|
||||||
|
lm.qvvrevent.qvvrdata.end(),
|
||||||
|
[&](const qvvr_data& d){ return d.QVVR_time == pu.matched.QVVR_time; });
|
||||||
|
if (it_evt != lm.qvvrevent.qvvrdata.end()) {
|
||||||
|
lm.qvvrevent.qvvrdata.erase(it_evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cerr << "[check_recall_file][Upload] SendAllQvvrFiles failed for terminal="
|
||||||
|
<< pu.terminal_id << " seq=" << pu.logical_seq << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ★修改结束
|
||||||
|
|
||||||
// ★说明:本函数不主动把 RUNNING 改 DONE/FAILED;
|
// ★说明:本函数不主动把 RUNNING 改 DONE/FAILED;
|
||||||
// 由“其他线程”在目录/下载结果返回后,找到对应 dev/monitor 的 lm.recall_list_static.front(),
|
// 由“其他线程”在目录/下载结果返回后,找到对应 dev/monitor 的 lm.recall_list_static.front(),
|
||||||
// 写入:
|
// 写入:
|
||||||
@@ -5386,13 +5756,16 @@ void on_device_response_minimal(int response_code,
|
|||||||
running_front->list_result = ActionResult::OK;
|
running_front->list_result = ActionResult::OK;
|
||||||
std::cout << "[RESP][FILEMENU->(EVENT/STATS)FILE][OK] dev=" << id
|
std::cout << "[RESP][FILEMENU->(EVENT/STATS)FILE][OK] dev=" << id
|
||||||
<< " monitor=" << running_monitor->monitor_id
|
<< " monitor=" << running_monitor->monitor_id
|
||||||
<< " dir=" << running_front->cur_dir << std::endl;
|
<< " dir=" << running_front->cur_dir
|
||||||
|
<< " entries=" << running_front->dir_files[running_front->cur_dir].size()
|
||||||
|
<< std::endl;
|
||||||
} else {
|
} else {
|
||||||
running_front->list_result = ActionResult::FAIL;
|
running_front->dir_files[running_front->cur_dir] = {};
|
||||||
|
running_front->list_result = ActionResult::OK;
|
||||||
std::cout << "[RESP][FILEMENU->(EVENT/STATS)FILE][WARN] dev=" << id
|
std::cout << "[RESP][FILEMENU->(EVENT/STATS)FILE][WARN] dev=" << id
|
||||||
<< " monitor=" << running_monitor->monitor_id
|
<< " monitor=" << running_monitor->monitor_id
|
||||||
<< " dir=" << running_front->cur_dir
|
<< " dir=" << running_front->cur_dir
|
||||||
<< " names missing in cache" << std::endl;
|
<< " cache miss -> treat as empty OK" << std::endl;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
running_front->list_result = ActionResult::FAIL;
|
running_front->list_result = ActionResult::FAIL;
|
||||||
@@ -5478,6 +5851,13 @@ void on_device_response_minimal(int response_code,
|
|||||||
for (auto& lm : dev->line) {
|
for (auto& lm : dev->line) {
|
||||||
if (lm.recall_list_static.empty()) continue;
|
if (lm.recall_list_static.empty()) continue;
|
||||||
RecallFile& f = lm.recall_list_static.front();
|
RecallFile& f = lm.recall_list_static.front();
|
||||||
|
|
||||||
|
std::cout << "[RESP][FILEDATA][SCAN] dev=" << id
|
||||||
|
<< " monitor=" << lm.monitor_id
|
||||||
|
<< " status=" << static_cast<int>(f.recall_status)
|
||||||
|
<< " phase=" << static_cast<int>(f.phase)
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
if (f.recall_status == static_cast<int>(RecallStatus::RUNNING) &&
|
if (f.recall_status == static_cast<int>(RecallStatus::RUNNING) &&
|
||||||
f.phase == RecallPhase::DOWNLOADING) {
|
f.phase == RecallPhase::DOWNLOADING) {
|
||||||
matched_monitor = &lm;
|
matched_monitor = &lm;
|
||||||
@@ -5499,7 +5879,7 @@ void on_device_response_minimal(int response_code,
|
|||||||
RecallFile& f = matched_monitor->recall_list_static.front();
|
RecallFile& f = matched_monitor->recall_list_static.front();
|
||||||
if (f.recall_status == static_cast<int>(RecallStatus::RUNNING) &&
|
if (f.recall_status == static_cast<int>(RecallStatus::RUNNING) &&
|
||||||
f.phase == RecallPhase::DOWNLOADING) {
|
f.phase == RecallPhase::DOWNLOADING) {
|
||||||
running_front = &f; // 回退成功
|
running_front = &f;
|
||||||
std::cout << "[RESP][FILEDATA][FALLBACK-CID] dev=" << id
|
std::cout << "[RESP][FILEDATA][FALLBACK-CID] dev=" << id
|
||||||
<< " cid=" << static_cast<int>(cid)
|
<< " cid=" << static_cast<int>(cid)
|
||||||
<< " monitor=" << matched_monitor->monitor_id << std::endl;
|
<< " monitor=" << matched_monitor->monitor_id << std::endl;
|
||||||
@@ -5674,6 +6054,9 @@ bool append_qvvr_event(const std::string& terminal_id,
|
|||||||
<< ", phase=" << phase
|
<< ", phase=" << phase
|
||||||
<< "}" << std::endl;
|
<< "}" << std::endl;
|
||||||
|
|
||||||
|
//lnk20251030
|
||||||
|
q.is_pair = false;
|
||||||
|
|
||||||
q.used_status = true;
|
q.used_status = true;
|
||||||
q.QVVR_PerTime = fPersisstime_sec;
|
q.QVVR_PerTime = fPersisstime_sec;
|
||||||
q.QVVR_Amg = fMagnitude_pu;
|
q.QVVR_Amg = fMagnitude_pu;
|
||||||
@@ -5699,6 +6082,9 @@ bool append_qvvr_event(const std::string& terminal_id,
|
|||||||
<< ", phase=" << phase
|
<< ", phase=" << phase
|
||||||
<< "}" << std::endl;
|
<< "}" << std::endl;
|
||||||
|
|
||||||
|
//lnk20251030
|
||||||
|
q.is_pair = false;
|
||||||
|
|
||||||
q.used_status = true;
|
q.used_status = true;
|
||||||
q.QVVR_type = nType;
|
q.QVVR_type = nType;
|
||||||
q.QVVR_time = triggerTimeMs;
|
q.QVVR_time = triggerTimeMs;
|
||||||
@@ -5714,6 +6100,10 @@ bool append_qvvr_event(const std::string& terminal_id,
|
|||||||
|
|
||||||
// 5) 直接追加
|
// 5) 直接追加
|
||||||
qvvr_data q{};
|
qvvr_data q{};
|
||||||
|
|
||||||
|
//lnk20251030
|
||||||
|
q.is_pair = false;
|
||||||
|
|
||||||
q.used_status = true;
|
q.used_status = true;
|
||||||
q.QVVR_type = nType;
|
q.QVVR_type = nType;
|
||||||
q.QVVR_time = triggerTimeMs; // ms
|
q.QVVR_time = triggerTimeMs; // ms
|
||||||
@@ -6234,23 +6624,33 @@ bool SendFileWebAuto(const std::string& id,
|
|||||||
if (dev_ptr) {
|
if (dev_ptr) {
|
||||||
const int bt = dev_ptr->busytype;
|
const int bt = dev_ptr->busytype;
|
||||||
|
|
||||||
// 若处于“事件文件/统计文件”补招阶段,则使用补招专用上传接口
|
// 若处于“事件文件/统计文件”补招阶段,则使用补招专用上传目录:comtrade/wave/...
|
||||||
if (bt == static_cast<int>(DeviceState::READING_EVENTFILE) ||
|
if (bt == static_cast<int>(DeviceState::READING_EVENTFILE) ||
|
||||||
bt == static_cast<int>(DeviceState::READING_STATSFILE)) {
|
bt == static_cast<int>(DeviceState::READING_STATSFILE)) {
|
||||||
file_cloudpath = "comtrade/" + dirname_with_slash(local_path);
|
|
||||||
|
std::string rel = dirname_with_slash(local_path); // 例如:download/00:B7:.../
|
||||||
|
// 将 download/ 前缀替换为 wave/
|
||||||
|
if (!replace_prefix(rel, "download/", "wave/")) {
|
||||||
|
// 若不是以 download/ 开头,兜底拼 wave/ + 原目录
|
||||||
|
rel = "wave/" + rel;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_cloudpath = "comtrade/" + rel; // 目标:comtrade/wave/00:B7:.../
|
||||||
|
|
||||||
std::cout << "[SendFileWebAuto] dev=" << id
|
std::cout << "[SendFileWebAuto] dev=" << id
|
||||||
<< " busytype=" << bt
|
<< " busytype=" << bt
|
||||||
<< " -> use recall upload URL" << std::endl;
|
<< " -> use recall upload URL (cloud path=" << file_cloudpath << ")\n";
|
||||||
} else {
|
} else {
|
||||||
file_cloudpath = "download/" + dirname_with_slash(local_path);
|
// 非补招场景沿用原来的 download 目录
|
||||||
|
file_cloudpath = dirname_with_slash(local_path); // 保持原逻辑
|
||||||
std::cout << "[SendFileWebAuto] dev=" << id
|
std::cout << "[SendFileWebAuto] dev=" << id
|
||||||
<< " busytype=" << bt
|
<< " busytype=" << bt
|
||||||
<< " -> use default upload URL" << std::endl;
|
<< " -> use default upload URL (cloud path=" << file_cloudpath << ")\n";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std::cout << "[SendFileWebAuto][WARN] device not found for id=" << id
|
std::cout << "[SendFileWebAuto][WARN] device not found for id=" << id
|
||||||
<< ", fallback to default URL" << std::endl;
|
<< ", fallback to default URL\n";
|
||||||
|
file_cloudpath = dirname_with_slash(local_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 实际上传调用
|
// 实际上传调用
|
||||||
@@ -6267,3 +6667,4 @@ bool SendFileWebAuto(const std::string& id,
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ public:
|
|||||||
class qvvr_data
|
class qvvr_data
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
bool is_pair;
|
||||||
bool used_status; //是否占用
|
bool used_status; //是否占用
|
||||||
int QVVR_type; //暂态类型
|
int QVVR_type; //暂态类型
|
||||||
uint64_t QVVR_time; //暂态开始时间 unsigned longlong
|
uint64_t QVVR_time; //暂态开始时间 unsigned longlong
|
||||||
@@ -815,11 +816,20 @@ inline void print_terminal(const terminal_dev& tmnl) { print_terminal_common(tmn
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////小工具
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////小工具
|
||||||
// 小工具:判断 s 是否以 suffix 结尾
|
// 小工具:判断 s 是否以 suffix 结尾
|
||||||
static inline bool has_suffix(const std::string& s, const std::string& suffix) {
|
inline bool has_suffix(const std::string& s, const std::string& suffix) {
|
||||||
if (suffix.size() > s.size()) return false;
|
if (suffix.size() > s.size()) return false;
|
||||||
return std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
|
return std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 小工具:替换前缀
|
||||||
|
inline bool replace_prefix(std::string& s, const std::string& from, const std::string& to) {
|
||||||
|
if (s.rfind(from, 0) == 0) { // 以 from 为前缀
|
||||||
|
s.replace(0, from.size(), to);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
inline std::string trim_cstr(const char* s, size_t n) {
|
inline std::string trim_cstr(const char* s, size_t n) {
|
||||||
if (!s) return {};
|
if (!s) return {};
|
||||||
size_t end = 0;
|
size_t end = 0;
|
||||||
@@ -994,3 +1004,5 @@ bool SendFileWebAuto(const std::string& id,
|
|||||||
const std::string& local_path,
|
const std::string& local_path,
|
||||||
const std::string& remote_path,
|
const std::string& remote_path,
|
||||||
std::string& out_filename);
|
std::string& out_filename);
|
||||||
|
|
||||||
|
void cleanup_old_unpaired_qvvr_events();
|
||||||
@@ -85,6 +85,18 @@ std::unique_ptr<T> make_unique(Args&&... args) {
|
|||||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 把“今天”做成年月日整数(YYYYMMDD),用于“每天只清理一次”的判定
|
||||||
|
static inline int local_ymd_today() {
|
||||||
|
std::time_t now = std::time(nullptr);
|
||||||
|
std::tm local_tm{};
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
localtime_s(&local_tm, &now);
|
||||||
|
#else
|
||||||
|
local_tm = *std::localtime(&now);
|
||||||
|
#endif
|
||||||
|
return (local_tm.tm_year + 1900) * 10000 + (local_tm.tm_mon + 1) * 100 + local_tm.tm_mday;
|
||||||
|
}
|
||||||
|
|
||||||
//处理参数
|
//处理参数
|
||||||
bool parse_param(int argc, char* argv[]) {
|
bool parse_param(int argc, char* argv[]) {
|
||||||
for (int i = 1; i < argc; ++i) {
|
for (int i = 1; i < argc; ++i) {
|
||||||
@@ -138,35 +150,6 @@ bool parse_param(int argc, char* argv[]) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取前置类型
|
|
||||||
/*void init_global_function_enable() {
|
|
||||||
if (subdir == "cfg_stat_data") { // 历史稳态
|
|
||||||
g_node_id = STAT_DATA_BASE_NODE_ID;
|
|
||||||
auto_register_report_enabled = 1;
|
|
||||||
} else if (subdir == "cfg_3s_data") { // 实时
|
|
||||||
g_node_id = THREE_SECS_DATA_BASE_NODE_ID;
|
|
||||||
three_secs_enabled = 1;
|
|
||||||
} else if (subdir == "cfg_soe_comtrade") { // 告警、录波、暂态
|
|
||||||
g_node_id = SOE_COMTRADE_BASE_NODE_ID;
|
|
||||||
} else if (subdir == "cfg_recallhis_data") { // 补招
|
|
||||||
g_node_id = RECALL_HIS_DATA_BASE_NODE_ID;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
//获取功能名称
|
|
||||||
/*std::string get_front_msg_from_subdir() {
|
|
||||||
if (subdir.find("cfg_3s_data") != std::string::npos)
|
|
||||||
return "实时数据进程";
|
|
||||||
else if (subdir.find("cfg_soe_comtrade") != std::string::npos)
|
|
||||||
return "暂态和告警进程";
|
|
||||||
else if (subdir.find("cfg_recallhis_data") != std::string::npos)
|
|
||||||
return "稳态补招进程";
|
|
||||||
else if (subdir.find("cfg_stat_data") != std::string::npos)
|
|
||||||
return "稳态统计进程";
|
|
||||||
else
|
|
||||||
return "unknown";
|
|
||||||
}*/
|
|
||||||
|
|
||||||
//获取前置路径
|
//获取前置路径
|
||||||
std::string get_parent_directory() {
|
std::string get_parent_directory() {
|
||||||
// 获取当前工作目录
|
// 获取当前工作目录
|
||||||
@@ -456,9 +439,8 @@ void Front::FrontThread() {
|
|||||||
try {
|
try {
|
||||||
while (!m_bIsFrontThreadCancle) {
|
while (!m_bIsFrontThreadCancle) {
|
||||||
|
|
||||||
check_recall_file(); //处理补招文件-稳态和暂态
|
|
||||||
check_recall_event(); // 处理补招事件,从list中读取然后直接调用接口,每一条可能都不同测点,每个测点自己做好记录
|
check_recall_event(); // 处理补招事件,从list中读取然后直接调用接口,每一条可能都不同测点,每个测点自己做好记录
|
||||||
//check_ledger_update(); // 触发台账更新
|
check_recall_file(); //处理补招文件-稳态和暂态
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
}
|
}
|
||||||
@@ -490,6 +472,9 @@ void Front::OnTimerThread()
|
|||||||
|
|
||||||
send_heartbeat_to_queue("1");
|
send_heartbeat_to_queue("1");
|
||||||
|
|
||||||
|
//记录“上次做日清理”的日期(YYYYMMDD),确保每天只做一次
|
||||||
|
static int s_lastCleanupYMD = -1;
|
||||||
|
|
||||||
while (!m_IsTimerCancel)
|
while (!m_IsTimerCancel)
|
||||||
{
|
{
|
||||||
update_log_entries_countdown();
|
update_log_entries_countdown();
|
||||||
@@ -509,6 +494,18 @@ void Front::OnTimerThread()
|
|||||||
backupCounter = 0;
|
backupCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 按天清理 —— 发现“日期变更”则执行一次清理
|
||||||
|
{
|
||||||
|
const int todayYMD = local_ymd_today(); // YYYYMMDD(本地时区)
|
||||||
|
if (todayYMD != s_lastCleanupYMD) {
|
||||||
|
// 说明进入了新的一天:执行清理(删除前日及更早的未配对事件)
|
||||||
|
std::cout << "[OnTimerThread] daily cleanup start, today=" << todayYMD << std::endl;
|
||||||
|
cleanup_old_unpaired_qvvr_events(); // 调用清理内存的暂态事件
|
||||||
|
s_lastCleanupYMD = todayYMD;
|
||||||
|
std::cout << "[OnTimerThread] daily cleanup done" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hbCounter++;
|
hbCounter++;
|
||||||
backupCounter++;
|
backupCounter++;
|
||||||
|
|
||||||
|
|||||||
@@ -756,7 +756,7 @@ bool parseJsonMessageUD(const std::string& json_str, const std::string& output_d
|
|||||||
//int procNum = item.value("maxProcessNum", -1);
|
//int procNum = item.value("maxProcessNum", -1);
|
||||||
//json_data.maxProcessNum = std::to_string(procNum);
|
//json_data.maxProcessNum = std::to_string(procNum);
|
||||||
|
|
||||||
//json_data.addr_str = item.value("ip", "");
|
json_data.mac = item.value("ip", "");
|
||||||
//json_data.port = item.value("port", "");
|
//json_data.port = item.value("port", "");
|
||||||
//json_data.timestamp = item.value("updateTime", "");
|
//json_data.timestamp = item.value("updateTime", "");
|
||||||
json_data.Righttime = item.value("Righttime", "");
|
json_data.Righttime = item.value("Righttime", "");
|
||||||
|
|||||||
@@ -873,10 +873,43 @@ void process_received_message(string mac, string id,const char* data, size_t len
|
|||||||
file_data.size());
|
file_data.size());
|
||||||
std::cout << "File saved: " << file_path << std::endl;
|
std::cout << "File saved: " << file_path << std::endl;
|
||||||
|
|
||||||
|
//调试用
|
||||||
|
// 若是 .cfg,先查看并打印内容(限长)
|
||||||
|
{
|
||||||
|
auto dot = file_path.find_last_of('.');
|
||||||
|
std::string ext = (dot == std::string::npos) ? "" : file_path.substr(dot);
|
||||||
|
// 转小写比较
|
||||||
|
for (auto& c : ext) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
||||||
|
if (ext == ".cfg") {
|
||||||
|
std::ifstream fin(file_path, std::ios::binary);
|
||||||
|
if (!fin) {
|
||||||
|
std::cerr << "[CFG] open failed: " << file_path << " (" << std::strerror(errno) << ")\n";
|
||||||
|
} else {
|
||||||
|
// 读取前 8KB 作为预览,避免日志过大
|
||||||
|
constexpr size_t kMaxPreview = 8 * 1024;
|
||||||
|
std::string buf;
|
||||||
|
buf.resize(kMaxPreview);
|
||||||
|
fin.read(&buf[0], static_cast<std::streamsize>(kMaxPreview));
|
||||||
|
std::streamsize got = fin.gcount();
|
||||||
|
buf.resize(static_cast<size_t>(got));
|
||||||
|
|
||||||
|
std::cout << "================ [CFG PREVIEW BEGIN] ================\n";
|
||||||
|
std::cout << "path=" << file_path << ", size=" << file_data.size()
|
||||||
|
<< " bytes, preview=" << got << " bytes\n";
|
||||||
|
// 直接打印文本预览;如果包含不可见字符,会按原样输出
|
||||||
|
std::cout.write(buf.data(), static_cast<std::streamsize>(buf.size()));
|
||||||
|
if (static_cast<size_t>(got) == kMaxPreview && fin.peek() != EOF) {
|
||||||
|
std::cout << "\n...[truncated]\n";
|
||||||
|
}
|
||||||
|
std::cout << "\n================ [CFG PREVIEW END] ==================\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//使用接口上送文件lnk20250826
|
//使用接口上送文件lnk20250826
|
||||||
std::string filename;
|
std::string filename;
|
||||||
SendFileWebAuto(id, file_path, file_path, filename);//如果是补招文件的下载,下载后也是直接上传,上传成功后更新补招状态即可
|
//SendFileWebAuto(id, file_path, file_path, filename);//如果是补招文件的下载,下载后也是直接上传,这样单纯补招波形也可以保证传文件
|
||||||
std::cout << "File upload: " << filename << std::endl;
|
std::cout << "File download success wait for upload: " << filename << std::endl;
|
||||||
|
|
||||||
//通知文件上传
|
//通知文件上传
|
||||||
on_device_response_minimal(static_cast<int>(ResponseCode::OK), id, 0, static_cast<int>(DeviceState::READING_FILEDATA));
|
on_device_response_minimal(static_cast<int>(ResponseCode::OK), id, 0, static_cast<int>(DeviceState::READING_FILEDATA));
|
||||||
@@ -889,7 +922,7 @@ void process_received_message(string mac, string id,const char* data, size_t len
|
|||||||
}
|
}
|
||||||
|
|
||||||
//当前文件下载完毕,调整为空闲处理下一项工作(如果这里后续有新文件等待下载,一般已经存入等待队列等候处理了,调成空闲状态后直接就会开始新文件的下载工作)
|
//当前文件下载完毕,调整为空闲处理下一项工作(如果这里后续有新文件等待下载,一般已经存入等待队列等候处理了,调成空闲状态后直接就会开始新文件的下载工作)
|
||||||
ClientManager::instance().change_device_state(id, DeviceState::READING_FILEDATA);
|
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2080,9 +2113,9 @@ void process_received_message(string mac, string id,const char* data, size_t len
|
|||||||
<< ", 特征幅值: " << record.fMagntitude << " pu"
|
<< ", 特征幅值: " << record.fMagntitude << " pu"
|
||||||
<< ", 时间戳: " << record.triggerTimeMs << "ms" << std::endl;
|
<< ", 时间戳: " << record.triggerTimeMs << "ms" << std::endl;
|
||||||
|
|
||||||
//记录补招上来的暂态事件,如果需要前置自行下载波形才需要这个接口
|
//记录补招上来的暂态事件
|
||||||
/*append_qvvr_event(id,event.head.name,
|
append_qvvr_event(id,event.head.name,
|
||||||
record.nType,record.fPersisstime,record.fMagntitude,record.triggerTimeMs,record.phase);*/
|
record.nType,record.fPersisstime,record.fMagntitude,record.triggerTimeMs,record.phase);
|
||||||
//直接发走暂态事件
|
//直接发走暂态事件
|
||||||
transfer_json_qvvr_data(id,event.head.name,
|
transfer_json_qvvr_data(id,event.head.name,
|
||||||
record.fMagntitude,record.fPersisstime,record.triggerTimeMs,record.nType,record.phase,"");
|
record.fMagntitude,record.fPersisstime,record.triggerTimeMs,record.nType,record.phase,"");
|
||||||
@@ -2093,9 +2126,6 @@ void process_received_message(string mac, string id,const char* data, size_t len
|
|||||||
recordlist.push_back(record);
|
recordlist.push_back(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//暂时移除CRC校验相关
|
//暂时移除CRC校验相关
|
||||||
//// ========== 新增 CRC 验证 ==========
|
//// ========== 新增 CRC 验证 ==========
|
||||||
//if (!full_data.empty()) {
|
//if (!full_data.empty()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user