add function

This commit is contained in:
lnk
2025-09-02 14:58:19 +08:00
parent 077c3f45a2
commit 0af8d02bb2
10 changed files with 951 additions and 42 deletions

View File

@@ -3082,15 +3082,451 @@ bool update_qvvr_file_download(const std::string& filename_with_mac, const std::
}
////////////////////////////////////////////////////////////////////////////////////////提取mac
std::string normalize_mac(const std::string& mac) {
std::string result = mac;
result.erase(std::remove(result.begin(), result.end(), '-'), result.end());
return result;
std::string normalize_mac(const std::string &mac) {
std::string res;
res.reserve(mac.size());
for (char c : mac) {
if (c != '-' && c != ':' && c != ' ')
res.push_back(c);
}
return res;
}
////////////////////////////////////////////////////////////////////////////////////////
//找到dev的mac
std::string get_mac_by_devid(const std::string &devid) {
std::lock_guard<std::mutex> lock(ledgermtx);
for (auto &dev : terminal_devlist) {
if (dev.terminal_id == devid) {
return normalize_mac(dev.addr_str); // 规范化后返回
}
}
return {}; // 没找到返回空串
}
////////////////////////////////////////////////////////////////////////////////////////目录信息发送接口函数
bool send_file_list(const std::string &dev_id, const std::vector<tag_dir_info> &FileList) {
// 找到对应 terminal_dev
std::lock_guard<std::mutex> lock(ledgermtx);
auto it = std::find_if(terminal_devlist.begin(), terminal_devlist.end(),
[&](const terminal_dev &dev) { return dev.terminal_id == dev_id; });
if (it == terminal_devlist.end()) {
std::cerr << "[send_file_list] device not found: " << dev_id << std::endl;
return false;
}
terminal_dev &dev = *it;
// 判断 isbusy==1 且 busytype==READING_FILEMENU
if (dev.isbusy != 1 || dev.busytype != static_cast<int>(DeviceState::READING_FILEMENU)) {
std::cerr << "[send_file_list] device not in READING_FILEMENU state." << std::endl;
return false;
}
// 构造 JSON 报文
nlohmann::json j;
j["guid"] = dev.guid;
j["FrontIP"] = FRONT_IP; // 这里填你的前置机 IP
j["Node"] = g_front_seg_index; // 节点号
j["Dev_mac"] = normalize_mac(dev.addr_str); // addr_str 存的是 MAC
// 构造 DirInfo 数组
nlohmann::json dirArray = nlohmann::json::array();
for (const auto &f : FileList) {
nlohmann::json item;
item["Name"] = f.name;
item["Type"] = (f.flag == 0) ? "dir" : "file";
item["Size"] = f.size;
dirArray.push_back(item);
}
// 构造 Detail 部分
nlohmann::json detail;
detail["Type"] = 0x2131; // 读取目录
detail["Msg"] = { {"DirInfo", dirArray} };
detail["Code"] = 200; // 请求成功
// 放到顶层
j["Detail"] = detail;
// 打印调试
std::cout << j.dump(4) << std::endl;
// ---- 入队发送 ----
queue_data_t connect_info;
connect_info.strTopic = Topic_Reply_Topic;
connect_info.strText = j.dump(); // 序列化为字符串
{
std::lock_guard<std::mutex> lock(queue_data_list_mutex);
queue_data_list.push_back(std::move(connect_info));
}
// 调试打印
std::cout << "[send_reply_to_cloud] queued: " << j.dump() << std::endl;
//发送后清除guid和标志
if (dev.isbusy > 0) {
dev.isbusy--;
}
if(dev.isbusy == 0){
dev.guid.clear();
dev.busytype = 0;
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////检查云前置终端的mq业务超时
int get_type_by_state(int state) {
switch (static_cast<DeviceState>(state)) {
case DeviceState::READING_STATS:
case DeviceState::READING_STATS_TIME:
case DeviceState::READING_REALSTAT:
case DeviceState::READING_FIXEDVALUE:
case DeviceState::READING_FIXEDVALUEDES:
case DeviceState::SET_FIXEDVALUE:
case DeviceState::READING_INTERFIXEDVALUE:
case DeviceState::READING_INTERFIXEDVALUEDES:
case DeviceState::READING_CONTROLWORD:
case DeviceState::SET_INTERFIXEDVALUE:
return 0x2106;
case DeviceState::READING_FILEMENU:
return 0x2131;
case DeviceState::READING_EVENTFILE:
case DeviceState::READING_FILEDATA:
return 0x2132;
default:
return 0; // 没有对应的type
}
}
// 定时检查业务超时
void check_device_busy_timeout()
{
std::lock_guard<std::mutex> lock(ledgermtx);
for (auto &dev : terminal_devlist)
{
if (dev.isbusy != 0) // 有业务在进行
{
dev.busytimecount++;
if (dev.busytype == static_cast<int>(DeviceState::READING_FILEDATA)) //下载文件业务
{
if (dev.busytimecount > 30)
{
std::cout << "[Timeout] Device " << dev.terminal_id
<< " busytype=READING_FILEMENU 超时("
<< dev.busytimecount << "s)" << std::endl;
//发送超时响应
send_reply_to_cloud(static_cast<int>(ResponseCode::BAD_REQUEST),dev.terminal_id,get_type_by_state(dev.busytype));
// 超时清空状态
dev.guid.clear(); // 清空进行中的 guid
dev.busytype = 0; // 复位业务类型
dev.isbusy = 0; // 标记空闲
dev.busytimecount = 0; // 计时清零
}
}
else //其他业务
{
if (dev.busytimecount > 10)
{
std::cout << "[Timeout] Device " << dev.terminal_id
<< " busytype=" << dev.busytype
<< " 超时(" << dev.busytimecount << "s)" << std::endl;
// 超时清空状态
dev.guid.clear();
dev.busytype = 0;
dev.isbusy = 0;
dev.busytimecount = 0;
}
}
}
}
}
/////////////////////////////////////////////////////////////////////////////////// 一分钟调用一次:检查 qvvr_file 超时并备份
static bool ensure_dir_exists(const std::string &path)
{
struct stat st;
if (stat(path.c_str(), &st) == 0) {
if (S_ISDIR(st.st_mode)) {
return true; // 已存在
}
return false; // 存在但不是目录
}
// 不存在则创建
if (mkdir(path.c_str(), 0755) == 0) {
return true;
}
return false;
}
void check_and_backup_qvvr_files() {
std::lock_guard<std::mutex> lock(ledgermtx);
const std::string data_dir = FRONT_PATH + "/data";
const std::string backup_dir = data_dir + "/comtrade_bak";
if (!ensure_dir_exists(data_dir) && !ensure_dir_exists(data_dir)) {
std::cerr << "[check_and_backup_qvvr_files] 创建 data 目录失败\n";
return;
}
if (!ensure_dir_exists(backup_dir) && !ensure_dir_exists(backup_dir)) {
std::cerr << "[check_and_backup_qvvr_files] 创建 comtrade_bak 目录失败\n";
return;
}
for (auto &dev : terminal_devlist) {
for (auto &line : dev.line) {
// 用迭代器遍历,允许在循环中 erase 当前元素
for (std::vector<qvvr_file>::iterator qit = line.qvvrevent.qvvrfile.begin();
qit != line.qvvrevent.qvvrfile.end(); /* no ++ here */) {
qvvr_file &qfile = *qit;
if (!qfile.used_status) {
++qit;
continue;
}
++qfile.file_time_count; // 每分钟+1
// 超时阈值:>10 分钟
if (qfile.file_time_count > 10) {
std::cout << "[Qvvr Timeout] dev=" << dev.terminal_id
<< " -> move files to: " << backup_dir << std::endl;
// 移动该记录内的所有文件
for (std::list<std::string>::const_iterator it = qfile.file_download.begin();
it != qfile.file_download.end(); ++it) {
const std::string &src = *it;
const size_t pos = src.find_last_of("/\\");
const std::string base = (pos == std::string::npos) ? src : src.substr(pos + 1);
const std::string dst = backup_dir + "/" + base;
if (rename(src.c_str(), dst.c_str()) != 0) {
std::cerr << " [ERROR] 移动失败: " << src
<< " -> " << dst << " , " << strerror(errno) << std::endl;
} else {
std::cout << " moved: " << src << " -> " << dst << std::endl;
}
}
// ✅ 直接从 qvvrfile 向量中删除这条记录
qit = line.qvvrevent.qvvrfile.erase(qit);
// 注意:不再对 qfile 读写,因为它已被删除
continue; // 不自增,由 erase 返回的新迭代器继续
}
++qit; // 正常前进
}
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////定值信息发送接口函数
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);
// 1. 找到对应 terminal_dev
auto it = std::find_if(terminal_devlist.begin(), terminal_devlist.end(),
[&](const terminal_dev &dev) { return dev.terminal_id == dev_id; });
if (it == terminal_devlist.end()) {
std::cerr << "[send_set_reply] device not found: " << dev_id << std::endl;
return false;
}
terminal_dev &dev = *it;
// 2. 检查状态
if (dev.isbusy != 2 || dev.busytype != static_cast<int>(DeviceState::READING_FIXEDVALUE)) {
std::cerr << "[send_set_reply] device not in READING_FIXEDVALUE state." << std::endl;
return false;
}
// 3. 遍历监测点,找到 logical_device_seq == mp_index 的监测点
bool found = false;
for (auto &mon : dev.line) {
if (std::atoi(mon.logical_device_seq.c_str()) == static_cast<int>(mp_index)) {
// ★ 清理原有的 set_values
mon.set_values.clear();
// ★ 将 fabsf 依次存入该监测点的 set_values
for (const auto &val : fabsf) {
mon.set_values.push_back(val);
}
found = true;
break;
}
}
if (!found) {
std::cerr << "[send_set_reply] monitor with seq=" << (int)mp_index
<< " not found in terminal " << dev_id << std::endl;
return false;
}
// 4. 状态递减
dev.isbusy--;
return true;
}
bool save_internal_value(const std::string &dev_id, const std::vector<float> &fabsf) {
// 找到对应 terminal_dev
std::lock_guard<std::mutex> lock(ledgermtx);
auto it = std::find_if(terminal_devlist.begin(), terminal_devlist.end(),
[&](const terminal_dev &dev) { return dev.terminal_id == dev_id; });
if (it == terminal_devlist.end()) {
std::cerr << "[send_set_reply] device not found: " << dev_id << std::endl;
return false;
}
terminal_dev &dev = *it;
// 判断 isbusy==3 且 busytype==READING_INTERFIXEDVALUE
if (dev.isbusy != 3 || dev.busytype != static_cast<int>(DeviceState::READING_INTERFIXEDVALUE)) {
std::cerr << "[send_set_reply] device not in READING_INTERFIXEDVALUE state." << std::endl;
return false;
}
// ★ 新增:清理原有的内部定值列表
dev.internal_values.clear();
//将值严格按顺序存入list中
for (const auto &val : fabsf) {
dev.internal_values.push_back(val);
}
dev.isbusy--;
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////回复定值读取响应
bool send_set_value_reply(const std::string &dev_id, unsigned char mp_index, const std::vector<DZ_TAB_STRUCT> &dz_info) {
std::lock_guard<std::mutex> lock(ledgermtx);
// 1) 找终端
auto it = std::find_if(terminal_devlist.begin(), terminal_devlist.end(),
[&](const terminal_dev &d) { return d.terminal_id == dev_id; });
if (it == terminal_devlist.end()) {
std::cerr << "[send_set_value_reply] device not found: " << dev_id << std::endl;
return false;
}
terminal_dev &dev = *it;
// 2) 校验状态:发送“定值读取结果”回复,应处于 READING_FIXEDVALUEisbusy == 1
if (dev.isbusy != 1 || dev.busytype != static_cast<int>(DeviceState::READING_FIXEDVALUE)) { //定值读取
std::cerr << "[send_set_value_reply] device not in READING_FIXEDVALUE state." << std::endl;
return false;
}
// 3) 定位监测点mp_index ∈ [1,6]),与 logical_device_seq 比对
ledger_monitor *pMon = nullptr;
for (auto &mon : dev.line) {
if (std::atoi(mon.logical_device_seq.c_str()) == static_cast<int>(mp_index)) {
pMon = &mon;
break;
}
}
if (!pMon) {
std::cerr << "[send_set_value_reply] monitor with seq=" << (int)mp_index
<< " not found in terminal " << dev_id << std::endl;
return false;
}
// 4) 取该监测点的 set_values严格按顺序用于 DZ_Value
std::vector<float> ordered_vals;
ordered_vals.reserve(pMon->set_values.size());
for (float v : pMon->set_values) ordered_vals.push_back(v);
if (ordered_vals.empty()) {
std::cerr << "[send_set_value_reply] monitor seq=" << (int)mp_index
<< " has empty set_values." << std::endl;
return false;
}
// 5) 生成 JSON结构严格贴合你给的样例
nlohmann::json j;
// 顶层
j["guid"] = dev.guid;
j["FrontIP"] = FRONT_IP; // 你的前置机 IP项目已有常量/变量)
j["Node"] = g_front_seg_index; // 节点号(项目已有变量)
j["Dev_mac"] = normalize_mac(dev.addr_str);
// Detail
nlohmann::json detail;
detail["Type"] = 0x2106; // 设备数据
// Msg
nlohmann::json msg;
msg["Cldid"] = mp_index; //测点序号
msg["DataType"] = 0x0C; //定值
// DataArray对象数组逐个填充DZ_Value 严格按 set_values 顺序
nlohmann::json dataArray = nlohmann::json::array();
const size_t n_meta = dz_info.size();
const size_t n_vals = ordered_vals.size();
const size_t n = std::min(n_meta, n_vals); // 以两者较短长度为准
if (n_meta != n_vals) {
std::cerr << "[send_set_value_reply] warn: dz_info size(" << n_meta
<< ") != set_values size(" << n_vals << "), will emit " << n << " items.\n";
return false; // 或者继续发送,视需求而定
}
for (size_t i = 0; i < n; ++i) {
const DZ_TAB_STRUCT &dz = dz_info[i];
nlohmann::json item;
item["LN_Num"] = dz.LN_Num;
item["DZ_Num"] = dz.DZ_Num;
item["DZ_Name"] = dz.DZ_Name;
item["DZ_Value"] = ordered_vals[i]; // ★ 严格按顺序
item["DZ_Type"] = dz.DZ_Type;
item["DZ_Min"] = dz.DZ_Min;
item["DZ_Max"] = dz.DZ_Max;
item["DZ_Default"]= dz.DZ_Default;
item["DZ_UNIT"] = dz.DZ_UNIT;
dataArray.push_back(std::move(item));
}
msg["DataArray"] = std::move(dataArray);
detail["Msg"] = std::move(msg);
detail["Code"] = 200;
j["Detail"] = std::move(detail);
// 6) 入队发送
queue_data_t connect_info;
connect_info.strTopic = Topic_Reply_Topic;
connect_info.strText = j.dump(); // 序列化为字符串
{
std::lock_guard<std::mutex> lk(queue_data_list_mutex);
queue_data_list.push_back(std::move(connect_info));
}
// 调试打印
std::cout << "[send_set_value_reply] queued JSON:\n" << j.dump(4) << std::endl;
// 7) 发送后更新终端状态(按你现有规则)
if (dev.isbusy > 0) {
dev.isbusy--;
}
if (dev.isbusy == 0) {
dev.guid.clear();
dev.busytype = 0;
if (pMon) {
pMon->set_values.clear();//清理本次定值记录
}
}
return true;
}