add function
This commit is contained in:
@@ -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_FIXEDVALUE;isbusy == 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user