2025-06-20 16:20:59 +08:00
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
#include <ctime>
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
#include <string>
|
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
#include <list>
|
|
|
|
|
|
#include <sstream> //流解析
|
|
|
|
|
|
#include <set> //写去重的设备类型
|
|
|
|
|
|
#include <fstream> //打开文件
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
#include <cctype>
|
|
|
|
|
|
#include <dirent.h>
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
#include <array>
|
|
|
|
|
|
#include <map>
|
|
|
|
|
|
#include <mutex>
|
|
|
|
|
|
#include <thread>
|
|
|
|
|
|
#include <atomic>
|
|
|
|
|
|
#include <cstdio>
|
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
#include <fnmatch.h>
|
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
#include "nlohmann/json.hpp"
|
|
|
|
|
|
#include "curl/curl.h"
|
2025-06-24 17:55:34 +08:00
|
|
|
|
#include "log4.h" //关键上送日志
|
2025-06-20 16:20:59 +08:00
|
|
|
|
#include "interface.h" //台账结构
|
|
|
|
|
|
#include "tinyxml2.h"
|
|
|
|
|
|
#include "rocketmq.h"
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
//进程标识
|
|
|
|
|
|
extern std::string subdir;
|
|
|
|
|
|
extern int g_front_seg_index;
|
|
|
|
|
|
extern int g_front_seg_num;
|
|
|
|
|
|
extern unsigned int g_node_id; //前置程序类型(100-600)
|
|
|
|
|
|
|
|
|
|
|
|
//初始化完成标识
|
|
|
|
|
|
extern int INITFLAG;
|
|
|
|
|
|
|
|
|
|
|
|
//线程阻塞计数
|
|
|
|
|
|
extern uint32_t g_ontime_blocked_times;
|
|
|
|
|
|
|
|
|
|
|
|
//台账锁
|
|
|
|
|
|
extern std::mutex ledgermtx;
|
|
|
|
|
|
|
|
|
|
|
|
//队列
|
|
|
|
|
|
extern std::mutex queue_data_list_mutex; //queue发送数据锁
|
|
|
|
|
|
extern std::list<queue_data_t> queue_data_list; //queue发送数据链表
|
|
|
|
|
|
|
|
|
|
|
|
extern int three_secs_enabled;
|
|
|
|
|
|
|
|
|
|
|
|
extern std::vector<terminal_dev> terminal_devlist;
|
|
|
|
|
|
|
|
|
|
|
|
extern std::map<std::string, Xmldata*> xmlinfo_list;//保存所有型号对应的icd映射文件解析数据
|
|
|
|
|
|
extern XmlConfig xmlcfg;//星形接线xml节点解析的数据-默认映射文件解析数据
|
|
|
|
|
|
extern std::list<CTopic *> topicList; //队列发送主题链表
|
|
|
|
|
|
|
|
|
|
|
|
extern XmlConfig xmlcfg2;//角型接线xml节点解析的数据-默认映射文件解析数据
|
|
|
|
|
|
extern std::list<CTopic*> topicList2; //角型接线发送主题链表
|
|
|
|
|
|
extern std::map<std::string, Xmldata*> xmlinfo_list2;//保存所有型号角形接线对应的icd映射文件解析数据
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
//补招
|
|
|
|
|
|
std::list<JournalRecall> g_StatisticLackList; //日志补招结构类链表
|
|
|
|
|
|
std::mutex g_StatisticLackList_list_mutex; //补招队列数据锁
|
|
|
|
|
|
|
|
|
|
|
|
std::string DEFAULT_CONFIG_FN = "Default_Config.xml"; //默认映射文件
|
|
|
|
|
|
std::string LEDGER_UPDATE_FN = "LedgerUpdate.log"; //台账更新本地记录日志
|
|
|
|
|
|
|
|
|
|
|
|
//一个终端最大检测点数
|
|
|
|
|
|
const int MAX_CPUNO = 10;
|
|
|
|
|
|
|
|
|
|
|
|
//角型接线标志,0不存在角形接线,1存在角形接线
|
|
|
|
|
|
int isdelta_flag = 0;
|
|
|
|
|
|
|
|
|
|
|
|
//多前置flag:1为开启,0为关闭
|
|
|
|
|
|
int MULTIPLE_NODE_FLAG = 1;
|
|
|
|
|
|
|
|
|
|
|
|
//终端台账数量配置
|
|
|
|
|
|
int IED_COUNT = 300; //默认300
|
|
|
|
|
|
|
|
|
|
|
|
//前置标志
|
|
|
|
|
|
std::string FRONT_INST = "";
|
|
|
|
|
|
std::string FRONT_IP = "";
|
|
|
|
|
|
|
|
|
|
|
|
//终端和监测点的状态筛选
|
|
|
|
|
|
std::string TERMINAL_STATUS = "";
|
|
|
|
|
|
std::string MONITOR_STATUS = "";
|
|
|
|
|
|
std::string ICD_FLAG = "";
|
|
|
|
|
|
|
|
|
|
|
|
//web接口
|
|
|
|
|
|
std::string WEB_DEVICE = "";
|
|
|
|
|
|
std::string WEB_ICD = "";
|
|
|
|
|
|
std::string WEB_EVENT = "";
|
|
|
|
|
|
std::string WEB_FILEUPLOAD = "";
|
|
|
|
|
|
std::string WEB_FILEDOWNLOAD = "";
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////mq配置
|
|
|
|
|
|
|
|
|
|
|
|
//备用
|
|
|
|
|
|
std::string BROKER_LIST = "";
|
|
|
|
|
|
|
|
|
|
|
|
//通用主题
|
|
|
|
|
|
std::string TOPIC_STAT = "";
|
|
|
|
|
|
std::string TOPIC_PST = "";
|
|
|
|
|
|
std::string TOPIC_PLT = "";
|
|
|
|
|
|
std::string TOPIC_EVENT = "";
|
|
|
|
|
|
std::string TOPIC_ALARM = "";
|
|
|
|
|
|
std::string TOPIC_SNG = "";
|
|
|
|
|
|
std::string TOPIC_RTDATA = "";
|
|
|
|
|
|
|
|
|
|
|
|
//通用tagkey
|
|
|
|
|
|
std::string G_ROCKETMQ_TAG = "";//tag
|
|
|
|
|
|
std::string G_ROCKETMQ_KEY = "";//key
|
|
|
|
|
|
|
|
|
|
|
|
//生产者
|
|
|
|
|
|
std::string G_ROCKETMQ_PRODUCER = ""; //rocketmq producer
|
|
|
|
|
|
std::string G_MQPRODUCER_IPPORT = ""; //rocketmq ip+port
|
|
|
|
|
|
std::string G_MQPRODUCER_ACCESSKEY = ""; //rocketmq 认证
|
|
|
|
|
|
std::string G_MQPRODUCER_SECRETKEY = ""; //rocketmq 秘钥
|
|
|
|
|
|
|
|
|
|
|
|
//日志
|
|
|
|
|
|
std::string G_LOG_TOPIC = "";//topie
|
|
|
|
|
|
std::string G_LOG_TAG = "";//tag
|
|
|
|
|
|
std::string G_LOG_KEY = "";//key
|
|
|
|
|
|
//终端连接
|
|
|
|
|
|
std::string G_CONNECT_TOPIC = "";//consumer topie
|
|
|
|
|
|
std::string G_CONNECT_TAG = "";//consumer tag
|
|
|
|
|
|
std::string G_CONNECT_KEY = "";//consumer key
|
|
|
|
|
|
//心跳
|
|
|
|
|
|
std::string Heart_Beat_Topic = "";
|
|
|
|
|
|
std::string Heart_Beat_Tag = "";
|
|
|
|
|
|
std::string Heart_Beat_Key = "";
|
|
|
|
|
|
//消息响应
|
|
|
|
|
|
std::string Topic_Reply_Topic = "";
|
|
|
|
|
|
std::string Topic_Reply_Tag = "";
|
|
|
|
|
|
std::string Topic_Reply_Key = "";
|
|
|
|
|
|
|
|
|
|
|
|
//消费者
|
|
|
|
|
|
std::string G_ROCKETMQ_CONSUMER = "";//rocketmq consumer
|
|
|
|
|
|
std::string G_MQCONSUMER_IPPORT = "";//consumer ip+port
|
|
|
|
|
|
std::string G_MQCONSUMER_ACCESSKEY = "";
|
|
|
|
|
|
std::string G_MQCONSUMER_SECRETKEY = "";
|
|
|
|
|
|
std::string G_MQCONSUMER_CHANNEL = "";
|
|
|
|
|
|
//实时数据请求
|
|
|
|
|
|
std::string G_MQCONSUMER_TOPIC_RT = "";//consumer topie
|
|
|
|
|
|
std::string G_MQCONSUMER_TAG_RT = "";//consumer tag
|
|
|
|
|
|
std::string G_MQCONSUMER_KEY_RT = "";//consumer key
|
|
|
|
|
|
//台账更新请求
|
|
|
|
|
|
std::string G_MQCONSUMER_TOPIC_UD = "";//consumer topie
|
|
|
|
|
|
std::string G_MQCONSUMER_TAG_UD = "";//consumer tag
|
|
|
|
|
|
std::string G_MQCONSUMER_KEY_UD = "";//consumer key
|
|
|
|
|
|
//补招数据请求
|
|
|
|
|
|
std::string G_MQCONSUMER_TOPIC_RC = "";//consumer topie
|
|
|
|
|
|
std::string G_MQCONSUMER_TAG_RC = "";//consumer tag
|
|
|
|
|
|
std::string G_MQCONSUMER_KEY_RC = "";//consumer key
|
|
|
|
|
|
//进程控制请求
|
|
|
|
|
|
std::string G_MQCONSUMER_TOPIC_SET = "";//consumer topie
|
|
|
|
|
|
std::string G_MQCONSUMER_TAG_SET = "";//consumer tag
|
|
|
|
|
|
std::string G_MQCONSUMER_KEY_SET = "";//consumer key
|
|
|
|
|
|
//日志数据请求
|
|
|
|
|
|
std::string G_MQCONSUMER_TOPIC_LOG = "";//consumer topie
|
|
|
|
|
|
std::string G_MQCONSUMER_TAG_LOG = "";//consumer tag
|
|
|
|
|
|
std::string G_MQCONSUMER_KEY_LOG = "";//consumer key
|
|
|
|
|
|
|
|
|
|
|
|
//测试用的主题
|
|
|
|
|
|
std::string G_ROCKETMQ_TOPIC_TEST = "";//topie
|
|
|
|
|
|
std::string G_ROCKETMQ_TAG_TEST = "";//tag
|
|
|
|
|
|
std::string G_ROCKETMQ_KEY_TEST = "";//key
|
|
|
|
|
|
|
|
|
|
|
|
//测试相关配置
|
|
|
|
|
|
int G_TEST_FLAG = 0;
|
|
|
|
|
|
int G_TEST_NUM = 0;
|
|
|
|
|
|
int G_TEST_TYPE = 0;
|
|
|
|
|
|
int TEST_PORT = 11000; //用于当前进程登录测试shell的端口
|
|
|
|
|
|
std::string G_TEST_LIST = ""; //测试用的发送实际数据的终端列表
|
|
|
|
|
|
std::vector<std::string> TESTARRAY; //解析的列表数组
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////其他文件定义的函数引用声明
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////当前文件函数声明
|
|
|
|
|
|
|
|
|
|
|
|
void create_ledger_log(trigger_update_xml_t* ledger_update_xml);
|
|
|
|
|
|
void print_trigger_update_xml(const trigger_update_xml_t& trigger_update);
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////辅助函数
|
|
|
|
|
|
|
|
|
|
|
|
//去除字符串前后空格
|
|
|
|
|
|
static void trim(std::string& s) {
|
|
|
|
|
|
auto not_space = [](int ch) { return !std::isspace(ch); };
|
|
|
|
|
|
s.erase(s.begin(), std::find_if(s.begin(), s.end(), not_space));
|
|
|
|
|
|
s.erase(std::find_if(s.rbegin(), s.rend(), not_space).base(), s.end());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//判断空格
|
|
|
|
|
|
bool is_blank(const std::string& str)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!std::isspace(*it)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////配置文件读取
|
|
|
|
|
|
|
|
|
|
|
|
//将 G_TEST_LIST 按逗号分割,填充 TESTARRAY
|
|
|
|
|
|
static void parseTestList() {
|
|
|
|
|
|
TESTARRAY.clear();
|
|
|
|
|
|
std::istringstream iss(G_TEST_LIST);
|
|
|
|
|
|
std::string token;
|
|
|
|
|
|
while (std::getline(iss, token, ',')) {
|
|
|
|
|
|
trim(token);
|
|
|
|
|
|
if (!token.empty()) {
|
|
|
|
|
|
TESTARRAY.push_back(token);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//简易映射方式解析配置文件文件
|
|
|
|
|
|
void loadConfig(const std::string& filename) {
|
|
|
|
|
|
// 1. 构造“节.键 → 变量地址”映射表
|
|
|
|
|
|
std::unordered_map<std::string, std::string*> strMap;
|
|
|
|
|
|
std::unordered_map<std::string, int*> intMap;
|
|
|
|
|
|
|
|
|
|
|
|
// [Flag]
|
|
|
|
|
|
strMap["Flag.FrontInst"] = &FRONT_INST;
|
|
|
|
|
|
strMap["Flag.FrontIP"] = &FRONT_IP;
|
|
|
|
|
|
|
|
|
|
|
|
// [Ledger]
|
|
|
|
|
|
intMap["Ledger.IedCount"] = &IED_COUNT;
|
|
|
|
|
|
strMap["Ledger.TerminalStatus"]= &TERMINAL_STATUS;
|
|
|
|
|
|
strMap["Ledger.MonitorStatus"] = &MONITOR_STATUS;
|
|
|
|
|
|
strMap["Ledger.IcdFlag"] = &ICD_FLAG;
|
|
|
|
|
|
|
|
|
|
|
|
// [Http]
|
|
|
|
|
|
strMap["Http.WebDevice"] = &WEB_DEVICE;
|
|
|
|
|
|
strMap["Http.WebIcd"] = &WEB_ICD;
|
|
|
|
|
|
strMap["Http.WebEvent"] = &WEB_EVENT;
|
|
|
|
|
|
strMap["Http.WebFileupload"] = &WEB_FILEUPLOAD;
|
|
|
|
|
|
strMap["Http.WebFiledownload"]= &WEB_FILEDOWNLOAD;
|
|
|
|
|
|
|
|
|
|
|
|
// [Queue]
|
|
|
|
|
|
strMap["Queue.BrokerList"] = &BROKER_LIST;
|
|
|
|
|
|
strMap["Queue.RTDataTopic"] = &TOPIC_RTDATA;
|
|
|
|
|
|
strMap["Queue.HisTopic"] = &TOPIC_STAT;
|
|
|
|
|
|
strMap["Queue.PSTTopic"] = &TOPIC_PST;
|
|
|
|
|
|
strMap["Queue.PLTTopic"] = &TOPIC_PLT;
|
|
|
|
|
|
strMap["Queue.EventTopic"] = &TOPIC_EVENT;
|
|
|
|
|
|
strMap["Queue.AlmTopic"] = &TOPIC_ALARM;
|
|
|
|
|
|
strMap["Queue.SngTopic"] = &TOPIC_SNG;
|
|
|
|
|
|
strMap["Queue.QUEUE_TAG"] = &G_ROCKETMQ_TAG;
|
|
|
|
|
|
strMap["Queue.QUEUE_KEY"] = &G_ROCKETMQ_KEY;
|
|
|
|
|
|
|
|
|
|
|
|
// [RocketMq] —— 生产者
|
|
|
|
|
|
strMap["RocketMq.producer"] = &G_ROCKETMQ_PRODUCER;
|
|
|
|
|
|
strMap["RocketMq.Ipport"] = &G_MQPRODUCER_IPPORT;
|
|
|
|
|
|
strMap["RocketMq.AccessKey"] = &G_MQPRODUCER_ACCESSKEY;
|
|
|
|
|
|
strMap["RocketMq.SecretKey"] = &G_MQPRODUCER_SECRETKEY;
|
|
|
|
|
|
|
|
|
|
|
|
strMap["RocketMq.LOGTopic"] = &G_LOG_TOPIC;
|
|
|
|
|
|
strMap["RocketMq.LOGTag"] = &G_LOG_TAG;
|
|
|
|
|
|
strMap["RocketMq.LOGKey"] = &G_LOG_KEY;
|
|
|
|
|
|
|
|
|
|
|
|
strMap["RocketMq.CONNECTTopic"] = &G_CONNECT_TOPIC;
|
|
|
|
|
|
strMap["RocketMq.CONNECTTag"] = &G_CONNECT_TAG;
|
|
|
|
|
|
strMap["RocketMq.CONNECTKey"] = &G_CONNECT_KEY;
|
|
|
|
|
|
|
|
|
|
|
|
strMap["RocketMq.Heart_Beat_Topic"] = &Heart_Beat_Topic;
|
|
|
|
|
|
strMap["RocketMq.Heart_Beat_Tag"] = &Heart_Beat_Tag;
|
|
|
|
|
|
strMap["RocketMq.Heart_Beat_Key"] = &Heart_Beat_Key;
|
|
|
|
|
|
|
|
|
|
|
|
strMap["RocketMq.Topic_Reply_Topic"] = &Topic_Reply_Topic;
|
|
|
|
|
|
strMap["RocketMq.Topic_Reply_Tag"] = &Topic_Reply_Tag;
|
|
|
|
|
|
strMap["RocketMq.Topic_Reply_Key"] = &Topic_Reply_Key;
|
|
|
|
|
|
|
|
|
|
|
|
// [RocketMq] —— 消费者
|
|
|
|
|
|
strMap["RocketMq.consumer"] = &G_ROCKETMQ_CONSUMER;
|
|
|
|
|
|
strMap["RocketMq.ConsumerIpport"] = &G_MQCONSUMER_IPPORT;
|
|
|
|
|
|
strMap["RocketMq.ConsumerAccessKey"] = &G_MQCONSUMER_ACCESSKEY;
|
|
|
|
|
|
strMap["RocketMq.ConsumerSecretKey"] = &G_MQCONSUMER_SECRETKEY;
|
|
|
|
|
|
strMap["RocketMq.ConsumerChannel"] = &G_MQCONSUMER_CHANNEL;
|
|
|
|
|
|
|
|
|
|
|
|
strMap["RocketMq.ConsumerTopicRT"] = &G_MQCONSUMER_TOPIC_RT;
|
|
|
|
|
|
strMap["RocketMq.ConsumerTagRT"] = &G_MQCONSUMER_TAG_RT;
|
|
|
|
|
|
strMap["RocketMq.ConsumerKeyRT"] = &G_MQCONSUMER_KEY_RT;
|
|
|
|
|
|
|
|
|
|
|
|
strMap["RocketMq.ConsumerTopicUD"] = &G_MQCONSUMER_TOPIC_UD;
|
|
|
|
|
|
strMap["RocketMq.ConsumerTagUD"] = &G_MQCONSUMER_TAG_UD;
|
|
|
|
|
|
strMap["RocketMq.ConsumerKeyUD"] = &G_MQCONSUMER_KEY_UD;
|
|
|
|
|
|
|
|
|
|
|
|
strMap["RocketMq.ConsumerTopicRC"] = &G_MQCONSUMER_TOPIC_RC;
|
|
|
|
|
|
strMap["RocketMq.ConsumerTagRC"] = &G_MQCONSUMER_TAG_RC;
|
|
|
|
|
|
strMap["RocketMq.ConsumerKeyRC"] = &G_MQCONSUMER_KEY_RC;
|
|
|
|
|
|
|
|
|
|
|
|
strMap["RocketMq.ConsumerTopicSET"] = &G_MQCONSUMER_TOPIC_SET;
|
|
|
|
|
|
strMap["RocketMq.ConsumerTagSET"] = &G_MQCONSUMER_TAG_SET;
|
|
|
|
|
|
strMap["RocketMq.ConsumerKeySET"] = &G_MQCONSUMER_KEY_SET;
|
|
|
|
|
|
|
|
|
|
|
|
strMap["RocketMq.ConsumerTopicLOG"] = &G_MQCONSUMER_TOPIC_LOG;
|
|
|
|
|
|
strMap["RocketMq.ConsumerTagLOG"] = &G_MQCONSUMER_TAG_LOG;
|
|
|
|
|
|
strMap["RocketMq.ConsumerKeyLOG"] = &G_MQCONSUMER_KEY_LOG;
|
|
|
|
|
|
|
|
|
|
|
|
strMap["RocketMq.Topic_Test"] = &G_ROCKETMQ_TOPIC_TEST;
|
|
|
|
|
|
strMap["RocketMq.Tag_Test"] = &G_ROCKETMQ_TAG_TEST;
|
|
|
|
|
|
strMap["RocketMq.Key_Test"] = &G_ROCKETMQ_KEY_TEST;
|
|
|
|
|
|
|
|
|
|
|
|
intMap["RocketMq.Testflag"] = &G_TEST_FLAG;
|
|
|
|
|
|
intMap["RocketMq.Testnum"] = &G_TEST_NUM;
|
|
|
|
|
|
intMap["RocketMq.Testtype"] = &G_TEST_TYPE;
|
|
|
|
|
|
intMap["RocketMq.TestPort"] = &TEST_PORT;
|
|
|
|
|
|
strMap["RocketMq.TestList"] = &G_TEST_LIST;
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 打开并逐行解析 INI 文件
|
|
|
|
|
|
std::ifstream fin(filename);
|
|
|
|
|
|
if (!fin.is_open()) {
|
|
|
|
|
|
std::cerr << "无法打开配置文件: " << filename << "\n";
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::string line;
|
|
|
|
|
|
std::string currentSection;
|
|
|
|
|
|
while (std::getline(fin, line)) {
|
|
|
|
|
|
trim(line);
|
|
|
|
|
|
if (line.empty() || line[0] == ';' || line[0] == '#') {
|
|
|
|
|
|
continue; // 跳过空白或注释
|
|
|
|
|
|
}
|
|
|
|
|
|
if (line.front() == '[' && line.back() == ']') {
|
|
|
|
|
|
currentSection = line.substr(1, line.size() - 2);
|
|
|
|
|
|
trim(currentSection);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
auto pos = line.find('=');
|
|
|
|
|
|
if (pos == std::string::npos) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
std::string key = line.substr(0, pos);
|
|
|
|
|
|
std::string value = line.substr(pos + 1);
|
|
|
|
|
|
trim(key);
|
|
|
|
|
|
trim(value);
|
|
|
|
|
|
|
|
|
|
|
|
// 去掉值两端双引号
|
|
|
|
|
|
if (value.size() >= 2 && value.front() == '"' && value.back() == '"') {
|
|
|
|
|
|
value = value.substr(1, value.size() - 2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 拼出 "节.键"
|
|
|
|
|
|
std::string fullKey = currentSection + "." + key;
|
|
|
|
|
|
|
|
|
|
|
|
// 如果在字符串映射表里,就写入对应的全局 std::string
|
|
|
|
|
|
auto sit = strMap.find(fullKey);
|
|
|
|
|
|
if (sit != strMap.end()) {
|
|
|
|
|
|
*(sit->second) = value;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 如果在整型映射表里,就 stoi 后写入对应的全局 int
|
|
|
|
|
|
auto iit = intMap.find(fullKey);
|
|
|
|
|
|
if (iit != intMap.end()) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
*(iit->second) = std::stoi(value);
|
|
|
|
|
|
} catch (...) {
|
|
|
|
|
|
*(iit->second) = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
fin.close();
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 将 G_TEST_LIST 拆分到 TESTARRAY
|
|
|
|
|
|
parseTestList();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//打印所有全局变量,名称对齐
|
|
|
|
|
|
void printConfig() {
|
|
|
|
|
|
const int nameWidth = 30; // 变量名区域宽度
|
|
|
|
|
|
std::cout << "------- Loaded Configuration -------\n";
|
|
|
|
|
|
|
|
|
|
|
|
// 辅助 lambda 方便打印
|
|
|
|
|
|
auto printStr = [&](const std::string& name, const std::string& val) {
|
|
|
|
|
|
std::cout << std::left << std::setw(nameWidth) << name
|
|
|
|
|
|
<< " = " << val << "\n";
|
|
|
|
|
|
};
|
|
|
|
|
|
auto printInt = [&](const std::string& name, int val) {
|
|
|
|
|
|
std::cout << std::left << std::setw(nameWidth) << name
|
|
|
|
|
|
<< " = " << val << "\n";
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "\n// 前置区分 —— 通用\n";
|
|
|
|
|
|
printInt("IED_COUNT", IED_COUNT);
|
|
|
|
|
|
printStr("FRONT_INST", FRONT_INST);
|
|
|
|
|
|
printStr("FRONT_IP", FRONT_IP);
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "\n// 消息队列 —— 通用\n";
|
|
|
|
|
|
printStr("BROKER_LIST", BROKER_LIST);
|
|
|
|
|
|
printStr("TOPIC_STAT", TOPIC_STAT);
|
|
|
|
|
|
printStr("TOPIC_PST", TOPIC_PST);
|
|
|
|
|
|
printStr("TOPIC_PLT", TOPIC_PLT);
|
|
|
|
|
|
printStr("TOPIC_EVENT", TOPIC_EVENT);
|
|
|
|
|
|
printStr("TOPIC_ALARM", TOPIC_ALARM);
|
|
|
|
|
|
printStr("TOPIC_SNG", TOPIC_SNG);
|
|
|
|
|
|
printStr("TOPIC_RTDATA", TOPIC_RTDATA);
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "\n// MQ —— 生产者\n";
|
|
|
|
|
|
printStr("G_ROCKETMQ_PRODUCER", G_ROCKETMQ_PRODUCER);
|
|
|
|
|
|
printStr("G_MQPRODUCER_IPPORT", G_MQPRODUCER_IPPORT);
|
|
|
|
|
|
printStr("G_MQPRODUCER_ACCESSKEY", G_MQPRODUCER_ACCESSKEY);
|
|
|
|
|
|
printStr("G_MQPRODUCER_SECRETKEY", G_MQPRODUCER_SECRETKEY);
|
|
|
|
|
|
|
|
|
|
|
|
printStr("G_LOG_TOPIC", G_LOG_TOPIC);
|
|
|
|
|
|
printStr("G_LOG_TAG", G_LOG_TAG);
|
|
|
|
|
|
printStr("G_LOG_KEY", G_LOG_KEY);
|
|
|
|
|
|
|
|
|
|
|
|
printStr("G_CONNECT_TOPIC", G_CONNECT_TOPIC);
|
|
|
|
|
|
printStr("G_CONNECT_TAG", G_CONNECT_TAG);
|
|
|
|
|
|
printStr("G_CONNECT_KEY", G_CONNECT_KEY);
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "\n// MQ —— 心跳 & 响应\n";
|
|
|
|
|
|
printStr("Heart_Beat_Topic", Heart_Beat_Topic);
|
|
|
|
|
|
printStr("Heart_Beat_Tag", Heart_Beat_Tag);
|
|
|
|
|
|
printStr("Heart_Beat_Key", Heart_Beat_Key);
|
|
|
|
|
|
|
|
|
|
|
|
printStr("Topic_Reply_Topic", Topic_Reply_Topic);
|
|
|
|
|
|
printStr("Topic_Reply_Tag", Topic_Reply_Tag);
|
|
|
|
|
|
printStr("Topic_Reply_Key", Topic_Reply_Key);
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "\n// MQ —— 消费者\n";
|
|
|
|
|
|
printStr("G_ROCKETMQ_CONSUMER", G_ROCKETMQ_CONSUMER);
|
|
|
|
|
|
printStr("G_MQCONSUMER_IPPORT", G_MQCONSUMER_IPPORT);
|
|
|
|
|
|
printStr("G_MQCONSUMER_ACCESSKEY", G_MQCONSUMER_ACCESSKEY);
|
|
|
|
|
|
printStr("G_MQCONSUMER_SECRETKEY", G_MQCONSUMER_SECRETKEY);
|
|
|
|
|
|
printStr("G_MQCONSUMER_CHANNEL", G_MQCONSUMER_CHANNEL);
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "\n// MQ —— 主题细分类\n";
|
|
|
|
|
|
printStr("G_MQCONSUMER_TOPIC_RT", G_MQCONSUMER_TOPIC_RT);
|
|
|
|
|
|
printStr("G_MQCONSUMER_TAG_RT", G_MQCONSUMER_TAG_RT);
|
|
|
|
|
|
printStr("G_MQCONSUMER_KEY_RT", G_MQCONSUMER_KEY_RT);
|
|
|
|
|
|
|
|
|
|
|
|
printStr("G_MQCONSUMER_TOPIC_UD", G_MQCONSUMER_TOPIC_UD);
|
|
|
|
|
|
printStr("G_MQCONSUMER_TAG_UD", G_MQCONSUMER_TAG_UD);
|
|
|
|
|
|
printStr("G_MQCONSUMER_KEY_UD", G_MQCONSUMER_KEY_UD);
|
|
|
|
|
|
|
|
|
|
|
|
printStr("G_MQCONSUMER_TOPIC_RC", G_MQCONSUMER_TOPIC_RC);
|
|
|
|
|
|
printStr("G_MQCONSUMER_TAG_RC", G_MQCONSUMER_TAG_RC);
|
|
|
|
|
|
printStr("G_MQCONSUMER_KEY_RC", G_MQCONSUMER_KEY_RC);
|
|
|
|
|
|
|
|
|
|
|
|
printStr("G_MQCONSUMER_TOPIC_SET", G_MQCONSUMER_TOPIC_SET);
|
|
|
|
|
|
printStr("G_MQCONSUMER_TAG_SET", G_MQCONSUMER_TAG_SET);
|
|
|
|
|
|
printStr("G_MQCONSUMER_KEY_SET", G_MQCONSUMER_KEY_SET);
|
|
|
|
|
|
|
|
|
|
|
|
printStr("G_MQCONSUMER_TOPIC_LOG", G_MQCONSUMER_TOPIC_LOG);
|
|
|
|
|
|
printStr("G_MQCONSUMER_TAG_LOG", G_MQCONSUMER_TAG_LOG);
|
|
|
|
|
|
printStr("G_MQCONSUMER_KEY_LOG", G_MQCONSUMER_KEY_LOG);
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "\n// MQ —— 测试用主题 & 参数\n";
|
|
|
|
|
|
printStr("G_ROCKETMQ_TOPIC_TEST", G_ROCKETMQ_TOPIC_TEST);
|
|
|
|
|
|
printStr("G_ROCKETMQ_TAG_TEST", G_ROCKETMQ_TAG_TEST);
|
|
|
|
|
|
printStr("G_ROCKETMQ_KEY_TEST", G_ROCKETMQ_KEY_TEST);
|
|
|
|
|
|
|
|
|
|
|
|
printInt("G_TEST_FLAG", G_TEST_FLAG);
|
|
|
|
|
|
printInt("G_TEST_NUM", G_TEST_NUM);
|
|
|
|
|
|
printInt("G_TEST_TYPE", G_TEST_TYPE);
|
|
|
|
|
|
printInt("TEST_PORT", TEST_PORT);
|
|
|
|
|
|
printStr("G_TEST_LIST", G_TEST_LIST);
|
|
|
|
|
|
|
|
|
|
|
|
// 打印解析后的 TESTARRAY
|
|
|
|
|
|
std::cout << std::left << std::setw(nameWidth) << "TESTARRAY" << " = [";
|
|
|
|
|
|
for (size_t i = 0; i < TESTARRAY.size(); ++i) {
|
|
|
|
|
|
std::cout << TESTARRAY[i];
|
|
|
|
|
|
if (i + 1 < TESTARRAY.size()) {
|
|
|
|
|
|
std::cout << ", ";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
std::cout << "]\n";
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "\n// 终端 & 监测点状态筛选\n";
|
|
|
|
|
|
printStr("TERMINAL_STATUS", TERMINAL_STATUS);
|
|
|
|
|
|
printStr("MONITOR_STATUS", MONITOR_STATUS);
|
|
|
|
|
|
printStr("ICD_FLAG", ICD_FLAG);
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "\n// Web 接口\n";
|
|
|
|
|
|
printStr("WEB_DEVICE", WEB_DEVICE);
|
|
|
|
|
|
printStr("WEB_ICD", WEB_ICD);
|
|
|
|
|
|
printStr("WEB_EVENT", WEB_EVENT);
|
|
|
|
|
|
printStr("WEB_FILEUPLOAD", WEB_FILEUPLOAD);
|
|
|
|
|
|
printStr("WEB_FILEDOWNLOAD", WEB_FILEDOWNLOAD);
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "-------------------------------------\n";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//初始化配置
|
|
|
|
|
|
void init_config() {
|
|
|
|
|
|
|
|
|
|
|
|
loadConfig(FRONT_PATH + "/config/front.cfg");
|
|
|
|
|
|
printConfig();
|
|
|
|
|
|
|
|
|
|
|
|
//多前置处理
|
|
|
|
|
|
if (g_front_seg_index > 0 && g_front_seg_num > 0) {
|
|
|
|
|
|
MULTIPLE_NODE_FLAG = 1;
|
|
|
|
|
|
std::cout << "this is multiple process of index:" << g_front_seg_index << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
if(g_front_seg_index > g_front_seg_num){
|
|
|
|
|
|
DIY_ERRORLOG("process","【ERROR】前置当前进程的进程号为:%d,前置的多进程最大进程号为:%d,当前进程的进程号应该为1到最大进程号范围内的整数,退出该进程",g_front_seg_index,g_front_seg_num);
|
|
|
|
|
|
exit(-1039);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
else if(g_front_seg_num == 0 && g_front_seg_index == 0){
|
|
|
|
|
|
MULTIPLE_NODE_FLAG = 0;
|
|
|
|
|
|
std::cout << "this is single process" << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
else{
|
|
|
|
|
|
DIY_ERRORLOG("process","【ERROR】前置当前进程的进程号为:%d,前置的多进程最大进程号为:%d,应该为大于0的整数,退出该进程",g_front_seg_index,g_front_seg_num);
|
|
|
|
|
|
exit(-1039);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//测试进程端口
|
|
|
|
|
|
if (g_node_id == STAT_DATA_BASE_NODE_ID)//统计采集
|
|
|
|
|
|
TEST_PORT = TEST_PORT + STAT_DATA_BASE_NODE_ID + g_front_seg_index;
|
|
|
|
|
|
else if (g_node_id == RECALL_HIS_DATA_BASE_NODE_ID) {//补召
|
|
|
|
|
|
TEST_PORT = TEST_PORT + RECALL_HIS_DATA_BASE_NODE_ID + g_front_seg_index;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (g_node_id == THREE_SECS_DATA_BASE_NODE_ID) {//3秒采集
|
|
|
|
|
|
TEST_PORT = TEST_PORT + THREE_SECS_DATA_BASE_NODE_ID + g_front_seg_index;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (g_node_id == SOE_COMTRADE_BASE_NODE_ID) {//暂态录波
|
|
|
|
|
|
TEST_PORT = TEST_PORT + SOE_COMTRADE_BASE_NODE_ID + g_front_seg_index;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////获取当前时间
|
|
|
|
|
|
|
|
|
|
|
|
// 用于获取当前时间,单位毫秒
|
|
|
|
|
|
double sGetMsTime() {
|
|
|
|
|
|
auto now = std::chrono::system_clock::now();
|
|
|
|
|
|
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
|
|
|
now.time_since_epoch()
|
|
|
|
|
|
).count();
|
|
|
|
|
|
return static_cast<double>(ms);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////实时触发部分
|
|
|
|
|
|
|
|
|
|
|
|
//获取实时触发文件
|
|
|
|
|
|
std::string get_3s_trig_fn() {
|
|
|
|
|
|
const std::string dirPath = FRONT_PATH + "/etc/trigger3s";
|
|
|
|
|
|
DIR* dp = opendir(dirPath.c_str());
|
|
|
|
|
|
if (!dp) return "";
|
|
|
|
|
|
|
|
|
|
|
|
struct dirent* entry;
|
|
|
|
|
|
std::vector<std::pair<std::string, time_t>> xmlFiles;
|
|
|
|
|
|
|
|
|
|
|
|
while ((entry = readdir(dp)) != nullptr) {
|
|
|
|
|
|
if (strstr(entry->d_name, ".xml")) {
|
|
|
|
|
|
std::string fullPath = dirPath + "/" + entry->d_name;
|
|
|
|
|
|
struct stat st;
|
|
|
|
|
|
if (stat(fullPath.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
|
|
|
|
|
|
xmlFiles.emplace_back(entry->d_name, st.st_mtime);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
closedir(dp);
|
|
|
|
|
|
|
|
|
|
|
|
if (xmlFiles.empty()) return "";
|
|
|
|
|
|
|
|
|
|
|
|
std::sort(xmlFiles.begin(), xmlFiles.end(),
|
|
|
|
|
|
[](const std::pair<std::string, time_t>& a, const std::pair<std::string, time_t>& b) {
|
|
|
|
|
|
return a.second > b.second; // 最近时间在前
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return xmlFiles.front().first;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 打印 trigger_t 结构体的函数
|
|
|
|
|
|
void print_trigger(const trigger_t& trigger) {
|
|
|
|
|
|
std::cout << " dev_idx: " << trigger.dev_idx
|
|
|
|
|
|
<< ", line_id: " << trigger.line_id
|
|
|
|
|
|
<< ", real_data: " << trigger.real_data
|
|
|
|
|
|
<< ", soe_data: " << trigger.soe_data
|
|
|
|
|
|
<< ", limit: " << trigger.limit
|
|
|
|
|
|
<< ", count: " << trigger.count
|
|
|
|
|
|
<< std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 打印 trigger_3s_xml_t 结构体的函数
|
|
|
|
|
|
void print_trigger_3s_xml(const trigger_3s_xml_t& trigger_3s_xml) {
|
|
|
|
|
|
std::cout << "Work Trigger Count: " << trigger_3s_xml.work_trigger_num << std::endl;
|
|
|
|
|
|
for (int i = 0; i < trigger_3s_xml.work_trigger_num; ++i) {
|
|
|
|
|
|
std::cout << " Work Trigger [" << (i + 1) << "]:" << std::endl;
|
|
|
|
|
|
print_trigger(trigger_3s_xml.work_triggers[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "New Trigger Count: " << trigger_3s_xml.new_trigger_num << std::endl;
|
|
|
|
|
|
for (int i = 0; i < trigger_3s_xml.new_trigger_num; ++i) {
|
|
|
|
|
|
std::cout << " New Trigger [" << (i + 1) << "]:" << std::endl;
|
|
|
|
|
|
print_trigger(trigger_3s_xml.new_triggers[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "Delete Trigger Count: " << trigger_3s_xml.delete_trigger_num << std::endl;
|
|
|
|
|
|
for (int i = 0; i < trigger_3s_xml.delete_trigger_num; ++i) {
|
|
|
|
|
|
std::cout << " Delete Trigger [" << (i + 1) << "]:" << std::endl;
|
|
|
|
|
|
print_trigger(trigger_3s_xml.delete_triggers[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "Modify Trigger Count: " << trigger_3s_xml.modify_trigger_num << std::endl;
|
|
|
|
|
|
for (int i = 0; i < trigger_3s_xml.modify_trigger_num; ++i) {
|
|
|
|
|
|
std::cout << " Modify Trigger [" << (i + 1) << "]:" << std::endl;
|
|
|
|
|
|
print_trigger(trigger_3s_xml.modify_triggers[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//处理触发标志
|
|
|
|
|
|
int getValueFromElemAttrStr(std::string str)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (str == "true")
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
else if (str == "false")
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
else
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//将文件内容读取到结构中
|
|
|
|
|
|
void parse_3s_trigger(trigger_3s_xml_t* trigger_3s_xml, const std::string& parentTag, tinyxml2::XMLElement* trigger_e)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!trigger_3s_xml || !trigger_e) return;
|
|
|
|
|
|
|
|
|
|
|
|
trigger_t trigger;
|
|
|
|
|
|
|
|
|
|
|
|
const char* attr = nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
// DevSeries
|
|
|
|
|
|
attr = trigger_e->Attribute("DevSeries");
|
|
|
|
|
|
trigger.dev_idx = attr ? std::atoi(attr) : 0;
|
|
|
|
|
|
|
|
|
|
|
|
// Line
|
|
|
|
|
|
attr = trigger_e->Attribute("Line");
|
|
|
|
|
|
trigger.line_id = attr ? std::atoi(attr) : 0;
|
|
|
|
|
|
|
|
|
|
|
|
// RealData
|
|
|
|
|
|
attr = trigger_e->Attribute("RealData");
|
|
|
|
|
|
std::string realDataStr = attr ? attr : "";
|
|
|
|
|
|
std::transform(realDataStr.begin(), realDataStr.end(), realDataStr.begin(), ::tolower);
|
|
|
|
|
|
trigger.real_data = getValueFromElemAttrStr(realDataStr);
|
|
|
|
|
|
|
|
|
|
|
|
// SOEData
|
|
|
|
|
|
attr = trigger_e->Attribute("SOEData");
|
|
|
|
|
|
std::string soeDataStr = attr ? attr : "";
|
|
|
|
|
|
std::transform(soeDataStr.begin(), soeDataStr.end(), soeDataStr.begin(), ::tolower);
|
|
|
|
|
|
trigger.soe_data = getValueFromElemAttrStr(soeDataStr);
|
|
|
|
|
|
|
|
|
|
|
|
// Limit
|
|
|
|
|
|
attr = trigger_e->Attribute("Limit");
|
|
|
|
|
|
trigger.limit = attr ? std::atoi(attr) : 0;
|
|
|
|
|
|
|
|
|
|
|
|
// Count
|
|
|
|
|
|
attr = trigger_e->Attribute("Count");
|
|
|
|
|
|
trigger.count = attr ? std::atoi(attr) : 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 分类插入
|
|
|
|
|
|
if (parentTag == "Work") {
|
|
|
|
|
|
trigger_3s_xml->work_triggers[trigger_3s_xml->work_trigger_num++] = trigger;
|
|
|
|
|
|
} else if (parentTag == "New") {
|
|
|
|
|
|
trigger_3s_xml->new_triggers[trigger_3s_xml->new_trigger_num++] = trigger;
|
|
|
|
|
|
} else if (parentTag == "Delete") {
|
|
|
|
|
|
trigger_3s_xml->delete_triggers[trigger_3s_xml->delete_trigger_num++] = trigger;
|
|
|
|
|
|
} else if (parentTag == "Modify") {
|
|
|
|
|
|
trigger_3s_xml->modify_triggers[trigger_3s_xml->modify_trigger_num++] = trigger;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 调试用
|
|
|
|
|
|
print_trigger_3s_xml(*trigger_3s_xml);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//实时触发文件处理
|
|
|
|
|
|
int load_3s_data_from_xml(trigger_3s_xml_t* trigger_3s_xml, const std::string& xml_fn) {
|
|
|
|
|
|
if (!trigger_3s_xml) return 1;
|
|
|
|
|
|
|
|
|
|
|
|
tinyxml2::XMLDocument doc;
|
|
|
|
|
|
tinyxml2::XMLError result = doc.LoadFile(xml_fn.c_str());
|
|
|
|
|
|
if (result == tinyxml2::XML_ERROR_FILE_NOT_FOUND) {
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
} else if (result != tinyxml2::XML_SUCCESS) {
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tinyxml2::XMLElement* root = doc.RootElement();
|
|
|
|
|
|
if (!root) return 1;
|
|
|
|
|
|
|
|
|
|
|
|
for (tinyxml2::XMLElement* groupElem = root->FirstChildElement(); groupElem != nullptr; groupElem = groupElem->NextSiblingElement()) {
|
|
|
|
|
|
std::string strTag = groupElem->Name();
|
|
|
|
|
|
if (strTag == "Work" || strTag == "New" || strTag == "Delete" || strTag == "Modify") {
|
|
|
|
|
|
for (tinyxml2::XMLElement* triggerElem = groupElem->FirstChildElement("Trigger"); triggerElem != nullptr; triggerElem = triggerElem->NextSiblingElement("Trigger")) {
|
|
|
|
|
|
parse_3s_trigger(trigger_3s_xml, strTag, triggerElem);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//将内容写入协议
|
|
|
|
|
|
void process_3s_config(trigger_3s_xml_t *trigger_3s_xml)
|
|
|
|
|
|
{
|
|
|
|
|
|
//根据协议补充内容
|
|
|
|
|
|
|
|
|
|
|
|
//根据协议补充内容
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//实时触发处理
|
|
|
|
|
|
int parse_3s_xml(trigger_3s_xml_t* trigger_3s_xml) {
|
|
|
|
|
|
std::cout << "begin 3s xml..." << std::endl;
|
|
|
|
|
|
std::memset(trigger_3s_xml, 0, sizeof(trigger_3s_xml_t));
|
|
|
|
|
|
|
|
|
|
|
|
std::string THREE_SECS_WEBSERVICE_DIR = FRONT_PATH + "/etc/trigger3s/"; //实时数据读取目录
|
|
|
|
|
|
std::string BAK_WEBSERVICE_3S_TRIG_COMMAND_XML_FN = THREE_SECS_WEBSERVICE_DIR + "bak_3s_trig_command.txt"; //实时触发文件备份文件名
|
|
|
|
|
|
|
|
|
|
|
|
std::string the_webservice_xml_fn = get_3s_trig_fn(); // 获取目录下最新 XML 文件名
|
|
|
|
|
|
|
|
|
|
|
|
if (the_webservice_xml_fn.length() > 4) {
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 等待 0.1 秒
|
|
|
|
|
|
|
|
|
|
|
|
std::string full_path = std::string(THREE_SECS_WEBSERVICE_DIR) + the_webservice_xml_fn;
|
|
|
|
|
|
|
|
|
|
|
|
if (load_3s_data_from_xml(trigger_3s_xml, full_path) != 0) {
|
|
|
|
|
|
std::cerr << "Failed to load 3s data from XML: " << full_path << std::endl;
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::remove(BAK_WEBSERVICE_3S_TRIG_COMMAND_XML_FN.c_str()); // 删除旧备份
|
|
|
|
|
|
if (std::rename(full_path.c_str(), BAK_WEBSERVICE_3S_TRIG_COMMAND_XML_FN.c_str()) != 0) {
|
|
|
|
|
|
std::perror("Rename failed");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "/etc/trigger3s/*.xml success..." << std::endl;
|
|
|
|
|
|
DIY_WARNLOG("process", "【WARN】前置读取实时数据触发文件成功,即将注册实时数据报告");
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "3s xml fail..." << std::endl;
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void check_3s_config() {
|
|
|
|
|
|
double now;
|
|
|
|
|
|
static double last_check_3s_config_time = 0.0; // 初始化时间
|
|
|
|
|
|
trigger_3s_xml_t trigger_3s_xml; // 3s触发文件
|
|
|
|
|
|
|
|
|
|
|
|
if (!three_secs_enabled) // 只有cfg_3s_data进程才会开启
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
now = sGetMsTime(); // 当前时间
|
|
|
|
|
|
if (std::fabs(now - last_check_3s_config_time) < 3 * 1000) // wait 3secs
|
|
|
|
|
|
return; // 当前进程任务执行时查看当前时间和上次执行时间间隔,小于3秒不执行,大于等于3秒往下执行
|
|
|
|
|
|
|
|
|
|
|
|
last_check_3s_config_time = now; // 记录本次运行时间
|
|
|
|
|
|
while (0 == parse_3s_xml(&trigger_3s_xml)) { // 处理3秒文件,一次处理一个
|
|
|
|
|
|
process_3s_config(&trigger_3s_xml); // 根据文件处理数据
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////补招部分
|
|
|
|
|
|
|
|
|
|
|
|
//将文件内容读取到结构中
|
|
|
|
|
|
void parse_recall(recall_xml_t* recall_xml, const std::string& parentTag, const std::map<std::string, std::string>& attributes, const std::string& id) {
|
|
|
|
|
|
recall_t recall;
|
|
|
|
|
|
std::memset(&recall, 0, sizeof(recall_t));
|
|
|
|
|
|
|
|
|
|
|
|
// 设置监测点 ID
|
|
|
|
|
|
recall.line_id = id;
|
|
|
|
|
|
|
|
|
|
|
|
// 解析时间字符串
|
|
|
|
|
|
auto parse_time = [](const std::string& time_str) -> std::time_t {
|
|
|
|
|
|
std::tm tm = {};
|
|
|
|
|
|
std::istringstream ss(time_str);
|
|
|
|
|
|
ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
|
|
|
|
|
|
if (ss.fail()) return 0;
|
|
|
|
|
|
return std::mktime(&tm);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
recall.start_time = parse_time(attributes.at("StartTime"));
|
|
|
|
|
|
recall.end_time = parse_time(attributes.at("EndTime"));
|
|
|
|
|
|
recall.need_steady = std::stoi(attributes.at("STEADY"));
|
|
|
|
|
|
recall.need_voltage = std::stoi(attributes.at("VOLTAGE"));
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << parentTag << " -> " << recall.line_id
|
|
|
|
|
|
<< " " << recall.need_steady
|
|
|
|
|
|
<< " " << recall.need_voltage
|
|
|
|
|
|
<< " " << recall.start_time
|
|
|
|
|
|
<< " " << recall.end_time << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
if (parentTag == "Work" && recall_xml->work_recall_num < MAX_RECALL_NUM) {
|
|
|
|
|
|
recall_xml->work_recalls[recall_xml->work_recall_num++] = recall;
|
|
|
|
|
|
} else if (parentTag == "New" && recall_xml->new_recall_num < MAX_RECALL_NUM) {
|
|
|
|
|
|
recall_xml->new_recalls[recall_xml->new_recall_num++] = recall;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//读取补招文件
|
|
|
|
|
|
int parse_recall_xml(recall_xml_t* recall_xml, const std::string& id) {
|
|
|
|
|
|
std::string cfg_dir = FRONT_PATH + "/etc/recall";
|
|
|
|
|
|
std::string pattern = subdir + "_" + std::to_string(g_front_seg_index) + "_" + id + "_*_Recall.xml";
|
|
|
|
|
|
|
|
|
|
|
|
DIR* dir = opendir(cfg_dir.c_str());
|
|
|
|
|
|
if (!dir) {
|
|
|
|
|
|
DIY_ERRORLOG("process", "【ERROR】前置的%s%d号进程 无法解析补招文件,补招文件路径FRONT_PATH + /etc/recall/不存在", get_front_msg_from_subdir(), g_front_seg_index);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct dirent* entry;
|
|
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
|
|
|
|
std::string filename = entry->d_name;
|
|
|
|
|
|
if (fnmatch(pattern.c_str(), filename.c_str(), 0) != 0) continue;
|
|
|
|
|
|
|
|
|
|
|
|
std::string filepath = cfg_dir + "/" + filename;
|
|
|
|
|
|
tinyxml2::XMLDocument doc;
|
|
|
|
|
|
if (doc.LoadFile(filepath.c_str()) != tinyxml2::XML_SUCCESS) {
|
|
|
|
|
|
DIY_ERRORLOG("process", "【ERROR】前置的%s%d号进程 无法解析补招文件%s,补招内容无效", get_front_msg_from_subdir(), g_front_seg_index, filepath.c_str());
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tinyxml2::XMLElement* root = doc.RootElement();
|
|
|
|
|
|
if (!root) continue;
|
|
|
|
|
|
|
|
|
|
|
|
for (tinyxml2::XMLElement* elem = root->FirstChildElement(); elem != nullptr; elem = elem->NextSiblingElement()) {
|
|
|
|
|
|
std::string tag = elem->Name();
|
|
|
|
|
|
if (tag == "Work" || tag == "New") {
|
|
|
|
|
|
for (tinyxml2::XMLElement* recallElem = elem->FirstChildElement("Recall"); recallElem != nullptr; recallElem = recallElem->NextSiblingElement("Recall")) {
|
|
|
|
|
|
std::map<std::string, std::string> attrs;
|
|
|
|
|
|
const char* start = recallElem->Attribute("StartTime");
|
|
|
|
|
|
const char* end = recallElem->Attribute("EndTime");
|
|
|
|
|
|
const char* steady = recallElem->Attribute("STEADY");
|
|
|
|
|
|
const char* voltage = recallElem->Attribute("VOLTAGE");
|
|
|
|
|
|
|
|
|
|
|
|
if (start && end && steady && voltage) {
|
|
|
|
|
|
attrs["StartTime"] = start;
|
|
|
|
|
|
attrs["EndTime"] = end;
|
|
|
|
|
|
attrs["STEADY"] = steady;
|
|
|
|
|
|
attrs["VOLTAGE"] = voltage;
|
|
|
|
|
|
|
|
|
|
|
|
parse_recall(recall_xml, tag, attrs, id);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
closedir(dir);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//将读取到的补招文件写入到监测点的运行结构中,后续根据实际补充
|
|
|
|
|
|
void process_recall_config(recall_xml_t* recall_xml)
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//根据监测点id来获取补招数据,补招时调用这个
|
|
|
|
|
|
void Check_Recall_Config(const std::string& id) {
|
|
|
|
|
|
if (g_node_id == HIS_DATA_BASE_NODE_ID ||
|
|
|
|
|
|
g_node_id == NEW_HIS_DATA_BASE_NODE_ID ||
|
|
|
|
|
|
g_node_id == RECALL_HIS_DATA_BASE_NODE_ID ||
|
|
|
|
|
|
g_node_id == RECALL_ALL_DATA_BASE_NODE_ID) {
|
|
|
|
|
|
|
|
|
|
|
|
recall_xml_t recall_xml;
|
|
|
|
|
|
std::memset(&recall_xml, 0, sizeof(recall_xml_t));
|
|
|
|
|
|
|
|
|
|
|
|
// 解析补招文件
|
|
|
|
|
|
parse_recall_xml(&recall_xml, id);
|
|
|
|
|
|
|
|
|
|
|
|
// 将补招数据赋值到全局变量
|
|
|
|
|
|
process_recall_config(&recall_xml);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//补招成功后删除补招文件,补招后调用这个
|
|
|
|
|
|
int delete_recall_xml(const std::string& id) {
|
|
|
|
|
|
std::string cfg_dir = FRONT_PATH + "/etc/recall";
|
|
|
|
|
|
std::string pattern = std::string(subdir) + "_" +
|
|
|
|
|
|
std::to_string(g_front_seg_index) + "_" +
|
|
|
|
|
|
id + "_*_Recall.xml";
|
|
|
|
|
|
|
|
|
|
|
|
DIR* dir = opendir(cfg_dir.c_str());
|
|
|
|
|
|
if (!dir) {
|
|
|
|
|
|
std::cerr << "Folder does not exist or cannot be opened: " << cfg_dir << std::endl;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct dirent* entry;
|
|
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
|
|
|
|
std::string filename = entry->d_name;
|
|
|
|
|
|
if (fnmatch(pattern.c_str(), filename.c_str(), 0) == 0) {
|
|
|
|
|
|
std::string fullpath = cfg_dir + "/" + filename;
|
|
|
|
|
|
if (remove(fullpath.c_str()) == 0) {
|
|
|
|
|
|
std::cout << "Deleted: " << fullpath << std::endl;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
std::cerr << "Failed to delete: " << fullpath << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
closedir(dir);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//删除过期的xmllnk:多个进程并发删除导致的失败不会影响进程
|
|
|
|
|
|
void DeletcRecallXml() {
|
|
|
|
|
|
std::string cfg_dir = FRONT_PATH + "/etc/recall";
|
|
|
|
|
|
std::string pattern = std::string(subdir) + "_*_Recall.xml";
|
|
|
|
|
|
|
|
|
|
|
|
DIR* dir = opendir(cfg_dir.c_str());
|
|
|
|
|
|
if (!dir) {
|
|
|
|
|
|
std::cerr << "folder does not exist!" << std::endl;
|
|
|
|
|
|
DIY_ERRORLOG("process", "【ERROR】前置的%s%d号进程 删除旧的补招文件失败,补招文件路径FRONT_PATH + /etc/recall/不存在",get_front_msg_from_subdir(), g_front_seg_index);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取当前时间,计算 2 天前的时间戳
|
|
|
|
|
|
std::time_t now = std::time(nullptr);
|
|
|
|
|
|
std::time_t cutoff = now - 2 * 24 * 60 * 60; // 两天前
|
|
|
|
|
|
|
|
|
|
|
|
struct dirent* entry;
|
|
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
|
|
|
|
std::string filename = entry->d_name;
|
|
|
|
|
|
|
|
|
|
|
|
if (fnmatch(pattern.c_str(), filename.c_str(), 0) == 0) {
|
|
|
|
|
|
std::string fullpath = cfg_dir + "/" + filename;
|
|
|
|
|
|
|
|
|
|
|
|
struct stat file_stat;
|
|
|
|
|
|
if (stat(fullpath.c_str(), &file_stat) == 0) {
|
|
|
|
|
|
if (file_stat.st_mtime < cutoff) {
|
|
|
|
|
|
if (remove(fullpath.c_str()) == 0) {
|
|
|
|
|
|
DIY_INFOLOG("process", "【NORMAL】前置的%s%d号进程 删除超过两天的补招文件",get_front_msg_from_subdir(), g_front_seg_index);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
std::cerr << "Failed to remove file: " << fullpath << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
closedir(dir);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//根据补招列表创建补招文件
|
|
|
|
|
|
void CreateRecallXml() {
|
|
|
|
|
|
std::time_t now = std::time(nullptr);
|
|
|
|
|
|
std::tm* tm_now = std::localtime(&now);
|
|
|
|
|
|
char timestamp[32] = {0};
|
|
|
|
|
|
std::strftime(timestamp, sizeof(timestamp), "%Y%m%d%H%M%S", tm_now);
|
|
|
|
|
|
|
|
|
|
|
|
g_StatisticLackList_list_mutex.lock();
|
|
|
|
|
|
|
|
|
|
|
|
if (!g_StatisticLackList.empty()) {
|
|
|
|
|
|
DIY_INFOLOG("process", "【NORMAL】前置的%s%d号进程 开始写入补招文件", get_front_msg_from_subdir(), g_front_seg_index);
|
|
|
|
|
|
|
|
|
|
|
|
std::map<std::string, std::list<JournalRecall>> id_map;
|
|
|
|
|
|
for (const auto& jr : g_StatisticLackList) {
|
|
|
|
|
|
id_map[jr.MonitorID].push_back(jr);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& pair : id_map) {
|
|
|
|
|
|
const std::string& monitor_id = pair.first;
|
|
|
|
|
|
const std::list<JournalRecall>& recalls = pair.second;
|
|
|
|
|
|
|
|
|
|
|
|
std::ostringstream path;
|
|
|
|
|
|
path << FRONT_PATH << "/etc/recall/" << subdir << "_" << g_front_seg_index << "_" << monitor_id << "_" << timestamp << "_Recall.xml";
|
|
|
|
|
|
|
|
|
|
|
|
tinyxml2::XMLDocument doc;
|
|
|
|
|
|
tinyxml2::XMLDeclaration* decl = doc.NewDeclaration();
|
|
|
|
|
|
doc.InsertFirstChild(decl);
|
|
|
|
|
|
|
|
|
|
|
|
tinyxml2::XMLElement* root = doc.NewElement("RecallList");
|
|
|
|
|
|
doc.InsertEndChild(root);
|
|
|
|
|
|
|
|
|
|
|
|
// 空 Work 段
|
|
|
|
|
|
tinyxml2::XMLElement* work = doc.NewElement("Work");
|
|
|
|
|
|
root->InsertEndChild(work);
|
|
|
|
|
|
|
|
|
|
|
|
// New 段
|
|
|
|
|
|
tinyxml2::XMLElement* new_elem = doc.NewElement("New");
|
|
|
|
|
|
for (const auto& jr : recalls) {
|
|
|
|
|
|
tinyxml2::XMLElement* recall = doc.NewElement("Recall");
|
|
|
|
|
|
recall->SetAttribute("MonitorID", jr.MonitorID.c_str());
|
|
|
|
|
|
recall->SetAttribute("StartTime", jr.StartTime.c_str());
|
|
|
|
|
|
recall->SetAttribute("EndTime", jr.EndTime.c_str());
|
|
|
|
|
|
recall->SetAttribute("STEADY", jr.STEADY.c_str());
|
|
|
|
|
|
recall->SetAttribute("VOLTAGE", jr.VOLTAGE.c_str());
|
|
|
|
|
|
new_elem->InsertEndChild(recall);
|
|
|
|
|
|
}
|
|
|
|
|
|
root->InsertEndChild(new_elem);
|
|
|
|
|
|
|
|
|
|
|
|
tinyxml2::XMLError save_result = doc.SaveFile(path.str().c_str());
|
|
|
|
|
|
if (save_result != tinyxml2::XML_SUCCESS) {
|
|
|
|
|
|
DIY_ERRORLOG("process", "【ERROR】前置的%s%d号进程 无法将补招文件写入路径: %s",get_front_msg_from_subdir(), g_front_seg_index, path.str().c_str());
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
g_StatisticLackList.clear();
|
|
|
|
|
|
g_StatisticLackList_list_mutex.unlock();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//生成待补招xml文件
|
|
|
|
|
|
void create_recall_xml()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (g_node_id == HIS_DATA_BASE_NODE_ID || g_node_id == NEW_HIS_DATA_BASE_NODE_ID || g_node_id == RECALL_HIS_DATA_BASE_NODE_ID || (g_node_id == RECALL_ALL_DATA_BASE_NODE_ID)) {
|
|
|
|
|
|
DeletcRecallXml();
|
|
|
|
|
|
CreateRecallXml();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 工具函数:将时间字符串转为 time_t(秒级)
|
|
|
|
|
|
static long long parse_time_to_epoch(const std::string& time_str) {
|
|
|
|
|
|
std::tm tm = {};
|
|
|
|
|
|
std::istringstream ss(time_str);
|
|
|
|
|
|
ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
|
|
|
|
|
|
if (ss.fail()) {
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
return static_cast<long long>(std::mktime(&tm));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 数据完整性补招判断(划分为1小时)
|
|
|
|
|
|
void Get_Recall_Time_Char(const std::string& start_time_str,
|
|
|
|
|
|
const std::string& end_time_str,
|
|
|
|
|
|
std::vector<RecallInfo>& recallinfo_list_hour) {
|
|
|
|
|
|
long long starttime = parse_time_to_epoch(start_time_str);
|
|
|
|
|
|
long long endtime = parse_time_to_epoch(end_time_str);
|
|
|
|
|
|
|
|
|
|
|
|
if (starttime == 0 || endtime == 0 || starttime >= endtime) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 初始区间加入
|
|
|
|
|
|
RecallInfo initial;
|
|
|
|
|
|
initial.starttime = starttime;
|
|
|
|
|
|
initial.endtime = endtime;
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<RecallInfo> recallinfo_list;
|
|
|
|
|
|
recallinfo_list.push_back(initial);
|
|
|
|
|
|
|
|
|
|
|
|
const long long max_interval = 3600; // 1小时
|
|
|
|
|
|
for (const auto& interval : recallinfo_list) {
|
|
|
|
|
|
long long duration = interval.endtime - interval.starttime;
|
|
|
|
|
|
|
|
|
|
|
|
for (long long j = 0; j <= duration; j += max_interval) {
|
|
|
|
|
|
RecallInfo info;
|
|
|
|
|
|
info.starttime = interval.starttime + j;
|
|
|
|
|
|
if (j + max_interval > duration) {
|
|
|
|
|
|
info.endtime = interval.endtime;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
info.endtime = interval.starttime + j + max_interval - 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
recallinfo_list_hour.push_back(info);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//mq调用将补招信息写入补招列表
|
|
|
|
|
|
int recall_json_handle(const std::string& jstr) {
|
|
|
|
|
|
// 不指定稳态/暂态则全部补招
|
|
|
|
|
|
int stat = 0;
|
|
|
|
|
|
int voltage = 0;
|
|
|
|
|
|
try {
|
|
|
|
|
|
std::vector<JournalRecall> recallParams;
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 解析 JSON 数组
|
|
|
|
|
|
auto json_root = nlohmann::json::parse(jstr);
|
|
|
|
|
|
if (!json_root.is_array()) {
|
|
|
|
|
|
std::cout << "json root解析错误" << std::endl;
|
|
|
|
|
|
return 10000;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 遍历每个补招项
|
|
|
|
|
|
for (auto& item : json_root) {
|
|
|
|
|
|
// 获取必需字段
|
|
|
|
|
|
if (!item.contains("monitorId") ||
|
|
|
|
|
|
!item.contains("timeInterval") ||
|
|
|
|
|
|
!item.contains("dataType"))
|
|
|
|
|
|
{
|
|
|
|
|
|
std::cout << "json内容解析错误" << std::endl;
|
|
|
|
|
|
return 10000;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2.1 解析 dataType
|
|
|
|
|
|
std::string datatype = item["dataType"].get<std::string>();
|
|
|
|
|
|
if (!datatype.empty()) {
|
|
|
|
|
|
stat = (datatype == "0") ? 1 : 0; // 稳态
|
|
|
|
|
|
voltage = (datatype == "1") ? 1 : 0; // 暂态
|
|
|
|
|
|
} else {
|
|
|
|
|
|
stat = voltage = 1; // 全补
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2.2 处理 monitorId 数组
|
|
|
|
|
|
auto& midArr = item["monitorId"];
|
|
|
|
|
|
auto& tiArr = item["timeInterval"];
|
|
|
|
|
|
if (midArr.is_array() && tiArr.is_array() && !midArr.empty()) {
|
|
|
|
|
|
for (auto& idItem : midArr) {
|
|
|
|
|
|
std::string monitorId = idItem.get<std::string>();
|
|
|
|
|
|
|
|
|
|
|
|
// 判断此监测点是否归属当前进程
|
|
|
|
|
|
bool mppair = false;
|
|
|
|
|
|
for (const auto& dev : terminal_devlist) {
|
|
|
|
|
|
// 只处理本进程对应的终端
|
|
|
|
|
|
if (std::stoi(dev.processNo) != g_front_seg_index &&
|
|
|
|
|
|
g_front_seg_index != 0) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
for (const auto& mon : dev.line) {
|
|
|
|
|
|
if (mon.monitor_id.empty()) continue;
|
|
|
|
|
|
if (mon.monitor_id == monitorId) {
|
|
|
|
|
|
mppair = true;
|
|
|
|
|
|
std::cout << "Matched monitorId " << monitorId
|
|
|
|
|
|
<< " in terminal " << dev.terminal_id
|
|
|
|
|
|
<< std::endl;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (mppair) break;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!mppair) continue;
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历 timeInterval 数组
|
|
|
|
|
|
for (auto& timeItem : tiArr) {
|
|
|
|
|
|
std::string ti = timeItem.get<std::string>();
|
|
|
|
|
|
auto pos = ti.find('~');
|
|
|
|
|
|
std::string start = ti.substr(0, pos);
|
|
|
|
|
|
std::string end = ti.substr(pos + 1);
|
|
|
|
|
|
|
|
|
|
|
|
JournalRecall param;
|
|
|
|
|
|
param.MonitorID = monitorId;
|
|
|
|
|
|
param.StartTime = start;
|
|
|
|
|
|
param.EndTime = end;
|
|
|
|
|
|
param.STEADY = std::to_string(stat);
|
|
|
|
|
|
param.VOLTAGE = std::to_string(voltage);
|
|
|
|
|
|
recallParams.push_back(param);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 2.3 monitorId 数组存在但为空 -> 补招所有监测点
|
|
|
|
|
|
else if (midArr.is_array() && midArr.empty()) {
|
|
|
|
|
|
std::cout << "monitorIdArray is null,补招所有监测点" << std::endl;
|
|
|
|
|
|
for (const auto& dev : terminal_devlist) {
|
|
|
|
|
|
if (std::stoi(dev.processNo) != g_front_seg_index &&
|
|
|
|
|
|
g_front_seg_index != 0) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
for (const auto& mon : dev.line) {
|
|
|
|
|
|
if (mon.monitor_id.empty()) continue;
|
|
|
|
|
|
for (auto& timeItem : tiArr) {
|
|
|
|
|
|
std::string ti = timeItem.get<std::string>();
|
|
|
|
|
|
auto pos = ti.find('~');
|
|
|
|
|
|
std::string start = ti.substr(0, pos);
|
|
|
|
|
|
std::string end = ti.substr(pos + 1);
|
|
|
|
|
|
|
|
|
|
|
|
JournalRecall param;
|
|
|
|
|
|
param.MonitorID = mon.monitor_id;
|
|
|
|
|
|
param.StartTime = start;
|
|
|
|
|
|
param.EndTime = end;
|
|
|
|
|
|
param.STEADY = std::to_string(stat);
|
|
|
|
|
|
param.VOLTAGE = std::to_string(voltage);
|
|
|
|
|
|
recallParams.push_back(param);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
std::cout << "monitorIdArray 不存在或类型不正确" << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 生成具体补招记录
|
|
|
|
|
|
for (auto& rp : recallParams) {
|
|
|
|
|
|
std::string start_time = rp.StartTime;
|
|
|
|
|
|
std::string end_time = rp.EndTime;
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "mp_id " << rp.MonitorID
|
|
|
|
|
|
<< " start_time " << start_time
|
|
|
|
|
|
<< " end_time " << end_time
|
|
|
|
|
|
<< " stat " << rp.STEADY
|
|
|
|
|
|
<< " voltage " << rp.VOLTAGE
|
|
|
|
|
|
<< std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<RecallInfo> recallinfo_list_hour;
|
|
|
|
|
|
Get_Recall_Time_Char(start_time, end_time, recallinfo_list_hour);
|
|
|
|
|
|
|
|
|
|
|
|
for (auto& info : recallinfo_list_hour) {
|
|
|
|
|
|
JournalRecall jr;
|
|
|
|
|
|
jr.MonitorID = rp.MonitorID;
|
|
|
|
|
|
|
|
|
|
|
|
// 转换 starttime
|
|
|
|
|
|
char buf[20];
|
|
|
|
|
|
std::tm tm{};
|
|
|
|
|
|
time_t st = static_cast<time_t>(info.starttime);
|
|
|
|
|
|
localtime_r(&st, &tm);
|
|
|
|
|
|
std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm);
|
|
|
|
|
|
jr.StartTime = buf;
|
|
|
|
|
|
|
|
|
|
|
|
// 转换 endtime
|
|
|
|
|
|
std::tm tm2{};
|
|
|
|
|
|
time_t et = static_cast<time_t>(info.endtime);
|
|
|
|
|
|
localtime_r(&et, &tm2);
|
|
|
|
|
|
std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm2);
|
|
|
|
|
|
jr.EndTime = buf;
|
|
|
|
|
|
|
|
|
|
|
|
jr.STEADY = rp.STEADY;
|
|
|
|
|
|
jr.VOLTAGE = rp.VOLTAGE;
|
|
|
|
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> lk(g_StatisticLackList_list_mutex);
|
|
|
|
|
|
g_StatisticLackList.push_back(jr);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (const std::exception& e) {
|
|
|
|
|
|
std::cout << "处理客户端发送的消息错误,原因:" << e.what() << std::endl;
|
|
|
|
|
|
return 10004;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////台账接口打印
|
|
|
|
|
|
|
|
|
|
|
|
// 打印 terminal_dev_map 中所有内容的函数
|
|
|
|
|
|
void printTerminalDevMap(const std::map<std::string, terminal_dev>& terminal_dev_map) {
|
|
|
|
|
|
for (const auto& kv : terminal_dev_map) {
|
|
|
|
|
|
const std::string& key = kv.first;
|
|
|
|
|
|
const terminal_dev& dev = kv.second; // 引用对象
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "Key: " << key
|
|
|
|
|
|
<< ", Terminal ID: " << dev.terminal_id
|
|
|
|
|
|
<< ", Terminal Code: " << dev.terminal_code
|
|
|
|
|
|
<< ", Organization Name: "<< dev.org_name
|
|
|
|
|
|
<< ", Maintenance Name: " << dev.maint_name
|
|
|
|
|
|
<< ", Station Name: " << dev.station_name
|
|
|
|
|
|
<< ", Factory: " << dev.tmnl_factory
|
|
|
|
|
|
<< ", Status: " << dev.tmnl_status
|
|
|
|
|
|
<< ", Device Type: " << dev.dev_type
|
|
|
|
|
|
<< ", Device Key: " << dev.dev_key
|
|
|
|
|
|
<< ", Device Series: " << dev.dev_series
|
|
|
|
|
|
<< ", ProcessNo: " << dev.processNo
|
|
|
|
|
|
<< ", MaxProcessNum: " << dev.maxProcessNum
|
|
|
|
|
|
<< ", Address: " << dev.addr_str
|
|
|
|
|
|
<< ", Port: " << dev.port
|
|
|
|
|
|
<< ", Timestamp: " << dev.timestamp
|
|
|
|
|
|
<< std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
// 打印监测点信息
|
|
|
|
|
|
for (size_t i = 0; i < dev.line.size(); ++i) {
|
|
|
|
|
|
const auto& m = dev.line[i];
|
|
|
|
|
|
std::cout << " Monitor [" << i << "] "
|
|
|
|
|
|
<< "ID: " << m.monitor_id
|
|
|
|
|
|
<< ", Code: " << m.terminal_code
|
|
|
|
|
|
<< ", Name: " << m.monitor_name
|
|
|
|
|
|
<< ", Seq: " << m.logical_device_seq
|
|
|
|
|
|
<< ", Voltage: "<< m.voltage_level
|
|
|
|
|
|
<< ", Connect: "<< m.terminal_connect
|
|
|
|
|
|
<< ", Timestamp:"<< m.timestamp
|
|
|
|
|
|
<< ", Status: " << m.status
|
|
|
|
|
|
<< std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////更新台账
|
|
|
|
|
|
|
|
|
|
|
|
//在台账更新目录查找自己进程要处理的文件
|
|
|
|
|
|
std::list<std::string> find_xml_belong_to_this_process()
|
|
|
|
|
|
{
|
|
|
|
|
|
char prefix[20]; // 假设最多需要20个字符(根据实际需要调整)
|
|
|
|
|
|
sprintf(prefix, "%d_%d", g_node_id, g_front_seg_index); // 将g_node_id和g_front_seg_index格式化为字符串
|
|
|
|
|
|
|
|
|
|
|
|
std::string LEDGER_UPDATE_DIR = FRONT_PATH + "/etc/ledgerupdate/"; //台账更新读取目录
|
|
|
|
|
|
|
|
|
|
|
|
DIR *dir = opendir(LEDGER_UPDATE_DIR.c_str()); // 打开目录
|
|
|
|
|
|
struct dirent *entry;
|
|
|
|
|
|
std::list<std::string> found_files; // 用于存储找到的所有匹配文件名
|
|
|
|
|
|
|
|
|
|
|
|
if (dir == NULL) {
|
|
|
|
|
|
std::cout << "Failed to open directory: " << LEDGER_UPDATE_DIR << std::endl;
|
|
|
|
|
|
return found_files; // 返回空的list
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历目录中的所有文件
|
|
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
|
|
|
|
std::string filename = entry->d_name;
|
|
|
|
|
|
|
|
|
|
|
|
// 排除 "." 和 ".." 目录
|
|
|
|
|
|
if (filename == "." || filename == "..") {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "find" << filename << "in" << LEDGER_UPDATE_DIR << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 判断文件名是否以 prefix 开头且扩展名是 .xml
|
|
|
|
|
|
if (filename.find(prefix) == 0 && filename.substr(filename.find_last_of('.') + 1) == "xml") {
|
|
|
|
|
|
std::string full_path = LEDGER_UPDATE_DIR + filename;
|
|
|
|
|
|
found_files.push_back(full_path); // 将完整路径加入容器
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
closedir(dir); // 关闭目录
|
|
|
|
|
|
|
|
|
|
|
|
return found_files; // 返回所有找到的文件名
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 根据 str_tag 将 terminal 添加到对应的数组
|
|
|
|
|
|
void add_terminal_to_trigger_update(trigger_update_xml_t& trigger_update_xml,
|
|
|
|
|
|
const std::string& str_tag,
|
|
|
|
|
|
const terminal_dev& work_terminal) {
|
|
|
|
|
|
if (str_tag == "add") {
|
|
|
|
|
|
std::cout << "new ledger!!!!" << std::endl;
|
|
|
|
|
|
trigger_update_xml.new_updates.push_back(work_terminal);
|
|
|
|
|
|
} else if (str_tag == "modify") {
|
|
|
|
|
|
std::cout << "modify ledger!!!" << std::endl;
|
|
|
|
|
|
trigger_update_xml.modify_updates.push_back(work_terminal);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
std::cerr << "Unknown tag: " << str_tag << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 将添加和修改的文件内容写入结构
|
|
|
|
|
|
void parse_terminal_from_data(trigger_update_xml_t& trigger_update_xml,
|
|
|
|
|
|
const std::string& str_tag,
|
|
|
|
|
|
const std::string& data,
|
|
|
|
|
|
const std::string& guid_value) {
|
|
|
|
|
|
terminal_dev work_terminal;
|
|
|
|
|
|
work_terminal.guid = guid_value;
|
|
|
|
|
|
|
|
|
|
|
|
tinyxml2::XMLDocument doc;
|
|
|
|
|
|
if (doc.Parse(data.c_str()) != tinyxml2::XML_SUCCESS) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto root = doc.FirstChildElement("terminal");
|
|
|
|
|
|
if (!root) return;
|
|
|
|
|
|
|
|
|
|
|
|
auto get_value = [&](const char* tag) -> std::string {
|
|
|
|
|
|
auto elem = root->FirstChildElement(tag);
|
|
|
|
|
|
return elem && elem->GetText() ? elem->GetText() : "";
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
work_terminal.terminal_id = get_value("id");
|
|
|
|
|
|
work_terminal.terminal_code = get_value("terminalCode");
|
|
|
|
|
|
work_terminal.org_name = get_value("orgName");
|
|
|
|
|
|
work_terminal.maint_name = get_value("maintName");
|
|
|
|
|
|
work_terminal.station_name = get_value("stationName");
|
|
|
|
|
|
work_terminal.tmnl_factory = get_value("manufacturer");
|
|
|
|
|
|
work_terminal.tmnl_status = get_value("status");
|
|
|
|
|
|
work_terminal.dev_type = get_value("devType");
|
|
|
|
|
|
work_terminal.dev_key = get_value("devKey");
|
|
|
|
|
|
work_terminal.dev_series = get_value("series");
|
|
|
|
|
|
work_terminal.processNo = get_value("processNo");
|
|
|
|
|
|
work_terminal.addr_str = get_value("ip");
|
|
|
|
|
|
work_terminal.port = get_value("port");
|
|
|
|
|
|
work_terminal.timestamp = get_value("updateTime");
|
|
|
|
|
|
|
|
|
|
|
|
for (tinyxml2::XMLElement* monitor = root->FirstChildElement("monitorData");
|
|
|
|
|
|
monitor;
|
|
|
|
|
|
monitor = monitor->NextSiblingElement("monitorData")) {
|
|
|
|
|
|
ledger_monitor mon;
|
|
|
|
|
|
mon.monitor_id = monitor->FirstChildElement("id") ? monitor->FirstChildElement("id")->GetText() : "N/A";
|
|
|
|
|
|
mon.monitor_name = monitor->FirstChildElement("name") ? monitor->FirstChildElement("name")->GetText() : "N/A";
|
|
|
|
|
|
mon.voltage_level = monitor->FirstChildElement("voltageLevel") ? monitor->FirstChildElement("voltageLevel")->GetText() : "N/A";
|
|
|
|
|
|
mon.terminal_connect = monitor->FirstChildElement("ptType") ? monitor->FirstChildElement("ptType")->GetText() : "N/A";
|
|
|
|
|
|
mon.logical_device_seq = monitor->FirstChildElement("lineNo") ? monitor->FirstChildElement("lineNo")->GetText() : "N/A";
|
|
|
|
|
|
mon.timestamp = monitor->FirstChildElement("timestamp") ? monitor->FirstChildElement("timestamp")->GetText() : "N/A";
|
|
|
|
|
|
mon.terminal_code = monitor->FirstChildElement("terminal_code") ? monitor->FirstChildElement("terminal_code")->GetText() : "N/A";
|
|
|
|
|
|
mon.status = monitor->FirstChildElement("status") ? monitor->FirstChildElement("status")->GetText() : "N/A";
|
|
|
|
|
|
|
|
|
|
|
|
work_terminal.line.push_back(mon);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
add_terminal_to_trigger_update(trigger_update_xml, str_tag, work_terminal);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 统一处理文件内容和结构
|
|
|
|
|
|
void parse_ledger_update(trigger_update_xml_t& trigger_update_xml,
|
|
|
|
|
|
const std::string& strTag,
|
|
|
|
|
|
const std::string& data,
|
|
|
|
|
|
const std::string& guid_value) {
|
|
|
|
|
|
std::cout << "record one xml.." << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
if (strTag == "add" || strTag == "modify") {
|
|
|
|
|
|
parse_terminal_from_data(trigger_update_xml, strTag, data, guid_value);
|
|
|
|
|
|
} else if (strTag == "delete") {
|
|
|
|
|
|
terminal_dev delete_terminal;
|
|
|
|
|
|
tinyxml2::XMLDocument doc;
|
|
|
|
|
|
|
|
|
|
|
|
if (doc.Parse(data.c_str()) != tinyxml2::XML_SUCCESS) {
|
|
|
|
|
|
std::cerr << "Failed to parse XML for delete tag." << std::endl;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tinyxml2::XMLElement* root = doc.FirstChildElement("terminalData");
|
|
|
|
|
|
if (!root) {
|
|
|
|
|
|
std::cerr << "Missing terminalData element in delete tag." << std::endl;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tinyxml2::XMLElement* idElem = root->FirstChildElement("id");
|
|
|
|
|
|
if (idElem && idElem->GetText()) {
|
|
|
|
|
|
delete_terminal.terminal_id = idElem->GetText();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
std::cerr << "Missing id element in delete tag." << std::endl;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
delete_terminal.guid = guid_value;
|
|
|
|
|
|
trigger_update_xml.delete_updates.push_back(delete_terminal);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
std::cerr << "Unsupported strTag: " << strTag << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//读取台账更新文件
|
|
|
|
|
|
int load_ledger_update_from_xml(trigger_update_xml_t& trigger_update_xml, const std::string& xml_fn) {
|
|
|
|
|
|
std::cout << "start to load one xml.." << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
std::ifstream file(xml_fn);
|
|
|
|
|
|
if (!file.is_open()) {
|
|
|
|
|
|
std::cerr << "Failed to open file: " << xml_fn << std::endl;
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::stringstream buffer;
|
|
|
|
|
|
buffer << file.rdbuf();
|
|
|
|
|
|
std::string content = buffer.str();
|
|
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
|
|
|
|
tinyxml2::XMLDocument doc;
|
|
|
|
|
|
if (doc.Parse(content.c_str()) != tinyxml2::XML_SUCCESS) {
|
|
|
|
|
|
std::cerr << "Failed to parse XML content." << std::endl;
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto* root = doc.FirstChildElement("ledger_update");
|
|
|
|
|
|
if (!root) {
|
|
|
|
|
|
std::cerr << "Missing <ledger_update> root tag." << std::endl;
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::string guid_value;
|
|
|
|
|
|
auto* guidElem = root->FirstChildElement("guid");
|
|
|
|
|
|
if (guidElem && guidElem->GetText()) {
|
|
|
|
|
|
guid_value = guidElem->GetText();
|
|
|
|
|
|
std::cout << "Found guid: " << guid_value << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char* tag_names[] = {"add", "modify", "delete"};
|
|
|
|
|
|
tinyxml2::XMLElement* tagElem = nullptr;
|
|
|
|
|
|
std::string target_tag;
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& tag : tag_names) {
|
|
|
|
|
|
tagElem = root->FirstChildElement(tag);
|
|
|
|
|
|
if (tagElem) {
|
|
|
|
|
|
target_tag = tag;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!tagElem) {
|
|
|
|
|
|
std::cerr << "No <add>, <modify>, or <delete> tag found!" << std::endl;
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (auto* termElem = tagElem->FirstChildElement("terminalData");
|
|
|
|
|
|
termElem;
|
|
|
|
|
|
termElem = termElem->NextSiblingElement("terminalData")) {
|
|
|
|
|
|
tinyxml2::XMLPrinter printer;
|
|
|
|
|
|
termElem->Accept(&printer);
|
|
|
|
|
|
std::string data_content = printer.CStr();
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "ledger data_content is " << data_content << std::endl;
|
|
|
|
|
|
parse_ledger_update(trigger_update_xml, target_tag, data_content, guid_value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "load one xml finish" << std::endl;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//台账更新处理文件
|
|
|
|
|
|
int parse_ledger_update_xml(trigger_update_xml_t& trigger_update_xml)
|
|
|
|
|
|
{
|
|
|
|
|
|
std::list<std::string> result = find_xml_belong_to_this_process();
|
|
|
|
|
|
|
|
|
|
|
|
if (result.empty()) return 1;
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& filename : result) {
|
|
|
|
|
|
std::cout << "Found XML: " << filename << std::endl;
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
|
|
|
|
|
|
|
|
if (!load_ledger_update_from_xml(trigger_update_xml, filename)) {
|
|
|
|
|
|
DIY_WARNLOG("process", "【WARN】成功读取台账更新文件: %s", filename.c_str());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (std::remove(filename.c_str()) != 0) {
|
|
|
|
|
|
DIY_ERRORLOG("process", "【ERROR】删除台账更新文件失败: %s", filename.c_str());
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
DIY_INFOLOG("process", "【NORMAL】成功删除台账更新文件: %s", filename.c_str());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!trigger_update_xml.new_updates.empty() ||
|
|
|
|
|
|
!trigger_update_xml.modify_updates.empty() ||
|
|
|
|
|
|
!trigger_update_xml.delete_updates.empty()) {
|
|
|
|
|
|
std::cout << "ledger update xml have data..." << std::endl;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "ledger update xml no data..." << std::endl;
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//更新单个台账
|
|
|
|
|
|
int update_one_terminal_ledger(const terminal_dev& update,terminal_dev& target_dev) {
|
|
|
|
|
|
// 更新基本信息
|
|
|
|
|
|
if (!update.terminal_id.empty()) {
|
|
|
|
|
|
target_dev.terminal_id = update.terminal_id;
|
|
|
|
|
|
std::cout << "terminal_id: " << target_dev.terminal_id << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!update.terminal_code.empty()) {
|
|
|
|
|
|
target_dev.terminal_code = update.terminal_code;
|
|
|
|
|
|
std::cout << "terminal_code: " << target_dev.terminal_code << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!update.tmnl_factory.empty()) {
|
|
|
|
|
|
target_dev.tmnl_factory = update.tmnl_factory;
|
|
|
|
|
|
std::cout << "tmnl_factory: " << target_dev.tmnl_factory << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!update.tmnl_status.empty()) {
|
|
|
|
|
|
target_dev.tmnl_status = update.tmnl_status;
|
|
|
|
|
|
std::cout << "tmnl_status: " << target_dev.tmnl_status << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!update.dev_type.empty()) {
|
|
|
|
|
|
target_dev.dev_type = update.dev_type;
|
|
|
|
|
|
std::cout << "dev_type: " << target_dev.dev_type << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!update.processNo.empty()) {
|
|
|
|
|
|
target_dev.processNo = update.processNo;
|
|
|
|
|
|
std::cout << "processNo: " << target_dev.processNo << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!update.dev_series.empty()) {
|
|
|
|
|
|
target_dev.dev_series = update.dev_series;
|
|
|
|
|
|
std::cout << "dev_series: " << target_dev.dev_series << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!update.dev_key.empty()) {
|
|
|
|
|
|
target_dev.dev_key = update.dev_key;
|
|
|
|
|
|
std::cout << "dev_key: " << target_dev.dev_key << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!update.addr_str.empty()) {
|
|
|
|
|
|
target_dev.addr_str = update.addr_str;
|
|
|
|
|
|
std::cout << "addr_str: " << target_dev.addr_str << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!update.port.empty()) {
|
|
|
|
|
|
target_dev.port = update.port;
|
|
|
|
|
|
std::cout << "port: " << target_dev.port << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!update.timestamp.empty()) {
|
|
|
|
|
|
struct tm timeinfo = {};
|
|
|
|
|
|
if (sscanf(update.timestamp.c_str(), "%4d-%2d-%2d %2d:%2d:%2d",
|
|
|
|
|
|
&timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday,
|
|
|
|
|
|
&timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec) == 6) {
|
|
|
|
|
|
timeinfo.tm_year -= 1900;
|
|
|
|
|
|
timeinfo.tm_mon -= 1;
|
|
|
|
|
|
timeinfo.tm_isdst = -1;
|
|
|
|
|
|
time_t raw_time = mktime(&timeinfo);
|
|
|
|
|
|
if (raw_time != -1) {
|
|
|
|
|
|
target_dev.timestamp = static_cast<long long>(raw_time);
|
|
|
|
|
|
std::cout << "timestamp (unix): " << target_dev.timestamp << std::endl;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
std::cerr << "Error: mktime failed." << std::endl;
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
std::cerr << "Error: invalid timestamp format." << std::endl;
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清空旧监测点并重新填充
|
|
|
|
|
|
target_dev.line.clear();
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& mon : update.line) {
|
|
|
|
|
|
if (mon.monitor_id.empty()) break;
|
|
|
|
|
|
|
|
|
|
|
|
ledger_monitor m;
|
|
|
|
|
|
m.monitor_id = mon.monitor_id;
|
|
|
|
|
|
m.monitor_name = mon.monitor_name;
|
|
|
|
|
|
m.logical_device_seq = mon.logical_device_seq;
|
|
|
|
|
|
m.voltage_level = mon.voltage_level;
|
|
|
|
|
|
m.terminal_connect = mon.terminal_connect;
|
|
|
|
|
|
m.status = mon.status;
|
|
|
|
|
|
m.terminal_code = mon.terminal_code;
|
|
|
|
|
|
m.timestamp = mon.timestamp;
|
|
|
|
|
|
|
|
|
|
|
|
if (m.terminal_connect != "0") {
|
|
|
|
|
|
isdelta_flag = 1;
|
|
|
|
|
|
std::cout << "monitor_id " << m.monitor_id << " uses delta wiring." << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!m.timestamp.empty()) {
|
|
|
|
|
|
struct tm timeinfo = {};
|
|
|
|
|
|
if (sscanf(m.timestamp.c_str(), "%4d-%2d-%2d %2d:%2d:%2d",
|
|
|
|
|
|
&timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday,
|
|
|
|
|
|
&timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec) == 6) {
|
|
|
|
|
|
timeinfo.tm_year -= 1900;
|
|
|
|
|
|
timeinfo.tm_mon -= 1;
|
|
|
|
|
|
timeinfo.tm_isdst = -1;
|
|
|
|
|
|
time_t raw_time = mktime(&timeinfo);
|
|
|
|
|
|
if (raw_time != -1) {
|
|
|
|
|
|
m.timestamp = static_cast<long long>(raw_time);
|
|
|
|
|
|
std::cout << "monitor time (unix): " << m.timestamp << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
target_dev.line.push_back(m);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//台账更新到台账列表
|
|
|
|
|
|
void process_ledger_update(trigger_update_xml_t& ledger_update_xml)
|
|
|
|
|
|
{
|
|
|
|
|
|
// --- 1. 新增处理 ---
|
|
|
|
|
|
std::cout << "add ledger num: " << ledger_update_xml.new_updates.size() << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
for (auto it = ledger_update_xml.new_updates.begin(); it != ledger_update_xml.new_updates.end(); ) {
|
|
|
|
|
|
terminal_dev& new_dev = *it;
|
|
|
|
|
|
|
|
|
|
|
|
auto found = std::find_if(terminal_devlist.begin(), terminal_devlist.end(),
|
|
|
|
|
|
[&](const terminal_dev& d) { return d.terminal_id == new_dev.terminal_id; });
|
|
|
|
|
|
|
|
|
|
|
|
if (found != terminal_devlist.end()) {
|
|
|
|
|
|
if (ledger_update_xml.modify_updates.size() < MAX_UPDATEA_NUM) {
|
|
|
|
|
|
ledger_update_xml.modify_updates.push_back(new_dev);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
std::cerr << "Exceeded MAX_UPDATEA_NUM limit for modify_updates!" << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
it = ledger_update_xml.new_updates.erase(it); // 删除已处理项
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (terminal_devlist.size() >= static_cast<size_t>(IED_COUNT)) {
|
|
|
|
|
|
send_reply_to_queue(new_dev.guid, "2",
|
|
|
|
|
|
"终端 id: " + new_dev.terminal_id + " 台账更新失败,配置台账数量已满");
|
|
|
|
|
|
++it;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
terminal_dev target_dev;
|
|
|
|
|
|
if (update_one_terminal_ledger(new_dev, target_dev) != 0) {
|
|
|
|
|
|
send_reply_to_queue(new_dev.guid, "2",
|
|
|
|
|
|
"终端 id: " + new_dev.terminal_id + " 台账更新失败,无法写入台账");
|
|
|
|
|
|
++it;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (parse_model_cfg_web_one(target_dev.dev_type).empty()) {
|
|
|
|
|
|
send_reply_to_queue(new_dev.guid, "2",
|
|
|
|
|
|
"终端 id: " + new_dev.terminal_id + " 台账更新失败,未找到装置型号");
|
|
|
|
|
|
++it;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Set_xml_nodeinfo_one(target_dev.dev_type);
|
|
|
|
|
|
init_loggers_bydevid(target_dev.terminal_id);
|
|
|
|
|
|
terminal_devlist.push_back(target_dev);
|
|
|
|
|
|
|
|
|
|
|
|
send_reply_to_queue(new_dev.guid, "2",
|
|
|
|
|
|
"终端 id: " + new_dev.terminal_id + " 台账添加成功");
|
|
|
|
|
|
|
|
|
|
|
|
it = ledger_update_xml.new_updates.erase(it);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --- 2. 修改处理 ---
|
|
|
|
|
|
std::cout << "modify ledger num: " << ledger_update_xml.modify_updates.size() << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
for (auto& mod_dev : ledger_update_xml.modify_updates) {
|
|
|
|
|
|
auto it = std::find_if(terminal_devlist.begin(), terminal_devlist.end(),
|
|
|
|
|
|
[&](const terminal_dev& d) { return d.terminal_id == mod_dev.terminal_id; });
|
|
|
|
|
|
|
|
|
|
|
|
if (it != terminal_devlist.end()) {
|
|
|
|
|
|
remove_loggers_by_terminal_id(mod_dev.terminal_id);
|
|
|
|
|
|
if (update_one_terminal_ledger(mod_dev, *it) != 0) {
|
|
|
|
|
|
send_reply_to_queue(mod_dev.guid, "2",
|
|
|
|
|
|
"终端 id: " + mod_dev.terminal_id + " 台账更新失败,写入失败");
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (parse_model_cfg_web_one(it->dev_type).empty()) {
|
|
|
|
|
|
send_reply_to_queue(mod_dev.guid, "2",
|
|
|
|
|
|
"终端 id: " + mod_dev.terminal_id + " 台账更新失败,未找到装置型号");
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Set_xml_nodeinfo_one(it->dev_type);
|
|
|
|
|
|
init_loggers_bydevid(mod_dev.terminal_id);
|
|
|
|
|
|
send_reply_to_queue(mod_dev.guid, "2",
|
|
|
|
|
|
"终端 id: " + mod_dev.terminal_id + " 台账修改成功");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
send_reply_to_queue(mod_dev.guid, "2",
|
|
|
|
|
|
"终端 id: " + mod_dev.terminal_id + " 台账修改失败,未找到终端");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --- 3. 删除处理 ---
|
|
|
|
|
|
std::cout << "delete ledger num: " << ledger_update_xml.delete_updates.size() << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
for (auto& del_dev : ledger_update_xml.delete_updates) {
|
|
|
|
|
|
auto it = std::find_if(terminal_devlist.begin(), terminal_devlist.end(),
|
|
|
|
|
|
[&](const terminal_dev& d) { return d.terminal_id == del_dev.terminal_id; });
|
|
|
|
|
|
|
|
|
|
|
|
if (it != terminal_devlist.end()) {
|
|
|
|
|
|
remove_loggers_by_terminal_id(del_dev.terminal_id);
|
|
|
|
|
|
terminal_devlist.erase(it);
|
|
|
|
|
|
send_reply_to_queue(del_dev.guid, "2",
|
|
|
|
|
|
"终端 id: " + del_dev.terminal_id + " 台账删除成功");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
send_reply_to_queue(del_dev.guid, "2",
|
|
|
|
|
|
"终端 id: " + del_dev.terminal_id + " 台账删除失败,未找到终端");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --- 4. 日志记录 ---
|
|
|
|
|
|
if (!ledger_update_xml.modify_updates.empty() ||
|
|
|
|
|
|
!ledger_update_xml.new_updates.empty() ||
|
|
|
|
|
|
!ledger_update_xml.delete_updates.empty()) {
|
|
|
|
|
|
create_ledger_log(&ledger_update_xml);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//台账更新处理函数
|
|
|
|
|
|
void check_ledger_update()
|
|
|
|
|
|
{
|
|
|
|
|
|
static double last_check_time = 0.0;
|
|
|
|
|
|
double now = sGetMsTime();
|
|
|
|
|
|
|
|
|
|
|
|
if (now - last_check_time < 3000.0)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
last_check_time = now;
|
|
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<trigger_update_xml_t> trigger_ledger_update_xml(new trigger_update_xml_t());
|
|
|
|
|
|
|
|
|
|
|
|
if (0 == parse_ledger_update_xml(*trigger_ledger_update_xml)) {
|
|
|
|
|
|
print_trigger_update_xml(*trigger_ledger_update_xml);
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(ledgermtx);
|
|
|
|
|
|
process_ledger_update(*trigger_ledger_update_xml);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////台账更新记录日志
|
|
|
|
|
|
|
|
|
|
|
|
// 获取当前时间并格式化为 "YYYY-MM-DD HH:MM:SS"
|
|
|
|
|
|
std::string get_current_time() {
|
|
|
|
|
|
std::time_t t = std::time(NULL);
|
|
|
|
|
|
struct std::tm tm = *std::localtime(&t);
|
|
|
|
|
|
|
|
|
|
|
|
char buffer[80];
|
|
|
|
|
|
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm);
|
|
|
|
|
|
return std::string(buffer);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 写入日志条目
|
|
|
|
|
|
void write_log_entry(std::ofstream &log_file, const std::string &action, const std::string &terminal_id, const std::string ¤t_time) {
|
|
|
|
|
|
log_file << terminal_id << "\t" << action << "time:" << current_time << "\n";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建台账更新日志
|
|
|
|
|
|
void create_ledger_log(trigger_update_xml_t* ledger_update_xml) {
|
|
|
|
|
|
std::cout << "create_ledger_log." << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
std::string log_filename = FRONT_PATH + "/etc/" + LEDGER_UPDATE_FN;
|
|
|
|
|
|
std::ofstream log_file(log_filename.c_str(), std::ios::app); // 以追加模式打开文件
|
|
|
|
|
|
|
|
|
|
|
|
if (!log_file.is_open()) {
|
|
|
|
|
|
std::cerr << "Failed to open log file: " << log_filename << std::endl;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<std::pair<std::string, std::string>> new_entries;
|
|
|
|
|
|
std::vector<std::pair<std::string, std::string>> modify_entries;
|
|
|
|
|
|
std::vector<std::pair<std::string, std::string>> delete_entries;
|
|
|
|
|
|
|
|
|
|
|
|
std::string current_time = get_current_time(); // 获取当前时间
|
|
|
|
|
|
|
|
|
|
|
|
// new_updates
|
|
|
|
|
|
for (const auto& dev : ledger_update_xml->new_updates) {
|
|
|
|
|
|
new_entries.emplace_back(dev.terminal_id, current_time);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// modify_updates
|
|
|
|
|
|
for (const auto& dev : ledger_update_xml->modify_updates) {
|
|
|
|
|
|
modify_entries.emplace_back(dev.terminal_id, current_time);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// delete_updates
|
|
|
|
|
|
for (const auto& dev : ledger_update_xml->delete_updates) {
|
|
|
|
|
|
delete_entries.emplace_back(dev.terminal_id, current_time);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 写入日志
|
|
|
|
|
|
if (!new_entries.empty()) {
|
|
|
|
|
|
log_file << "<new>\n";
|
|
|
|
|
|
for (const auto& entry : new_entries) {
|
|
|
|
|
|
write_log_entry(log_file, "add", entry.first, entry.second);
|
|
|
|
|
|
}
|
|
|
|
|
|
log_file << "</new>\n";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!modify_entries.empty()) {
|
|
|
|
|
|
log_file << "<modify>\n";
|
|
|
|
|
|
for (const auto& entry : modify_entries) {
|
|
|
|
|
|
write_log_entry(log_file, "modify", entry.first, entry.second);
|
|
|
|
|
|
}
|
|
|
|
|
|
log_file << "</modify>\n";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!delete_entries.empty()) {
|
|
|
|
|
|
log_file << "<delete>\n";
|
|
|
|
|
|
for (const auto& entry : delete_entries) {
|
|
|
|
|
|
write_log_entry(log_file, "delete", entry.first, entry.second);
|
|
|
|
|
|
}
|
|
|
|
|
|
log_file << "</delete>\n";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
log_file.close();
|
|
|
|
|
|
std::cout << "Ledger log has been updated." << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////shell打印日志
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------ 全局日志列表和锁 ------------------
|
|
|
|
|
|
std::list<std::string> errorList;
|
|
|
|
|
|
std::list<std::string> warnList;
|
|
|
|
|
|
std::list<std::string> normalList;
|
|
|
|
|
|
|
|
|
|
|
|
pthread_mutex_t errorListMutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
|
pthread_mutex_t warnListMutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
|
pthread_mutex_t normalListMutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------ 输出开关 ------------------
|
|
|
|
|
|
bool errorOutputEnabled = false; // 是否将 error 级别写入 errorList
|
|
|
|
|
|
bool warnOutputEnabled = false; // 是否将 warn 级别写入 warnList
|
|
|
|
|
|
bool normalOutputEnabled = false; // 是否将 normal 级别写入 normalList
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------ 用于恢复原始缓冲区 ------------------
|
|
|
|
|
|
static std::streambuf* g_originalCoutBuf = NULL;
|
|
|
|
|
|
static std::streambuf* g_originalClogBuf = NULL;
|
|
|
|
|
|
static std::streambuf* g_originalCerrBuf = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------ 日志级别枚举(C++98) ------------------
|
|
|
|
|
|
enum LogLevel {
|
|
|
|
|
|
LOGERROR,
|
|
|
|
|
|
LOGWARN,
|
|
|
|
|
|
LOGNORMAL
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
|
|
// TeeStreamBuf: 先写到原始buf(保持终端输出),再拷贝到list里
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
|
|
class TeeStreamBuf : public std::streambuf
|
|
|
|
|
|
{
|
|
|
|
|
|
public:
|
|
|
|
|
|
// 默认构造:先把指针设为NULL
|
|
|
|
|
|
TeeStreamBuf()
|
|
|
|
|
|
: m_originalBuf(NULL), m_level(LOGNORMAL)
|
|
|
|
|
|
{
|
|
|
|
|
|
pthread_mutex_init(&m_mutex, NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 带参构造:直接初始化
|
|
|
|
|
|
TeeStreamBuf(std::streambuf* originalBuf, LogLevel level)
|
|
|
|
|
|
: m_originalBuf(originalBuf), m_level(level)
|
|
|
|
|
|
{
|
|
|
|
|
|
pthread_mutex_init(&m_mutex, NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 析构函数:销毁互斥锁
|
|
|
|
|
|
virtual ~TeeStreamBuf()
|
|
|
|
|
|
{
|
|
|
|
|
|
pthread_mutex_destroy(&m_mutex);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 自定义 init(...) 函数:在同一个对象上重新设置
|
|
|
|
|
|
void init(std::streambuf* originalBuf, LogLevel level)
|
|
|
|
|
|
{
|
|
|
|
|
|
m_originalBuf = originalBuf;
|
|
|
|
|
|
m_level = level;
|
|
|
|
|
|
pthread_mutex_lock(&m_mutex);
|
|
|
|
|
|
m_buffer.clear();
|
|
|
|
|
|
pthread_mutex_unlock(&m_mutex);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
|
// 当 flush 或 std::endl 时会调用 sync()
|
|
|
|
|
|
virtual int sync()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 先让原始缓冲区执行同步
|
|
|
|
|
|
if (m_originalBuf) {
|
|
|
|
|
|
m_originalBuf->pubsync();
|
|
|
|
|
|
}
|
|
|
|
|
|
// 再将自身的缓存 flush
|
|
|
|
|
|
flushBuffer();
|
|
|
|
|
|
return 0; // 成功
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 当写入一个新字符时,overflow() 被调用
|
|
|
|
|
|
virtual int_type overflow(int_type ch)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ch == traits_type::eof()) {
|
|
|
|
|
|
return ch;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 1) 写到原始缓冲区 → 保留终端输出
|
|
|
|
|
|
if (m_originalBuf) {
|
|
|
|
|
|
if (m_originalBuf->sputc(static_cast<char>(ch)) == traits_type::eof()) {
|
|
|
|
|
|
return traits_type::eof();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 2) 存到我们的临时缓存,注意加锁保护
|
|
|
|
|
|
pthread_mutex_lock(&m_mutex); //防止多线程推入崩溃lnk20250305
|
|
|
|
|
|
m_buffer.push_back(static_cast<char>(ch));
|
|
|
|
|
|
// 3) 遇到换行就 flushBuffer()
|
|
|
|
|
|
if (ch == '\n') {
|
|
|
|
|
|
flushBuffer_locked();
|
|
|
|
|
|
}
|
|
|
|
|
|
pthread_mutex_unlock(&m_mutex);
|
|
|
|
|
|
return ch;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
// 内部版本:假定互斥锁已经被加锁
|
|
|
|
|
|
void flushBuffer_locked()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (m_buffer.empty()) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 根据等级和对应开关,将 m_buffer 写入相应的 list
|
|
|
|
|
|
switch (m_level) {
|
|
|
|
|
|
case LOGERROR:
|
|
|
|
|
|
if (normalOutputEnabled) {
|
|
|
|
|
|
pthread_mutex_lock(&normalListMutex);
|
|
|
|
|
|
normalList.push_back(m_buffer);
|
|
|
|
|
|
pthread_mutex_unlock(&normalListMutex);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (warnOutputEnabled) {
|
|
|
|
|
|
pthread_mutex_lock(&warnListMutex);
|
|
|
|
|
|
warnList.push_back(m_buffer);
|
|
|
|
|
|
pthread_mutex_unlock(&warnListMutex);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (errorOutputEnabled) {
|
|
|
|
|
|
pthread_mutex_lock(&errorListMutex);
|
|
|
|
|
|
errorList.push_back(m_buffer);
|
|
|
|
|
|
pthread_mutex_unlock(&errorListMutex);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case LOGWARN:
|
|
|
|
|
|
if (normalOutputEnabled) {
|
|
|
|
|
|
pthread_mutex_lock(&normalListMutex);
|
|
|
|
|
|
normalList.push_back(m_buffer);
|
|
|
|
|
|
pthread_mutex_unlock(&normalListMutex);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (warnOutputEnabled) {
|
|
|
|
|
|
pthread_mutex_lock(&warnListMutex);
|
|
|
|
|
|
warnList.push_back(m_buffer);
|
|
|
|
|
|
pthread_mutex_unlock(&warnListMutex);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case LOGNORMAL:
|
|
|
|
|
|
if (normalOutputEnabled) {
|
|
|
|
|
|
pthread_mutex_lock(&normalListMutex);
|
|
|
|
|
|
normalList.push_back(m_buffer);
|
|
|
|
|
|
pthread_mutex_unlock(&normalListMutex);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
m_buffer.clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 对外接口,内部对 m_buffer 加锁
|
|
|
|
|
|
void flushBuffer()
|
|
|
|
|
|
{
|
|
|
|
|
|
pthread_mutex_lock(&m_mutex);
|
|
|
|
|
|
flushBuffer_locked();
|
|
|
|
|
|
pthread_mutex_unlock(&m_mutex);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
// 禁止自动生成的赋值函数
|
|
|
|
|
|
TeeStreamBuf& operator=(const TeeStreamBuf&);
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
std::streambuf* m_originalBuf;
|
|
|
|
|
|
LogLevel m_level;
|
|
|
|
|
|
std::string m_buffer;
|
|
|
|
|
|
pthread_mutex_t m_mutex;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------ 全局Tee对象(避免重复赋值) ------------------
|
|
|
|
|
|
static TeeStreamBuf g_errorTeeBuf;
|
|
|
|
|
|
static TeeStreamBuf g_warnTeeBuf;
|
|
|
|
|
|
static TeeStreamBuf g_normalTeeBuf;
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------ 重定向函数 ------------------
|
|
|
|
|
|
// 只在第一次启用时,用 init(...) 初始化 TeeStreamBuf;
|
|
|
|
|
|
// 之后再启用时,不再 new 或 赋值,而是直接用之前构造好的对象。
|
|
|
|
|
|
void redirectErrorOutput(bool enabled)
|
|
|
|
|
|
{
|
|
|
|
|
|
errorOutputEnabled = enabled;
|
|
|
|
|
|
if (enabled) {
|
|
|
|
|
|
if (g_originalCerrBuf == NULL) {
|
|
|
|
|
|
g_originalCerrBuf = std::cerr.rdbuf();
|
|
|
|
|
|
g_errorTeeBuf.init(g_originalCerrBuf, LOGERROR);
|
|
|
|
|
|
}
|
|
|
|
|
|
std::cerr.rdbuf(&g_errorTeeBuf);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (g_originalCerrBuf) {
|
|
|
|
|
|
std::cerr.rdbuf(g_originalCerrBuf);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void redirectWarnOutput(bool enabled)
|
|
|
|
|
|
{
|
|
|
|
|
|
warnOutputEnabled = enabled;
|
|
|
|
|
|
if (enabled) {
|
|
|
|
|
|
if (g_originalClogBuf == NULL) {
|
|
|
|
|
|
g_originalClogBuf = std::clog.rdbuf();
|
|
|
|
|
|
g_warnTeeBuf.init(g_originalClogBuf, LOGWARN);
|
|
|
|
|
|
}
|
|
|
|
|
|
std::clog.rdbuf(&g_warnTeeBuf);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (g_originalClogBuf) {
|
|
|
|
|
|
std::clog.rdbuf(g_originalClogBuf);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void redirectNormalOutput(bool enabled)
|
|
|
|
|
|
{
|
|
|
|
|
|
normalOutputEnabled = enabled;
|
|
|
|
|
|
if (enabled) {
|
|
|
|
|
|
if (g_originalCoutBuf == NULL) {
|
|
|
|
|
|
g_originalCoutBuf = std::cout.rdbuf();
|
|
|
|
|
|
g_normalTeeBuf.init(g_originalCoutBuf, LOGNORMAL);
|
|
|
|
|
|
}
|
|
|
|
|
|
std::cout.rdbuf(&g_normalTeeBuf);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (g_originalCoutBuf) {
|
|
|
|
|
|
std::cout.rdbuf(g_originalCoutBuf);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////脚本控制
|
|
|
|
|
|
|
|
|
|
|
|
void execute_bash(string fun,int process_num,string type)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 为 char 数组分配足够的空间
|
|
|
|
|
|
char p_num_str[20];
|
|
|
|
|
|
// 使用 sprintf 转换
|
|
|
|
|
|
std::sprintf(p_num_str, "%d", process_num);
|
|
|
|
|
|
const char* script = std::string(FRONT_PATH + "/bin/set_process.sh").c_str();//使用setsid防止端口占用
|
|
|
|
|
|
const char* param1 = fun.c_str();
|
|
|
|
|
|
const char* param2 = p_num_str;
|
|
|
|
|
|
const char* param3 = type.c_str();
|
|
|
|
|
|
|
|
|
|
|
|
// 构造完整的命令
|
|
|
|
|
|
char command[256];
|
|
|
|
|
|
snprintf(command, sizeof(command), "%s %s %s %s &", script, param1, param2, param3);
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "command:" << command <<std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
// 执行命令
|
|
|
|
|
|
system(command);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////打印台账更新
|
|
|
|
|
|
|
|
|
|
|
|
void print_monitor(const ledger_monitor& mon) {
|
|
|
|
|
|
auto safe = [](const std::string& s) { return s.empty() ? "N/A" : s; };
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "Monitor ID: " << safe(mon.monitor_id) << "\n";
|
|
|
|
|
|
std::cout << "Terminal Code: " << safe(mon.terminal_code) << "\n";
|
|
|
|
|
|
std::cout << "Monitor Name: " << safe(mon.monitor_name) << "\n";
|
|
|
|
|
|
std::cout << "Logical Device Sequence: " << safe(mon.logical_device_seq) << "\n";
|
|
|
|
|
|
std::cout << "Voltage Level: " << safe(mon.voltage_level) << "\n";
|
|
|
|
|
|
std::cout << "Terminal Connect: " << safe(mon.terminal_connect) << "\n";
|
|
|
|
|
|
std::cout << "Timestamp: " << safe(mon.timestamp) << "\n";
|
|
|
|
|
|
std::cout << "Status: " << safe(mon.status) << "\n";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void print_terminal(const terminal_dev& tmnl) {
|
|
|
|
|
|
auto safe = [](const std::string& s) { return s.empty() ? "N/A" : s; };
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "GUID: " << safe(tmnl.guid) << "\n";
|
|
|
|
|
|
std::cout << "Terminal ID: " << safe(tmnl.terminal_id) << "\n";
|
|
|
|
|
|
std::cout << "Terminal Code: " << safe(tmnl.terminal_code)<< "\n";
|
|
|
|
|
|
std::cout << "Organization Name: "<< safe(tmnl.org_name) << "\n";
|
|
|
|
|
|
std::cout << "Maintenance Name: " << safe(tmnl.maint_name) << "\n";
|
|
|
|
|
|
std::cout << "Station Name: " << safe(tmnl.station_name) << "\n";
|
|
|
|
|
|
std::cout << "Factory Name: " << safe(tmnl.tmnl_factory) << "\n";
|
|
|
|
|
|
std::cout << "Terminal Status: " << safe(tmnl.tmnl_status) << "\n";
|
|
|
|
|
|
std::cout << "Device Type: " << safe(tmnl.dev_type) << "\n";
|
|
|
|
|
|
std::cout << "Device Key: " << safe(tmnl.dev_key) << "\n";
|
|
|
|
|
|
std::cout << "Device Series: " << safe(tmnl.dev_series) << "\n";
|
|
|
|
|
|
std::cout << "Address: " << safe(tmnl.addr_str) << "\n";
|
|
|
|
|
|
std::cout << "Port: " << safe(tmnl.port) << "\n";
|
|
|
|
|
|
std::cout << "Timestamp: " << safe(tmnl.timestamp) << "\n";
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < 10 && !tmnl.line[i].monitor_id.empty(); ++i) {
|
|
|
|
|
|
std::cout << " Monitor " << (i + 1) << ":\n";
|
|
|
|
|
|
print_monitor(tmnl.line[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void print_trigger_update_xml(const trigger_update_xml_t& trigger_update) {
|
|
|
|
|
|
std::cout << "Work Updates Count: " << trigger_update.work_updates.size() << "\n";
|
|
|
|
|
|
std::cout << "New Updates Count: " << trigger_update.new_updates.size() << "\n";
|
|
|
|
|
|
std::cout << "Delete Updates Count: " << trigger_update.delete_updates.size() << "\n";
|
|
|
|
|
|
std::cout << "Modify Updates Count: " << trigger_update.modify_updates.size() << "\n\n";
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "Work Updates:\n";
|
|
|
|
|
|
for (size_t i = 0; i < trigger_update.work_updates.size(); ++i) {
|
|
|
|
|
|
std::cout << "Work Update " << (i + 1) << ":\n";
|
|
|
|
|
|
print_terminal(trigger_update.work_updates[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "\nNew Updates:\n";
|
|
|
|
|
|
for (size_t i = 0; i < trigger_update.new_updates.size(); ++i) {
|
|
|
|
|
|
std::cout << "New Update " << (i + 1) << ":\n";
|
|
|
|
|
|
print_terminal(trigger_update.new_updates[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "\nDelete Updates:\n";
|
|
|
|
|
|
for (size_t i = 0; i < trigger_update.delete_updates.size(); ++i) {
|
|
|
|
|
|
std::cout << "Delete Update " << (i + 1) << ":\n";
|
|
|
|
|
|
print_terminal(trigger_update.delete_updates[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "\nModify Updates:\n";
|
|
|
|
|
|
for (size_t i = 0; i < trigger_update.modify_updates.size(); ++i) {
|
|
|
|
|
|
std::cout << "Modify Update " << (i + 1) << ":\n";
|
|
|
|
|
|
print_terminal(trigger_update.modify_updates[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////解析映射文件
|
|
|
|
|
|
|
|
|
|
|
|
//解析映射文件
|
|
|
|
|
|
bool ParseXMLConfig2(int xml_flag, XmlConfig *cfg, std::list<CTopic*> *ctopiclist, const std::string& path)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 注释同原代码
|
|
|
|
|
|
const std::string strPhasic[4] = { "A", "B", "C", "T" };
|
|
|
|
|
|
const std::string strLine[4] = { "AB", "BC", "CA", "T" };
|
|
|
|
|
|
int nStart = 1, nEnd = 1;
|
|
|
|
|
|
std::string strValueTemp;
|
|
|
|
|
|
|
|
|
|
|
|
tinyxml2::XMLDocument doc;
|
|
|
|
|
|
std::string xmlFile;
|
|
|
|
|
|
if (path == "not define") {
|
|
|
|
|
|
xmlFile = FRONT_PATH + "/etc/" + DEFAULT_CONFIG_FN; //默认映射文件
|
|
|
|
|
|
std::cout << "[调试] 加载XML路径: " << xmlFile << std::endl;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
xmlFile = FRONT_PATH + "/dat/" + path + ".xml";
|
|
|
|
|
|
std::cout << "[调试] 加载XML路径: " << xmlFile << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
auto result = doc.LoadFile(xmlFile.c_str());
|
|
|
|
|
|
if (result != tinyxml2::XML_SUCCESS) {
|
|
|
|
|
|
std::cout << "[错误] 无法打开XML文件: " << xmlFile << std::endl;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tinyxml2::XMLElement* root = doc.RootElement();
|
|
|
|
|
|
if (!root) {
|
|
|
|
|
|
std::cout << "[错误] XML无根节点!" << std::endl;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
for (tinyxml2::XMLElement* topicElem = root->FirstChildElement(); topicElem; topicElem = topicElem->NextSiblingElement()) {
|
|
|
|
|
|
std::string strTag = topicElem->Name();
|
|
|
|
|
|
if (strTag == "Topic") {
|
|
|
|
|
|
CTopic* topic = new CTopic();
|
|
|
|
|
|
topic->strTopic = topicElem->Attribute("name") ? topicElem->Attribute("name") : "";
|
|
|
|
|
|
ctopiclist->push_back(topic);
|
|
|
|
|
|
std::cout << "[调试] 解析Topic节点: " << topic->strTopic << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
// HISDATA、RTDATA
|
|
|
|
|
|
if (topic->strTopic == "HISDATA" || topic->strTopic == "RTDATA") {
|
|
|
|
|
|
for (tinyxml2::XMLElement* dataTypeElem = topicElem->FirstChildElement(); dataTypeElem; dataTypeElem = dataTypeElem->NextSiblingElement()) {
|
|
|
|
|
|
if (std::string(dataTypeElem->Name()) == "DataType") {
|
|
|
|
|
|
CDataType* dt = new CDataType();
|
|
|
|
|
|
dt->iDataType = dataTypeElem->IntAttribute("value", 0);
|
|
|
|
|
|
dt->type = dataTypeElem->Attribute("type") ? dataTypeElem->Attribute("type") : "";
|
|
|
|
|
|
dt->BaseFlag0 = 0;
|
|
|
|
|
|
dt->BaseFlag1 = 0;
|
|
|
|
|
|
topic->DataTypeList.push_back(dt);
|
|
|
|
|
|
|
|
|
|
|
|
// 监测点
|
|
|
|
|
|
for (tinyxml2::XMLElement* monitorElem = dataTypeElem->FirstChildElement(); monitorElem; monitorElem = monitorElem->NextSiblingElement()) {
|
|
|
|
|
|
if (std::string(monitorElem->Name()) == "Monitor") {
|
|
|
|
|
|
CMonitor* mt = new CMonitor();
|
|
|
|
|
|
mt->strMonitor = monitorElem->Attribute("name") ? monitorElem->Attribute("name") : "";
|
|
|
|
|
|
mt->type = monitorElem->Attribute("type") ? monitorElem->Attribute("type") : "";
|
|
|
|
|
|
dt->MonitorList.push_back(mt);
|
|
|
|
|
|
|
|
|
|
|
|
// 数据项
|
|
|
|
|
|
for (tinyxml2::XMLElement* itemElem = monitorElem->FirstChildElement(); itemElem; itemElem = itemElem->NextSiblingElement()) {
|
|
|
|
|
|
if (std::string(itemElem->Name()) == "Item") {
|
|
|
|
|
|
CItem* it = new CItem();
|
|
|
|
|
|
it->strItemName = itemElem->Attribute("name") ? itemElem->Attribute("name") : "";
|
|
|
|
|
|
it->type = itemElem->Attribute("type") ? itemElem->Attribute("type") : "";
|
|
|
|
|
|
if (it->strItemName == "FLAG")
|
|
|
|
|
|
it->strItemValue = itemElem->Attribute("value") ? itemElem->Attribute("value") : "";
|
|
|
|
|
|
mt->ItemList.push_back(it);
|
|
|
|
|
|
|
|
|
|
|
|
// Sequence
|
|
|
|
|
|
for (tinyxml2::XMLElement* seqElem = itemElem->FirstChildElement(); seqElem; seqElem = seqElem->NextSiblingElement()) {
|
|
|
|
|
|
if (std::string(seqElem->Name()) == "Sequence") {
|
|
|
|
|
|
std::string strPhase = seqElem->Attribute("value") ? seqElem->Attribute("value") : "";
|
|
|
|
|
|
|
|
|
|
|
|
// 读取ABC三相
|
|
|
|
|
|
if ((!xml_flag || it->strItemName != "V") && strPhase == "7") {
|
|
|
|
|
|
for (int n = 0; n < 3; ++n) {
|
|
|
|
|
|
CSequence* sq = new CSequence();
|
|
|
|
|
|
sq->strSValue = strPhase;
|
|
|
|
|
|
sq->type = seqElem->Attribute("type") ? seqElem->Attribute("type") : "";
|
|
|
|
|
|
sq->strSeq = strPhasic[n];
|
|
|
|
|
|
it->SequenceList.push_back(sq);
|
|
|
|
|
|
|
|
|
|
|
|
for (tinyxml2::XMLElement* valueElem = seqElem->FirstChildElement(); valueElem; valueElem = valueElem->NextSiblingElement()) {
|
|
|
|
|
|
if (std::string(valueElem->Name()) == "Value") {
|
|
|
|
|
|
std::string strDVName = valueElem->Attribute("name") ? valueElem->Attribute("name") : "";
|
|
|
|
|
|
std::string strDAName = valueElem->Attribute("DA") ? valueElem->Attribute("DA") : "";
|
|
|
|
|
|
|
|
|
|
|
|
// l_phs* → phsAB
|
|
|
|
|
|
if (strDAName.find("l_phs*") != std::string::npos) {
|
|
|
|
|
|
strDAName.replace(strDAName.find("l_phs"), 5, "phs");
|
|
|
|
|
|
size_t starPos = strDAName.find("*");
|
|
|
|
|
|
if (starPos != std::string::npos)
|
|
|
|
|
|
strDAName.replace(starPos, 1, strLine[n]);
|
|
|
|
|
|
} else if (strDAName.find("phs*") != std::string::npos) {
|
|
|
|
|
|
size_t starPos = strDAName.find("*");
|
|
|
|
|
|
if (starPos != std::string::npos)
|
|
|
|
|
|
strDAName.replace(starPos, 1, sq->strSeq);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 谐波数据
|
|
|
|
|
|
if (strDVName.find("%") != std::string::npos && strDAName.find("%-") != std::string::npos) {
|
|
|
|
|
|
size_t firstP = strDVName.find('%'), lastP = strDVName.rfind('%');
|
|
|
|
|
|
if (firstP != std::string::npos && lastP != std::string::npos && firstP != lastP) {
|
|
|
|
|
|
std::string harmRange = strDVName.substr(firstP + 1, lastP - firstP - 1); // 0,49
|
|
|
|
|
|
size_t comma = harmRange.find(',');
|
|
|
|
|
|
if (comma != std::string::npos) {
|
|
|
|
|
|
nStart = std::stoi(harmRange.substr(0, comma));
|
|
|
|
|
|
nEnd = std::stoi(harmRange.substr(comma + 1));
|
|
|
|
|
|
strValueTemp = "%" + harmRange + "%";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 取DA的范围
|
|
|
|
|
|
size_t lb = strDAName.find('['), rb = strDAName.find(']');
|
|
|
|
|
|
int strDAoffset = 0;
|
|
|
|
|
|
if (lb != std::string::npos && rb != std::string::npos && lb < rb) {
|
|
|
|
|
|
std::string sub = strDAName.substr(lb + 1, rb - lb - 1);
|
|
|
|
|
|
size_t dash = sub.find('-');
|
|
|
|
|
|
if (dash != std::string::npos)
|
|
|
|
|
|
strDAoffset = std::stoi(sub.substr(dash + 1));
|
|
|
|
|
|
}
|
|
|
|
|
|
int offset = valueElem->IntAttribute("Offset", 0);
|
|
|
|
|
|
for (int i = nStart; i <= nEnd; ++i) {
|
|
|
|
|
|
std::string strDVNameTemp = strDVName;
|
|
|
|
|
|
std::string strDANameTemp = strDAName;
|
|
|
|
|
|
CDataValue* dv1 = new CDataValue();
|
|
|
|
|
|
dv1->type = valueElem->Attribute("type") ? valueElem->Attribute("type") : "";
|
|
|
|
|
|
if (valueElem->Attribute("Coefficient")) {
|
|
|
|
|
|
dv1->strCoefficient = valueElem->Attribute("Coefficient");
|
|
|
|
|
|
dv1->fCoefficient = valueElem->FloatAttribute("Coefficient", 1.0f);
|
|
|
|
|
|
}
|
|
|
|
|
|
dv1->strOffset = valueElem->Attribute("Offset") ? valueElem->Attribute("Offset") : "";
|
|
|
|
|
|
dv1->iOffset = offset;
|
|
|
|
|
|
dv1->DO = valueElem->Attribute("DO") ? valueElem->Attribute("DO") : "";
|
|
|
|
|
|
|
|
|
|
|
|
size_t tmpPos = strDVNameTemp.find(strValueTemp);
|
|
|
|
|
|
if (tmpPos != std::string::npos)
|
|
|
|
|
|
strDVNameTemp.replace(tmpPos, strValueTemp.size(), std::to_string(i + offset));
|
|
|
|
|
|
size_t subPos = strDANameTemp.find('[');
|
|
|
|
|
|
size_t subPos2 = strDANameTemp.find(']');
|
|
|
|
|
|
if (subPos != std::string::npos && subPos2 != std::string::npos)
|
|
|
|
|
|
strDANameTemp.replace(subPos + 1, subPos2 - subPos - 1, std::to_string(i - strDAoffset));
|
|
|
|
|
|
dv1->strName = strDVNameTemp;
|
|
|
|
|
|
dv1->DA = strDANameTemp;
|
|
|
|
|
|
|
|
|
|
|
|
// BaseFlag, LimitUp, LimitDown
|
|
|
|
|
|
if (valueElem->Attribute("BaseFlag")) {
|
|
|
|
|
|
dv1->BaseFlag = valueElem->Attribute("BaseFlag");
|
|
|
|
|
|
if (dv1->BaseFlag == "1") dt->BaseFlag1++; else dt->BaseFlag0++;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
dt->BaseFlag0++;
|
|
|
|
|
|
dv1->BaseFlag = "0";
|
|
|
|
|
|
}
|
|
|
|
|
|
dv1->LimitUp = valueElem->Attribute("LimitUp") ? valueElem->Attribute("LimitUp") : "not define";
|
|
|
|
|
|
dv1->LimitDown = valueElem->Attribute("LimitDown") ? valueElem->Attribute("LimitDown") : "not define";
|
|
|
|
|
|
|
|
|
|
|
|
if (!dv1->DO.empty() && !dv1->DA.empty())
|
|
|
|
|
|
dv1->strFullName = dv1->DO + "$" + dv1->DA;
|
|
|
|
|
|
else
|
|
|
|
|
|
dv1->strFullName = "not define";
|
|
|
|
|
|
sq->DataValueList.push_back(dv1);
|
|
|
|
|
|
|
|
|
|
|
|
// 调试打印谐波展开
|
|
|
|
|
|
std::cout << "[调试] 谐波展开DataValue: " << dv1->strName << " DA=" << dv1->DA << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 非谐波数据
|
|
|
|
|
|
CDataValue* dv2 = new CDataValue();
|
|
|
|
|
|
dv2->strName = strDVName;
|
|
|
|
|
|
dv2->type = valueElem->Attribute("type") ? valueElem->Attribute("type") : "";
|
|
|
|
|
|
if (valueElem->Attribute("Coefficient")) {
|
|
|
|
|
|
dv2->strCoefficient = valueElem->Attribute("Coefficient");
|
|
|
|
|
|
dv2->fCoefficient = valueElem->FloatAttribute("Coefficient", 1.0f);
|
|
|
|
|
|
}
|
|
|
|
|
|
dv2->DO = valueElem->Attribute("DO") ? valueElem->Attribute("DO") : "";
|
|
|
|
|
|
dv2->DA = strDAName;
|
|
|
|
|
|
|
|
|
|
|
|
if (valueElem->Attribute("BaseFlag")) {
|
|
|
|
|
|
dv2->BaseFlag = valueElem->Attribute("BaseFlag");
|
|
|
|
|
|
if (dv2->BaseFlag == "1") dt->BaseFlag1++; else dt->BaseFlag0++;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
dt->BaseFlag0++;
|
|
|
|
|
|
dv2->BaseFlag = "0";
|
|
|
|
|
|
}
|
|
|
|
|
|
dv2->LimitUp = valueElem->Attribute("LimitUp") ? valueElem->Attribute("LimitUp") : "not define";
|
|
|
|
|
|
dv2->LimitDown = valueElem->Attribute("LimitDown") ? valueElem->Attribute("LimitDown") : "not define";
|
|
|
|
|
|
|
|
|
|
|
|
if (valueElem->Attribute("PltFlag") && std::string(valueElem->Attribute("PltFlag")) == "True")
|
|
|
|
|
|
dv2->bPlt = true;
|
|
|
|
|
|
else
|
|
|
|
|
|
dv2->bPlt = false;
|
|
|
|
|
|
if (!dv2->DO.empty() && !dv2->DA.empty())
|
|
|
|
|
|
dv2->strFullName = dv2->DO + "$" + dv2->DA;
|
|
|
|
|
|
else
|
|
|
|
|
|
dv2->strFullName = "not define";
|
|
|
|
|
|
sq->DataValueList.push_back(dv2);
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "[调试] 普通DataValue: " << dv2->strName << " DA=" << dv2->DA << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// T相
|
|
|
|
|
|
if (strPhase == "8") {
|
|
|
|
|
|
CSequence* sq = new CSequence();
|
|
|
|
|
|
sq->strSValue = strPhase;
|
|
|
|
|
|
sq->type = seqElem->Attribute("type") ? seqElem->Attribute("type") : "";
|
|
|
|
|
|
sq->strSeq = strPhasic[3];
|
|
|
|
|
|
it->SequenceList.push_back(sq);
|
|
|
|
|
|
for (tinyxml2::XMLElement* valueElem = seqElem->FirstChildElement(); valueElem; valueElem = valueElem->NextSiblingElement()) {
|
|
|
|
|
|
if (std::string(valueElem->Name()) == "Value") {
|
|
|
|
|
|
CDataValue* dv2 = new CDataValue();
|
|
|
|
|
|
dv2->strName = valueElem->Attribute("name") ? valueElem->Attribute("name") : "";
|
|
|
|
|
|
dv2->type = valueElem->Attribute("type") ? valueElem->Attribute("type") : "";
|
|
|
|
|
|
if (valueElem->Attribute("Coefficient")) {
|
|
|
|
|
|
dv2->strCoefficient = valueElem->Attribute("Coefficient");
|
|
|
|
|
|
dv2->fCoefficient = valueElem->FloatAttribute("Coefficient", 1.0f);
|
|
|
|
|
|
}
|
|
|
|
|
|
dv2->DO = valueElem->Attribute("DO") ? valueElem->Attribute("DO") : "";
|
|
|
|
|
|
dv2->DA = valueElem->Attribute("DA") ? valueElem->Attribute("DA") : "";
|
|
|
|
|
|
|
|
|
|
|
|
if (valueElem->Attribute("BaseFlag")) {
|
|
|
|
|
|
dv2->BaseFlag = valueElem->Attribute("BaseFlag");
|
|
|
|
|
|
if (dv2->BaseFlag == "1") dt->BaseFlag1++; else dt->BaseFlag0++;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
dt->BaseFlag0++;
|
|
|
|
|
|
dv2->BaseFlag = "0";
|
|
|
|
|
|
}
|
|
|
|
|
|
dv2->LimitUp = valueElem->Attribute("LimitUp") ? valueElem->Attribute("LimitUp") : "not define";
|
|
|
|
|
|
dv2->LimitDown = valueElem->Attribute("LimitDown") ? valueElem->Attribute("LimitDown") : "not define";
|
|
|
|
|
|
if (!dv2->DO.empty() && !dv2->DA.empty())
|
|
|
|
|
|
dv2->strFullName = dv2->DO + "$" + dv2->DA;
|
|
|
|
|
|
else
|
|
|
|
|
|
dv2->strFullName = "not define";
|
|
|
|
|
|
sq->DataValueList.push_back(dv2);
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "[调试] T相DataValue: " << dv2->strName << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// RTDATASOE
|
|
|
|
|
|
else if (topic->strTopic == "RTDATASOE") {
|
|
|
|
|
|
for (tinyxml2::XMLElement* dataTypeElem = topicElem->FirstChildElement(); dataTypeElem; dataTypeElem = dataTypeElem->NextSiblingElement()) {
|
|
|
|
|
|
if (std::string(dataTypeElem->Name()) == "DataType") {
|
|
|
|
|
|
CDataType* dt = new CDataType();
|
|
|
|
|
|
dt->iDataType = dataTypeElem->IntAttribute("value", 0);
|
|
|
|
|
|
dt->type = dataTypeElem->Attribute("type") ? dataTypeElem->Attribute("type") : "";
|
|
|
|
|
|
topic->DataTypeList.push_back(dt);
|
|
|
|
|
|
|
|
|
|
|
|
for (tinyxml2::XMLElement* soeElem = dataTypeElem->FirstChildElement(); soeElem; soeElem = soeElem->NextSiblingElement()) {
|
|
|
|
|
|
if (std::string(soeElem->Name()) == "SOE") {
|
|
|
|
|
|
CEventData* ed = new CEventData();
|
|
|
|
|
|
ed->triggerFlag = soeElem->Attribute("TriggerFlag") ? soeElem->Attribute("TriggerFlag") : "";
|
|
|
|
|
|
ed->DO = soeElem->Attribute("DO") ? soeElem->Attribute("DO") : "";
|
|
|
|
|
|
ed->DA = soeElem->Attribute("DA") ? soeElem->Attribute("DA") : "";
|
|
|
|
|
|
ed->type = soeElem->Attribute("type") ? soeElem->Attribute("type") : "";
|
|
|
|
|
|
if (!ed->DO.empty() && !ed->DA.empty())
|
|
|
|
|
|
ed->strFullName = ed->DO + "$" + ed->DA;
|
|
|
|
|
|
else
|
|
|
|
|
|
ed->strFullName = "not define";
|
|
|
|
|
|
dt->SOEList.push_back(ed);
|
|
|
|
|
|
std::cout << "[调试] SOE事件: " << ed->strFullName << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// SOEDATA
|
|
|
|
|
|
else if (topic->strTopic == "SOEDATA") {
|
|
|
|
|
|
for (tinyxml2::XMLElement* dataTypeElem = topicElem->FirstChildElement(); dataTypeElem; dataTypeElem = dataTypeElem->NextSiblingElement()) {
|
|
|
|
|
|
if (std::string(dataTypeElem->Name()) == "DataType") {
|
|
|
|
|
|
CEventData* ed = new CEventData();
|
|
|
|
|
|
ed->triggerFlag = dataTypeElem->Attribute("name") ? dataTypeElem->Attribute("name") : "";
|
|
|
|
|
|
ed->DO = dataTypeElem->Attribute("DO") ? dataTypeElem->Attribute("DO") : "";
|
|
|
|
|
|
ed->DA = dataTypeElem->Attribute("DA") ? dataTypeElem->Attribute("DA") : "";
|
|
|
|
|
|
ed->type = dataTypeElem->Attribute("type") ? dataTypeElem->Attribute("type") : "";
|
|
|
|
|
|
if (!ed->DO.empty() && !ed->DA.empty())
|
|
|
|
|
|
ed->strFullName = ed->DO + "$" + ed->DA;
|
|
|
|
|
|
else
|
|
|
|
|
|
ed->strFullName = "not define";
|
|
|
|
|
|
cfg->SOEList.push_back(ed);
|
|
|
|
|
|
std::cout << "[调试] SOEDATA事件: " << ed->strFullName << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 其他节点
|
|
|
|
|
|
else if (strTag == "WavePhasic") {
|
|
|
|
|
|
const char* flagAttr = topicElem->Attribute("Flag");
|
|
|
|
|
|
if (flagAttr)
|
|
|
|
|
|
cfg->WavePhasicFlag = flagAttr;
|
|
|
|
|
|
if (!cfg->WavePhasicFlag.empty() && cfg->WavePhasicFlag == "1") {
|
|
|
|
|
|
const char* aAttr = topicElem->Attribute("A");
|
|
|
|
|
|
if (aAttr) cfg->WavePhasicA = aAttr;
|
|
|
|
|
|
const char* bAttr = topicElem->Attribute("B");
|
|
|
|
|
|
if (bAttr) cfg->WavePhasicB = bAttr;
|
|
|
|
|
|
const char* cAttr = topicElem->Attribute("C");
|
|
|
|
|
|
if (cAttr) cfg->WavePhasicC = cAttr;
|
|
|
|
|
|
}
|
|
|
|
|
|
std::cout << "[调试] WavePhasic解析" << std::endl;
|
|
|
|
|
|
} else if (strTag == "UnitOfTime") {
|
|
|
|
|
|
const char* unitAttr = topicElem->Attribute("Unit");
|
|
|
|
|
|
if (unitAttr)
|
|
|
|
|
|
cfg->UnitOfTimeUnit = unitAttr;
|
|
|
|
|
|
std::cout << "[调试] UnitOfTime解析" << std::endl;
|
|
|
|
|
|
} else if (strTag == "ValueOfTime") {
|
|
|
|
|
|
const char* unitAttr = topicElem->Attribute("Unit");
|
|
|
|
|
|
if (unitAttr)
|
|
|
|
|
|
cfg->ValueOfTimeUnit = unitAttr;
|
|
|
|
|
|
std::cout << "[调试] ValueOfTime解析" << std::endl;
|
|
|
|
|
|
} else if (strTag == "ComtradeFile") {
|
|
|
|
|
|
const char* waveTimeFlag = topicElem->Attribute("WaveTimeFlag");
|
|
|
|
|
|
if (waveTimeFlag)
|
|
|
|
|
|
cfg->WaveTimeFlag = waveTimeFlag;
|
|
|
|
|
|
std::cout << "[调试] ComtradeFile解析" << std::endl;
|
|
|
|
|
|
} else if (strTag == "IED") {
|
|
|
|
|
|
const char* nameAttr = topicElem->Attribute("name");
|
|
|
|
|
|
if (nameAttr)
|
|
|
|
|
|
cfg->IEDname = nameAttr;
|
|
|
|
|
|
std::cout << "[调试] IED解析" << std::endl;
|
|
|
|
|
|
} else if (strTag == "LDevice") {
|
|
|
|
|
|
const char* prefixAttr = topicElem->Attribute("Prefix");
|
|
|
|
|
|
if (prefixAttr)
|
|
|
|
|
|
cfg->LDevicePrefix = prefixAttr;
|
|
|
|
|
|
std::cout << "[调试] LDevice解析" << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//清理旧的映射配置
|
|
|
|
|
|
void clearXmlConfigAndTopicList(Xmldata* data) {
|
|
|
|
|
|
// 清空 XmlConfig
|
|
|
|
|
|
data->xmlcfg = XmlConfig(); // 通过重新赋值重置 xmlcfg
|
|
|
|
|
|
|
|
|
|
|
|
// 清空 topicList
|
|
|
|
|
|
std::list<CTopic*>::iterator it;
|
|
|
|
|
|
for (it = data->topicList.begin(); it != data->topicList.end(); ++it) {
|
|
|
|
|
|
delete *it; // 释放内存
|
|
|
|
|
|
}
|
|
|
|
|
|
data->topicList.clear(); // 清空链表
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//配置映射文件单个
|
|
|
|
|
|
void Set_xml_nodeinfo_one(const std::string& dev_type)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 假定 xmlinfo_list 是 std::map<std::string, Xmldata*>
|
|
|
|
|
|
if (xmlinfo_list.count(dev_type) && xmlinfo_list[dev_type] != nullptr) { // 已存在这个类型的节点
|
|
|
|
|
|
if (xmlinfo_list[dev_type]->updataflag == true) { // 需要更新
|
|
|
|
|
|
|
|
|
|
|
|
// 删除这个点的 xmlcfg 和 topicList
|
|
|
|
|
|
clearXmlConfigAndTopicList(xmlinfo_list[dev_type]);
|
|
|
|
|
|
|
|
|
|
|
|
ret = ParseXMLConfig2(
|
|
|
|
|
|
0,
|
|
|
|
|
|
&(xmlinfo_list[dev_type]->xmlcfg),
|
|
|
|
|
|
&(xmlinfo_list[dev_type]->topicList),
|
|
|
|
|
|
xmlinfo_list[dev_type]->xmlbase.MODEL_ID
|
|
|
|
|
|
);
|
|
|
|
|
|
if (!ret) {
|
|
|
|
|
|
std::cout << "!!!! this ledger xml config fail!!!!" << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else {
|
|
|
|
|
|
std::cout << "xmlinfo_list not contain this devtype" << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理角形
|
|
|
|
|
|
if (isdelta_flag) {
|
|
|
|
|
|
if (xmlinfo_list2.count(dev_type) && xmlinfo_list2[dev_type] != nullptr) { // 已存在
|
|
|
|
|
|
if (xmlinfo_list2[dev_type]->updataflag == true) { // 需要更新
|
|
|
|
|
|
|
|
|
|
|
|
// 删除这个点的 xmlcfg 和 topicList
|
|
|
|
|
|
clearXmlConfigAndTopicList(xmlinfo_list2[dev_type]);
|
|
|
|
|
|
|
|
|
|
|
|
ret = ParseXMLConfig2(
|
|
|
|
|
|
1,
|
|
|
|
|
|
&(xmlinfo_list2[dev_type]->xmlcfg),
|
|
|
|
|
|
&(xmlinfo_list2[dev_type]->topicList),
|
|
|
|
|
|
xmlinfo_list2[dev_type]->xmlbase.MODEL_ID
|
|
|
|
|
|
);
|
|
|
|
|
|
if (!ret) {
|
|
|
|
|
|
std::cout << "!!!! this ledger xml config fail!!!!" << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else {
|
|
|
|
|
|
std::cout << "xmlinfo_list2 not contain this devtype" << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//配置映射文件全部
|
|
|
|
|
|
void Set_xml_nodeinfo()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 配置无对应 xml 文件时的默认解析配置
|
|
|
|
|
|
if (!DEFAULT_CONFIG_FN.empty()) { //可改为在配置中读取默认映射文件名
|
|
|
|
|
|
std::string path = "not define";
|
|
|
|
|
|
ParseXMLConfig2(0, &xmlcfg, &topicList, path); // 调用 ParseXMLConfig() 解析 JiangSu_Config.xml 配置文件
|
|
|
|
|
|
if (isdelta_flag) {
|
|
|
|
|
|
ParseXMLConfig2(1, &xmlcfg2, &topicList2, path); // 角型接线
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!xmlinfo_list.empty()) {
|
|
|
|
|
|
std::cout << "!!!!!!!!!! xmlinfo_list.size() != 0 !!!!!!!!!!!" << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
for (auto& pair : xmlinfo_list) {
|
|
|
|
|
|
const std::string& key = pair.first;
|
|
|
|
|
|
Xmldata* value = pair.second;
|
|
|
|
|
|
|
|
|
|
|
|
if (value && value->updataflag) {
|
|
|
|
|
|
ParseXMLConfig2(0, &(value->xmlcfg), &(value->topicList), value->xmlbase.MODEL_ID);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (isdelta_flag) {
|
|
|
|
|
|
for (auto& pair : xmlinfo_list2) {
|
|
|
|
|
|
const std::string& key = pair.first;
|
|
|
|
|
|
Xmldata* value = pair.second;
|
|
|
|
|
|
|
|
|
|
|
|
if (value && value->updataflag) {
|
|
|
|
|
|
ParseXMLConfig2(1, &(value->xmlcfg), &(value->topicList), value->xmlbase.MODEL_ID);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
std::cout << "!!!!!!!!!! xmlinfo_list.size() == 0 !!!!!!!!!!!" << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|