diff --git a/LFtid1056/cloudfront/code/cfg_parser.cpp b/LFtid1056/cloudfront/code/cfg_parser.cpp index 94475e6..e6a30de 100644 --- a/LFtid1056/cloudfront/code/cfg_parser.cpp +++ b/LFtid1056/cloudfront/code/cfg_parser.cpp @@ -2649,6 +2649,60 @@ void Set_xml_nodeinfo() } } +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////录播波形匹配 +// 工具函数:解析 "dd/MM/yyyy,hh:mm:ss.zzz" 格式的字符串为时间戳(毫秒) +static long long parse_datetime_to_epoch_ms(const std::string& dt_str) { + std::tm tm = {}; + char dummy; + int ms = 0; + + std::istringstream ss(dt_str); + ss >> std::get_time(&tm, "%d/%m/%Y,%H:%M:%S") >> dummy >> ms; + if (ss.fail()) return 0; + + std::time_t time_sec = std::mktime(&tm); + if (time_sec < 0) return 0; + + return static_cast(time_sec) * 1000 + ms; +} + +// 主函数:从 .cfg 文件中提取起始和触发时间戳(毫秒) +bool extract_timestamp_from_cfg_file(const std::string& cfg_path, long long& start_tm, long long& trig_tm) { + struct stat st; + if (stat(cfg_path.c_str(), &st) != 0) { + std::cerr << "File not found: " << cfg_path << "\n"; + return false; + } + + std::ifstream infile(cfg_path); + if (!infile) { + std::cerr << "Cannot open file: " << cfg_path << "\n"; + return false; + } + + std::string line, prev_line, current_line; + while (std::getline(infile, line)) { + // 去除前后空白 + line.erase(line.find_last_not_of(" \t\r\n") + 1); + line.erase(0, line.find_first_not_of(" \t\r\n")); + + std::string upper = line; + std::transform(upper.begin(), upper.end(), upper.begin(), ::toupper); + if (upper == "ASCII" || upper == "BINARY") break; + + prev_line = current_line; + current_line = line; + } + + if (prev_line.length() > 3) prev_line = prev_line.substr(0, prev_line.length() - 3); + if (current_line.length() > 3) current_line = current_line.substr(0, current_line.length() - 3); + + start_tm = parse_datetime_to_epoch_ms(prev_line); + trig_tm = parse_datetime_to_epoch_ms(current_line); + + return start_tm > 0 && trig_tm > 0; +} + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////数据转换函数 // DataArrayItem to_json @@ -2760,4 +2814,117 @@ std::vector GenerateDeviceInfoFromLedger(const std::vector& file_list_raw) { + std::vector file_names; + + // 1. 提取文件名部分 + for (const auto& full_path : file_list_raw) { + size_t pos = full_path.find_last_of("/\\"); + if (pos != std::string::npos && pos + 1 < full_path.size()) { + file_names.push_back(full_path.substr(pos + 1)); + } else { + file_names.push_back(full_path); + } + } + + // 2. 遍历终端 + for (auto& dev : terminal_devlist) { + if (dev.terminal_id == id) { + for (auto& monitor : dev.line) { + try { + ushort monitor_seq = static_cast(std::stoi(monitor.logical_device_seq)); + if (monitor_seq == nCpuNo) { + // 构造 qvvr_file + qvvr_file qfile; + qfile.file_name.assign(file_names.begin(), file_names.end()); + qfile.is_download = false; + qfile.is_pair = false; + qfile.file_time_count = 0; // 可后续补充 + qfile.file_start =false; + + // 添加到唯一的 qvvrevent + monitor.qvvrevent.qvvrfile.push_back(std::move(qfile)); + return true; + } + } catch (...) { + continue; + } + } + } + } + + return false; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////下载成功通知 + +bool update_qvvr_file_download(const std::string& filename_with_mac, const std::string& terminal_id) { + // 去除 mac 路径前缀 + size_t pos = filename_with_mac.find_last_of("/\\"); + std::string filename = (pos != std::string::npos) ? filename_with_mac.substr(pos + 1) : filename_with_mac; + + // 提取逻辑序号(如 PQM1 → 1) + size_t under_pos = filename.find('_'); + if (under_pos == std::string::npos) return false; + + std::string type_part = filename.substr(0, under_pos); // e.g. PQMonitor_PQM1 + size_t num_start = type_part.find_last_not_of("0123456789"); + if (num_start == std::string::npos || num_start + 1 >= type_part.size()) return false; + + std::string seq_str = type_part.substr(num_start + 1); + ushort logical_seq = static_cast(std::stoi(seq_str)); + + for (auto& dev : terminal_devlist) { + if (dev.terminal_id != terminal_id) continue; + + for (auto& monitor : dev.line) { + try { + ushort monitor_seq = static_cast(std::stoi(monitor.logical_device_seq)); + if (monitor_seq != logical_seq) continue; + } catch (...) { + continue; + } + + // 匹配监测点下 qvvrfile 中的 file_name + for (auto& qfile : monitor.qvvrevent.qvvrfile) { + auto it = std::find(qfile.file_name.begin(), qfile.file_name.end(), filename); + if (it != qfile.file_name.end()) { + // 添加到 file_download(去重) + if (std::find(qfile.file_download.begin(), qfile.file_download.end(), filename) == qfile.file_download.end()) { + qfile.file_download.push_back(filename); + } + + qfile.file_time_count = 0; + qfile.file_start = true; + + // 检查 file_download 是否与 file_name 完全一致(集合相同) + std::set s_name(qfile.file_name.begin(), qfile.file_name.end()); + std::set s_down(qfile.file_download.begin(), qfile.file_download.end()); + if (s_name == s_down) { + qfile.is_download = true; + + // 找到其中的 .cfg 文件进行匹配 + for (const auto& f : qfile.file_download) { + if (f.size() >= 4 && f.substr(f.size() - 4) == ".cfg") { + if (compare_qvvr_and_file(f)) {//提取文件时标和监测点事件的时标匹配 + qfile.is_pair = true; + //发送所有文件 + //发送暂态事件 + } + break; // 只处理第一个 cfg 文件 + } + } + } + + return true; + } + } + } + } + + return false; +} diff --git a/LFtid1056/cloudfront/code/interface.h b/LFtid1056/cloudfront/code/interface.h index b94c1f2..e172148 100644 --- a/LFtid1056/cloudfront/code/interface.h +++ b/LFtid1056/cloudfront/code/interface.h @@ -49,6 +49,33 @@ public: std::string VOLTAGE; //补招暂态事件标识 0-不补招;1-补招 }; +//录波文件和暂态事件 +class qvvr_data +{ + int used_status; //是否占用 + int QVVR_type; //暂态类型 + uint64_t QVVR_time; //暂态开始时间 unsigned longlong + double QVVR_PerTime; //暂态持续时间 + double QVVR_Amg; //暂态幅值 + int phase; //相别(仅瞬态上送)0-A 1-B 2-C 3-AB 4-BC 5-CA 其他-ABC/异常 +}; + +class qvvr_file +{ + bool file_start; + int file_time_count; //组内文件下载时间计数(第一个文件下载后十分钟内如果其他文件没下载全或者下载全了没匹配事件则将已下载的文件都移到备份区comtrade_bak) + bool is_download; //文件是否下载完全,最后一个文件下载成功后对比成功则更新这个标志 + bool is_pair; //文件是否和事件匹配,从comtrade/mac/路径下取file_download中的cfg文件提取时间和持续时间来匹配,匹配后接口发送这组file_download全部文件,发送成功后删除这组文件,然后更新事件中的文件列表 + std::list file_name; //文件列表(文件列表上送后就记录) + std::list file_download; //文件已下载列表(每次列表上送会有多个文件,多个文件都下载完全则开始匹配,每次更新都去重并对比file_name) +}; + +class qvvr_event +{ + std::vector qvvrdata; //暂态事件列表 + std::vector qvvrfile; //暂态文件组列表 +}; + //监测点台账 class ledger_monitor { @@ -66,6 +93,8 @@ public: double PT2; // 电压变比2 double CT1; // 电流变比1 double CT2; // 电流变比2 + + qvvr_event qvvrevent; //暂态事件 }; //终端台账 @@ -84,13 +113,13 @@ public: std::string dev_type; std::string dev_key; std::string dev_series; - std::string addr_str; - std::string port; + std::string addr_str; //装置ip + std::string port; //装置端口 std::string timestamp; std::string processNo; std::string maxProcessNum; - std::string mac; // 装置MAC地址 + std::string mac; // 装置MAC地址,接口中从addr_str获取,因为ip和mac放同一位置 std::vector line; }; @@ -425,6 +454,15 @@ std::string generate_json( //构造装置主动上送数据的报文 const std::vector& dataArray //数据数组。 ); +int transfer_json_qvvr_data(unsigned int func_type, int monitor_id, //暂态事件的接口 + double mag, double dur, long long start_tm, long long end_tm, int dis_kind, + const std::string& uuid_cfg, const std::string& uuid_dat, + const std::string& mp_id, const std::string& Qvvr_rptname, const std::string& devtype); + +//录波文件目录接口 + +//录波文件下载完成通知接口 + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #endif