20 Commits

Author SHA1 Message Date
lnk
9f74ada894 pqdif进程跳过xml和报告初始化 2026-07-20 16:29:23 +08:00
lnk
86dc51d066 添加单点补招pqdif文件的逻辑 2026-07-17 16:25:44 +08:00
lnk
abc5f9e735 添加pqdif处理函数 2026-07-17 15:58:19 +08:00
lnk
0e2ade351c 台账更新添加comtype 2026-07-17 13:57:45 +08:00
lnk
fe16beb188 初始化添加pqdif判断,新增pqdif进程 2026-07-17 11:15:16 +08:00
lnk
45d139e628 最多只记录一次原始数据 2026-07-14 14:20:23 +08:00
lnk
111ffa36bd 数据追踪原始报告块 2026-07-07 16:32:48 +08:00
lnk
d78cef25a0 数据追踪增加接线方式和来源 2026-07-03 14:43:13 +08:00
lnk
c0860a48d6 长闪不发空数据 2026-06-25 10:04:26 +08:00
lnk
62a9742287 长闪不发送空数据 2026-06-24 16:39:35 +08:00
lnk
c221102a5a 优化波形文件匹配逻辑和日志提示 2026-06-12 15:26:19 +08:00
lnk
d3579a2aa7 完成数据追踪原始数据上送 2026-06-09 16:13:52 +08:00
lnk
cd92ea09f1 注释不必要的mq发送 2026-06-08 10:50:03 +08:00
lnk
465e304085 添加错误映射文件的处理防止崩溃,优化mq处理topic前缀,添加空指针预防 2026-06-02 16:23:23 +08:00
lnk
c388bd04fe remove file after upload 2026-05-22 11:34:14 +08:00
lnk
c6ca57a204 添加装置重启功能和控制位写入功能 2026-05-14 16:16:16 +08:00
lnk
2f584fda30 添加文件删除和复位代码 2026-05-09 16:34:14 +08:00
lnk
2f2e0d6430 文件目录和下载请求功能自测通过 2026-05-07 16:48:26 +08:00
lnk
fc861024c3 fix bug in test mq 2026-04-30 16:06:29 +08:00
lnk
3c98bf7eae fix bug in mq C++ 2026-04-30 15:45:34 +08:00
21 changed files with 6500 additions and 409 deletions

View File

@@ -45,11 +45,15 @@
using namespace std;
static std::once_flag g_consumer_once;
static std::once_flag g_producer_once;
extern std::string G_ROCKETMQ_PRODUCER;//rocketmq producer
extern std::string G_ROCKETMQ_IPPORT;//rocketmq ip+port
extern std::string G_ROCKETMQ_TOPIC;//topie
extern std::string G_ROCKETMQ_TAG;//tag
extern std::string G_ROCKETMQ_KEY;//key
extern std::string G_ROCKETMQ_TOPIC_TEST;//topie
extern std::string G_ROCKETMQ_TAG_TEST;//tag
extern std::string G_ROCKETMQ_KEY_TEST;//key
extern std::string G_MQCONSUMER_TOPIC_LOG;
extern std::string G_MQCONSUMER_TOPIC_SET;
@@ -57,6 +61,8 @@ extern std::string G_MQCONSUMER_TOPIC_RC;
extern std::string G_MQCONSUMER_TOPIC_UD;
extern std::string G_MQCONSUMER_TOPIC_RT;
extern std::string G_MQCONSUMER_TOPIC_TEST;
extern std::string FRONT_INST;
extern bool DEBUGOPEN;
@@ -110,8 +116,8 @@ public:
RocketMQConsumer(const std::string& consumerName, const std::string& nameServer);
// 禁用拷贝和赋值
//RocketMQConsumer(const RocketMQConsumer&) {}
RocketMQConsumer& operator=(const RocketMQConsumer&) { return *this; }
RocketMQConsumer(const RocketMQConsumer&) = delete;
RocketMQConsumer& operator=(const RocketMQConsumer&) = delete;
// 订阅主题和标签,并注册回调
void subscribe(const std::string& topic,
@@ -227,24 +233,25 @@ RocketMQConsumer::RocketMQConsumer(const std::string& consumerGroup,
G_MQCONSUMER_CHANNEL
);
// 限制消费线程池,防止 ConsumeTP 爆炸
consumer_.setConsumeThreadCount(4);
listener_ = new InternalListener(this);
}
// 启动消费者
void RocketMQConsumer::start()
{
/*if (StartPushConsumer(consumer_) != 0) {
pthread_mutex_lock(&g_consumerMapMutex);
g_consumerMap.erase(consumer_);
pthread_mutex_unlock(&g_consumerMapMutex);
DestroyPushConsumer(consumer_);
throw std::runtime_error("Failed to start push consumer.");
static bool started = false;
if (started) {
std::cout << "Consumer already started" << std::endl;
return;
}
else{
std::cout << "RocketMQ Consumer started." << std::endl;
}*/
consumer_.registerMessageListener(listener_);
consumer_.start();
started = true;
}
void RocketMQConsumer::subscribe(const std::string& topic, const std::string& tag, MessageCallBack callback)
@@ -260,6 +267,23 @@ void RocketMQConsumer::subscribe(const std::string& topic, const std::string& ta
// 使用 std::pair 作为键
std::pair<std::string, std::string> mapKey(topic, tag);
callbacks_[mapKey] = callback;
//冀北添加,其他地方也通用
// 如果带 MQ_INST_xxx% 前缀再注册一个短topic
size_t pos = topic.find('%');
if (pos != std::string::npos)
{
std::string shortTopic = topic.substr(pos + 1);
callbacks_[std::make_pair(shortTopic, tag)] = callback;
std::cout << "[CALLBACK_ALIAS] "
<< topic
<< " -> "
<< shortTopic
<< ", tag=" << tag
<< std::endl;
}
}
/*
// 静态消息处理回调实现
@@ -361,6 +385,8 @@ RocketMQConsumer::~RocketMQConsumer()
} catch (...) {
}
sleep(1); // 等内部线程退出
delete listener_;
listener_ = NULL;
@@ -434,16 +460,13 @@ void rocketmq_consumer_receive(
const std::string& nameServer,
const std::vector<Subscription>& subscriptions) // 接收多个订阅
{
if (g_consumer == NULL) {
std::call_once(g_consumer_once, [&](){
try {
//InitializeConsumer(consumerName, nameServer, topic, tag, callback);//初始化后mq库内部来完成消息的获取
InitializeConsumer(consumerName, nameServer, subscriptions); // 初始化后MQ库内部开始获取消息
}
catch (...) {
InitializeConsumer(consumerName, nameServer, subscriptions);
} catch (...) {
std::cerr << "Cannot consume message because consumer initialization failed." << std::endl;
return;
}
}
});
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -507,6 +530,8 @@ public:
//SetProducerMaxMessageSize(producer_, 1024 * 1024); // 1MB
producer_.setMaxMessageSize(1024 * 1024);
producer_.setSendMsgTimeout(3000);//添加超时防阻塞
//SetProducerSessionCredentials(producer_, G_MQCONSUMER_ACCESSKEY.c_str(),G_MQCONSUMER_SECRETKEY.c_str(), "");
producer_.setSessionCredentials(
G_MQCONSUMER_ACCESSKEY,
@@ -848,14 +873,9 @@ void rocketmq_producer_send(const std::string& body,
const std::string& tags,
const std::string& keys)
{
if (g_producer == NULL) {
try {
std::call_once(g_producer_once, [&](){
InitializeProducer();
} catch (...) {
std::cerr << "Cannot send message because producer initialization failed." << std::endl;
return;
}
}
});
try {
g_producer->sendMessage(body, topic, tags, keys);
@@ -876,9 +896,9 @@ void StartSendMessage(CProducer* producer)
CSendResult result;
// create message and set some values for it
CMessage* msg = CreateMessage(G_ROCKETMQ_TOPIC.c_str());
SetMessageTags(msg, G_ROCKETMQ_TAG.c_str());
SetMessageKeys(msg, G_ROCKETMQ_KEY.c_str());
CMessage* msg = CreateMessage(G_ROCKETMQ_TOPIC_TEST.c_str());
SetMessageTags(msg, G_ROCKETMQ_TAG_TEST.c_str());
SetMessageKeys(msg, G_ROCKETMQ_KEY_TEST.c_str());
for (int i = 0; i < 10; i++)
{
@@ -902,9 +922,9 @@ void StartSendMessage(CProducer* producer,const char* strbody)
CSendResult result;
// create message and set some values for it
CMessage* msg = CreateMessage(G_ROCKETMQ_TOPIC.c_str());
SetMessageTags(msg, G_ROCKETMQ_TAG.c_str());
SetMessageKeys(msg, G_ROCKETMQ_KEY.c_str());
CMessage* msg = CreateMessage(G_ROCKETMQ_TOPIC_TEST.c_str());
SetMessageTags(msg, G_ROCKETMQ_TAG_TEST.c_str());
SetMessageKeys(msg, G_ROCKETMQ_KEY_TEST.c_str());
SetMessageBody(msg, strbody);
// send message
@@ -963,13 +983,13 @@ void rocketmq_test_rt()
{
Ckafka_data_t data;
data.monitor_id = 123123;
data.strTopic = QString::fromStdString(std::string(FRONT_INST) + "_" + G_MQCONSUMER_TOPIC_RT);
data.strTopic = QString::fromStdString(G_MQCONSUMER_TOPIC_RT);
std::ifstream file("rt.txt"); // 文件中存储长字符串
std::stringstream buffer;
buffer << file.rdbuf(); // 读取整个文件内容
data.strText = QString::fromStdString(buffer.str());
data.mp_id = 123123;
data.mp_id = QString::number(123456);
my_rocketmq_send(data);
}
//extern std::string G_MQCONSUMER_TOPIC_UD;
@@ -977,13 +997,13 @@ void rocketmq_test_ud()//用来测试台账更新
{
Ckafka_data_t data;
data.monitor_id = 123123;
data.strTopic = QString::fromStdString(std::string(FRONT_INST) + "_" + G_MQCONSUMER_TOPIC_UD);
data.strTopic = QString::fromStdString(G_MQCONSUMER_TOPIC_UD);
std::ifstream file("ud.txt"); // 文件中存储长字符串
std::stringstream buffer;
buffer << file.rdbuf(); // 读取整个文件内容
data.strText = QString::fromStdString(buffer.str());
data.mp_id = 123123;
data.mp_id = QString::number(123456);
my_rocketmq_send(data);
}
@@ -991,13 +1011,13 @@ void rocketmq_test_set()//用来测试进程控制脚本
{
Ckafka_data_t data;
data.monitor_id = 123123;
data.strTopic = QString::fromStdString(std::string(FRONT_INST) + "_" + G_MQCONSUMER_TOPIC_SET);
data.strTopic = QString::fromStdString(G_MQCONSUMER_TOPIC_SET);
std::ifstream file("set.txt"); // 文件中存储长字符串
std::stringstream buffer;
buffer << file.rdbuf(); // 读取整个文件内容
data.strText = QString::fromStdString(buffer.str());
data.mp_id = 123123;
data.mp_id = QString::number(123456);
my_rocketmq_send(data);
}
@@ -1005,13 +1025,13 @@ void rocketmq_test_only()//用来测试进程控制脚本
{
Ckafka_data_t data;
data.monitor_id = 123123;
data.strTopic = QString::fromStdString(std::string(FRONT_INST) + "_" + G_MQCONSUMER_TOPIC_SET);
std::ifstream file("set_debug.txt"); // 文件中存储长字符串
data.strTopic = QString::fromStdString(G_MQCONSUMER_TOPIC_TEST);
std::ifstream file("test.txt"); // 文件中存储长字符串
std::stringstream buffer;
buffer << file.rdbuf(); // 读取整个文件内容
data.strText = QString::fromStdString(buffer.str());
data.mp_id = 123123;
data.mp_id = QString::number(123456);
my_rocketmq_send(data);
}
@@ -1020,13 +1040,13 @@ void rocketmq_test_rc()
{
Ckafka_data_t data;
data.monitor_id = 123123;
data.strTopic = QString::fromStdString(std::string(FRONT_INST) + "_" + G_MQCONSUMER_TOPIC_RC);
data.strTopic = QString::fromStdString(G_MQCONSUMER_TOPIC_RC);
std::ifstream file("rc.txt"); // 文件中存储长字符串
std::stringstream buffer;
buffer << file.rdbuf(); // 读取整个文件内容
data.strText = QString::fromStdString(buffer.str());
data.mp_id = 123123;
data.mp_id = QString::number(123456);
my_rocketmq_send(data);
}
@@ -1035,13 +1055,13 @@ void rocketmq_test_log()
{
Ckafka_data_t data;
data.monitor_id = 123123;
data.strTopic = QString::fromStdString(std::string(FRONT_INST) + "_" + G_MQCONSUMER_TOPIC_LOG);
data.strTopic = QString::fromStdString(G_MQCONSUMER_TOPIC_LOG);
std::ifstream file("log_test.txt"); // 文件中存储长字符串
std::stringstream buffer;
buffer << file.rdbuf(); // 读取整个文件内容
data.strText = QString::fromStdString(buffer.str());
data.mp_id = 123123;
data.mp_id = QString::number(123456);
my_rocketmq_send(data);
}

View File

@@ -156,6 +156,7 @@ public:
char dev_type[64];
char dev_key[255];
char dev_series[255];
char com_type[32];
char addr_str[64];
char port[64];
char timestamp[64];
@@ -280,9 +281,9 @@ extern int g_front_seg_num;
//生产者
std::string G_ROCKETMQ_PRODUCER = "";//rocketmq producer
std::string G_ROCKETMQ_IPPORT = "";//rocketmq ip+port
std::string G_ROCKETMQ_TOPIC = "";//topie
std::string G_ROCKETMQ_TAG = "";//tag
std::string G_ROCKETMQ_KEY = "";//key
std::string G_ROCKETMQ_TOPIC_TEST = "";//topie
std::string G_ROCKETMQ_TAG_TEST = "";//tag
std::string G_ROCKETMQ_KEY_TEST = "";//key
int QUEUENUM = 0;
std::string BROKERNAME = "";
//消费者
@@ -321,12 +322,22 @@ std::string Heart_Beat_Key = "";
std::string Topic_Reply_Topic = "";
std::string Topic_Reply_Tag = "";
std::string Topic_Reply_Key = "";
//数据追踪
std::string DATA_TRACE_TOPIC = "";
std::string DATA_TRACE_TAG = "";
std::string DATA_TRACE_KEY = "";
//lnk20260310添加文件管理的topic和tag
std::string G_MQCONSUMER_TOPIC_FILE = "";//consumer topie
std::string G_MQCONSUMER_TAG_FILE = "";//consumer tag
std::string G_MQCONSUMER_KEY_FILE = "";//consumer key
std::string G_REPLY_TOPIC_FILE = "";//consumer topie
std::string G_REPLY_TAG_FILE = "";//consumer tag
std::string G_REPLY_KEY_FILE = "";//consumer key
std::string G_MQCONSUMER_TOPIC_TEST = "";
int G_TEST_FLAG = 0;
int G_TEST_NUM = 0;
int G_TEST_TYPE = 0;
@@ -644,12 +655,12 @@ void init_config() {
G_ROCKETMQ_PRODUCER = strdup(ba.data());
ba = settings.value("RocketMq/Ipport", "").toString().toLatin1();
G_ROCKETMQ_IPPORT = strdup(ba.data());
ba = settings.value("RocketMq/Topic", "").toString().toLatin1();
G_ROCKETMQ_TOPIC = strdup(ba.data());
ba = settings.value("RocketMq/Tag", "").toString().toLatin1();
G_ROCKETMQ_TAG = strdup(ba.data());
ba = settings.value("RocketMq/Key", "").toString().toLatin1();
G_ROCKETMQ_KEY = strdup(ba.data());
ba = settings.value("RocketMq/TESTTopic", "").toString().toLatin1();
G_ROCKETMQ_TOPIC_TEST = strdup(ba.data());
ba = settings.value("RocketMq/TESTTag", "").toString().toLatin1();
G_ROCKETMQ_TAG_TEST = strdup(ba.data());
ba = settings.value("RocketMq/TESTKey", "").toString().toLatin1();
G_ROCKETMQ_KEY_TEST = strdup(ba.data());
QUEUENUM = settings.value("RocketMq/Queuenum", 0).toInt();
//心跳
@@ -667,6 +678,13 @@ void init_config() {
ba = settings.value("RocketMq/Topic_Reply_Key", "").toString().toLatin1();
Topic_Reply_Key = strdup(ba.data());
//数据追踪
ba = settings.value("RocketMq/TraceTopic", "").toString().toLatin1();
DATA_TRACE_TOPIC = strdup(ba.data());
ba = settings.value("RocketMq/TraceTag", "").toString().toLatin1();
DATA_TRACE_TAG = strdup(ba.data());
ba = settings.value("RocketMq/TraceKey", "").toString().toLatin1();
DATA_TRACE_KEY = strdup(ba.data());
//消费者
ba = settings.value("RocketMq/consumer", "").toString().toLatin1();
@@ -724,12 +742,21 @@ void init_config() {
G_CONNECT_KEY = strdup(ba.data());
//lnk20260310添加文件管理的topic和tag
ba = settings.value("RocketMq/ConsumerTopicFile", "").toString().toLatin1();
ba = settings.value("RocketMq/ConsumerTopicFILE", "").toString().toLatin1();
G_MQCONSUMER_TOPIC_FILE = strdup(ba.data());
ba = settings.value("RocketMq/ConsumerTagFile", "").toString().toLatin1();
ba = settings.value("RocketMq/ConsumerTagFILE", "").toString().toLatin1();
G_MQCONSUMER_TAG_FILE = strdup(ba.data());
ba = settings.value("RocketMq/ConsumerKeyFile", "").toString().toLatin1();
ba = settings.value("RocketMq/ConsumerKeyFILE", "").toString().toLatin1();
G_MQCONSUMER_KEY_FILE = strdup(ba.data());
ba = settings.value("RocketMq/ReplyTopicFILE", "").toString().toLatin1();
G_REPLY_TOPIC_FILE = strdup(ba.data());
ba = settings.value("RocketMq/ReplyTagFILE", "").toString().toLatin1();
G_REPLY_TAG_FILE = strdup(ba.data());
ba = settings.value("RocketMq/ReplyKeyFILE", "").toString().toLatin1();
G_REPLY_KEY_FILE = strdup(ba.data());
ba = settings.value("RocketMq/ConsumerTopicTEST", "").toString().toLatin1();
G_MQCONSUMER_TOPIC_TEST = strdup(ba.data());
//MQ测试
@@ -747,9 +774,9 @@ void init_config() {
//生产者相关打印
std::cout << "Read G_ROCKETMQ_PRODUCER:" << G_ROCKETMQ_PRODUCER << std::endl;
std::cout << "Read G_ROCKETMQ_IPPORT:" << G_ROCKETMQ_IPPORT << std::endl;
std::cout << "Read G_ROCKETMQ_TOPIC:" << G_ROCKETMQ_TOPIC << std::endl;
std::cout << "Read G_ROCKETMQ_TAG:" << G_ROCKETMQ_TAG << std::endl;
std::cout << "Read G_ROCKETMQ_KEY:" << G_ROCKETMQ_KEY << std::endl;
std::cout << "Read G_ROCKETMQ_TOPIC_TEST:" << G_ROCKETMQ_TOPIC_TEST << std::endl;
std::cout << "Read G_ROCKETMQ_TAG_TEST:" << G_ROCKETMQ_TAG_TEST << std::endl;
std::cout << "Read G_ROCKETMQ_KEY_TEST:" << G_ROCKETMQ_KEY_TEST << std::endl;
std::cout << "Read QUEUENUM:" << QUEUENUM << std::endl;
std::cout << "Read G_LOG_TOPIC:" << G_LOG_TOPIC << std::endl;
std::cout << "Read G_LOG_TAG:" << G_LOG_TAG << std::endl;
@@ -834,6 +861,9 @@ void init_config() {
else if (g_node_id == SOE_COMTRADE_BASE_NODE_ID) {//暂态录波
TEST_PORT = TEST_PORT + SOE_COMTRADE_BASE_NODE_ID + g_front_seg_index;
}
else if (g_node_id == PQDIF_DATA_BASE_NODE_ID) {
TEST_PORT = TEST_PORT + PQDIF_DATA_BASE_NODE_ID + g_front_seg_index;
}
}
@@ -1512,8 +1542,8 @@ int parse_rpt_log_ini()
}
for (cpuno = 0; cpuno < ied->cpucount; cpuno++) {
LD_info = &(ied_usr->LD_info[cpuno]);
for (cpuno = 0; cpuno < ied->cpucount; cpuno++) {//遍历所有测点但是某些测点可能为空台账初始化时ied->cpucount取的是测点号
LD_info = &(ied_usr->LD_info[cpuno]);//测点号从1开始cpuno从0开始所以ied_usr->LD_info[cpuno]对应测点号为cpuno+1注意台账初始化时ied_usr->LD_info数组的大小是按照测点总数分配的所以有些测点号可能没有对应的LD_info添加判断防止崩溃
//char str[256]; //256大小
char* tmp = Get_IED(ied_usr->dev_type);
if(tmp == NULL){std::cerr << "front read ied config error!" << std::endl;continue;}
@@ -1527,10 +1557,21 @@ int parse_rpt_log_ini()
//调试用
printf("%s使用内存地址 LD_name[%d]: %p\n", ied_usr->terminal_id, cpuno, (void*)ied_usr->LD_info[cpuno].LD_name);
//添加判断有的监测点没有cpuno为2直接申请了LD_info[1]没申请LD_info[0]
if(ied_usr->LD_info[cpuno].LD_name == NULL){
//添加判断,比如有的监测点没有cpuno为2直接申请了LD_info[1]没申请LD_info[0]
if(ied_usr->LD_info[cpuno].LD_name == NULL){ //空说明台账初始化没申请空间
printf("this ld_info didn't palloc space ,maybe this ledger has problem!");
DIY_ERRORLOG_CODE("process",0,LOG_CODE_RPTINIT,"【ERROR】终端%s的监测点序号为%d的监测点无法初始化报告,这个装置的台账存在缺失,请检查装置台账的监测点总数和各监测点的序号",ied_usr->terminal_id,cpuno + 1);
//添加默认值防止崩溃20260601
LD_info->rptcount = 0;
LD_info->logcount = 0;
LD_info->cpuno = 0;
LD_info->LD_name = NULL;
LD_info->rptinfo = NULL;
LD_info->loginfo = NULL;
delete[] tmp;//释放内存
continue;//跳过防止崩溃
}
@@ -1544,7 +1585,19 @@ int parse_rpt_log_ini()
apr_snprintf(buf, sizeof(buf), "%s", rpt_cfg_strlists[type]->at(i).toAscii().constData());
fill_rptctrl_by_cfg(LD_info, i, buf);
//fill_rptctrl_by_cfg(LD_info, i, buf);
int ret = fill_rptctrl_by_cfg(LD_info, i, buf);
if (ret != 0) {
printf("[RPT_INIT][ERROR] fill_rptctrl_by_cfg failed dev=%s cpu=%d rpt=%d cfg=%s\n",
ied_usr->terminal_id, cpuno, i, buf);
DIY_ERRORLOG_CODE(ied_usr->terminal_id,1,LOG_CODE_RPTINIT,"【ERROR】终端%s的监测点序号为%d的监测点的第%d个报告配置解析失败请检查报告配置格式是否正确",ied_usr->terminal_id, cpuno + 1, i + 1);
if (LD_info->rptinfo && LD_info->rptinfo[i]) {
LD_info->rptinfo[i]->rptID = NULL;
LD_info->rptinfo[i]->LD_info = NULL;
}
}
}
@@ -1651,6 +1704,8 @@ void add_terminal_to_trigger_update(trigger_update_xml_t* trigger_update_xml, co
}
}
static bool terminal_update_should_bind_to_current_process(const terminal* dev);
// 解析 XML 数据并提取 terminal 信息
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 work_terminal = {}; // 创建新的 terminal 对象
@@ -1666,6 +1721,13 @@ void parse_terminal_from_data(trigger_update_xml_t* trigger_update_xml, const st
strcpy(work_terminal.dev_type, extract_value(data, "devType").c_str());
strcpy(work_terminal.dev_key, extract_value(data, "devKey").c_str());
strcpy(work_terminal.dev_series, extract_value(data, "series").c_str());
std::string com_type = extract_value(data, "comType");
if (com_type.empty())
com_type = extract_value(data, "com_type");
if (com_type.empty())
com_type = "MMS";
strncpy(work_terminal.com_type, com_type.c_str(), sizeof(work_terminal.com_type) - 1);
work_terminal.com_type[sizeof(work_terminal.com_type) - 1] = '\0';
strcpy(work_terminal.processNo, extract_value(data, "processNo").c_str());
strcpy(work_terminal.addr_str, extract_value(data, "ip").c_str());
strcpy(work_terminal.port, extract_value(data, "port").c_str());
@@ -1752,6 +1814,13 @@ void parse_terminal_from_data(trigger_update_xml_t* trigger_update_xml, const st
// 根据 str_tag 将 terminal 添加到相应的数组
if (!terminal_update_should_bind_to_current_process(&work_terminal)) {
std::cout << "skip ledger update xml by comType:" << work_terminal.com_type
<< " terminal:" << work_terminal.terminal_id
<< " process:" << subdir << g_front_seg_index << std::endl;
return;
}
add_terminal_to_trigger_update(trigger_update_xml, str_tag, work_terminal);
}
@@ -1772,11 +1841,25 @@ void parse_ledger_update(trigger_update_xml_t* trigger_update_xml, const std::st
if (strTag == "delete") {
// 填充终端信息
strcpy(delete_terminal.terminal_id, extract_value(data, "id").c_str());
std::string com_type = extract_value(data, "comType");
if (com_type.empty())
com_type = extract_value(data, "com_type");
if (com_type.empty())
com_type = "MMS";
strncpy(delete_terminal.com_type, com_type.c_str(), sizeof(delete_terminal.com_type) - 1);
delete_terminal.com_type[sizeof(delete_terminal.com_type) - 1] = '\0';
//添加guid20250506
strncpy(delete_terminal.guid, guid_value.c_str(), sizeof(delete_terminal.guid) - 1);
delete_terminal.guid[sizeof(delete_terminal.guid) - 1] = '\0';
if (!terminal_update_should_bind_to_current_process(&delete_terminal)) {
std::cout << "skip delete ledger update xml by comType:" << delete_terminal.com_type
<< " terminal:" << delete_terminal.terminal_id
<< " process:" << subdir << g_front_seg_index << std::endl;
return;
}
// 将删除信息写入 delete_updates
std::cout << "delete ledger!!!!!"<<std::endl;
if (trigger_update_xml->delete_update_num < MAX_UPDATEA_NUM) {
@@ -2996,6 +3079,10 @@ void Cout_account_information() {
add_comm_log(const_cast<char*>(text.toLocal8Bit().constData()));
for (cpuno = 0; cpuno < ied->cpucount; cpuno++) {
LD_info = &(ied_usr->LD_info[cpuno]);
if (LD_info->cpuno == 0 || LD_info->LD_name == NULL)
continue;
QString text2;//待组装的pgsql语句
text2.append(QString("mp_id: \"%1\" terminal_code:\"%2\" ").arg(LD_info->mp_id).arg(LD_info->terminal_code));
add_comm_log(const_cast<char*>(text2.toLocal8Bit().constData()));
@@ -3335,6 +3422,45 @@ void SendJsonAPI_web(const std::string& strUrl, const char* code, const std::str
}
// 打印 terminal_dev_map 中所有内容的函数
static bool is_cfg_pqdif_process()
{
return std::strstr(subdir, "cfg_pqdif_data") != NULL;
}
static bool is_pqdif_com_type(const char* com_type)
{
QString type = com_type ? QString::fromLocal8Bit(com_type).trimmed() : QString();
return type.compare("PQDIF", Qt::CaseInsensitive) == 0;
}
static bool terminal_should_bind_to_current_process(const terminal_dev* dev)
{
if (dev == NULL)
return false;
if (is_pqdif_com_type(dev->com_type))
return is_cfg_pqdif_process();
if (is_cfg_pqdif_process())
return false;
return (atoi(dev->processNo) == g_front_seg_index || g_front_seg_index == 0);
}
static bool terminal_update_should_bind_to_current_process(const terminal* dev)
{
if (dev == NULL)
return false;
if (is_pqdif_com_type(dev->com_type))
return is_cfg_pqdif_process();
if (is_cfg_pqdif_process())
return false;
return true;
}
void printTerminalDevMap(const QMap<QString, terminal_dev*>& terminal_dev_map) {
QMap<QString, terminal_dev*>::const_iterator it;
for (it = terminal_dev_map.constBegin(); it != terminal_dev_map.constEnd(); ++it) {
@@ -3353,6 +3479,7 @@ void printTerminalDevMap(const QMap<QString, terminal_dev*>& terminal_dev_map) {
<< ", Device Type:" << QString(dev->dev_type)
<< ", Device Key:" << QString(dev->dev_key)
<< ", Device Series:" << QString(dev->dev_series)
<< ", Device comType:" << QString(dev->com_type)
<< ", Device processNo:" << QString(dev->processNo)
<< ", Device maxProcessNum:" << QString(dev->maxProcessNum)
<< ", Address:" << QString(dev->addr_str)
@@ -3387,6 +3514,7 @@ void printLedger(const ied_usr_t& ied_usr) {
std::cout << "|-- dev_type: " << ied_usr.dev_type << std::endl;
std::cout << "|-- dev_key: " << ied_usr.dev_key << std::endl;
std::cout << "|-- dev_series: " << ied_usr.dev_series << std::endl;
std::cout << "|-- com_type: " << ied_usr.com_type << std::endl;
std::cout << "|-- dev_flag: " << ied_usr.dev_flag << std::endl;
std::cout << "|-- last_call_wavelist_time: " << ied_usr.last_call_wavelist_time << std::endl;
@@ -3481,6 +3609,7 @@ void printLedgerinshell(const ied_usr_t& ied_usr, QIODevice* outputDevice) {
outputDevice->write("\r\x1B[K");outputDevice->write("|-- dev_type: " + QByteArray(ied_usr.dev_type) + "\n");
outputDevice->write("\r\x1B[K");outputDevice->write("|-- dev_key: " + QByteArray(ied_usr.dev_key) + "\n");
outputDevice->write("\r\x1B[K");outputDevice->write("|-- dev_series: " + QByteArray(ied_usr.dev_series) + "\n");
outputDevice->write("\r\x1B[K");outputDevice->write("|-- com_type: " + QByteArray(ied_usr.com_type) + "\n");
outputDevice->write("\r\x1B[K");outputDevice->write("|-- dev_processNo: " + QByteArray(ied_usr.processNo) + "\n");
outputDevice->write("\r\x1B[K");outputDevice->write("|-- dev_flag: " + QByteArray::number(ied_usr.dev_flag) + "\n");
outputDevice->write("\r\x1B[K");outputDevice->write("|-- last_call_wavelist_time: " + QByteArray::number(ied_usr.last_call_wavelist_time) + "\n");
@@ -4032,6 +4161,13 @@ int terminal_ledger_web(QMap<QString, terminal_dev*>* terminal_dev_map,
else strncpy(dev->dev_series, "N/A", sizeof(dev->dev_series) - 1);
dev->dev_series[sizeof(dev->dev_series) - 1] = '\0';
cJSON* comType = cJSON_GetObjectItem(item, "comType");
if (comType == NULL)
comType = cJSON_GetObjectItem(item, "com_type");
if (comType && comType->type == cJSON_String) strncpy(dev->com_type, comType->valuestring, sizeof(dev->com_type) - 1);
else strncpy(dev->com_type, "MMS", sizeof(dev->com_type) - 1);
dev->com_type[sizeof(dev->com_type) - 1] = '\0';
//lnk20250210台账进程号
cJSON* processNo = cJSON_GetObjectItem(item, "processNo"); // processNo转为字符串
if (processNo && processNo->type == cJSON_Number) snprintf(dev->processNo, sizeof(dev->processNo), "%d", processNo->valueint);
@@ -4137,19 +4273,20 @@ int terminal_ledger_web(QMap<QString, terminal_dev*>* terminal_dev_map,
//lnk20260312防止内存泄漏
bool inserted = false;
bool should_bind = terminal_should_bind_to_current_process(dev);
// 检查是否存在重复键
if (terminal_dev_map->contains(key)) {
std::cerr << "Duplicate terminal_code found: " << key.toStdString() << std::endl;
// 删除旧的 terminal_dev 对象以避免内存泄漏
delete terminal_dev_map->value(key);
if (should_bind) delete terminal_dev_map->value(key);
// 移除旧的键值对
terminal_dev_map->remove(key);
if (should_bind) terminal_dev_map->remove(key);
// 插入新的 terminal_dev 对象
if(atoi(dev->processNo) == g_front_seg_index || g_front_seg_index == 0){//lnk20250210匹配进程号
if(should_bind){//lnk20250210匹配进程号
//调试用
std::cout<< "process num match" << std::endl;
terminal_dev_map->insert(key, dev);
@@ -4157,7 +4294,7 @@ int terminal_ledger_web(QMap<QString, terminal_dev*>* terminal_dev_map,
}//后续修改为只有进程号匹配上index才录入当前进程
} else {
// 插入新的 terminal_dev 对象
if(atoi(dev->processNo) == g_front_seg_index || g_front_seg_index == 0){//lnk20250210匹配进程号
if(should_bind){//lnk20250210匹配进程号
//调试用
std::cout<< "process num match" << std::endl;
terminal_dev_map->insert(key, dev);
@@ -4189,6 +4326,18 @@ int terminal_ledger_web(QMap<QString, terminal_dev*>* terminal_dev_map,
return 0; // 确保函数有返回值
}
static void init_oper_type_cache(ied_usr_t *ied_usr)
{
if (ied_usr == NULL)
return;
ied_usr->oper_type_cache.inited = SD_FALSE;
ied_usr->oper_type_cache.ledrs_oper_type_id = -1;
ied_usr->oper_type_cache.reboot_oper_type_id = -1;
ied_usr->oper_type_cache.reset_oper_type_id = -1;
}
int parse_device_cfg_web()
{
std::cout << "parse_device_cfg_web" << endl;
@@ -4306,6 +4455,7 @@ int parse_device_cfg_web()
char dev_type[64];
char dev_key[255];
char dev_series[255];
char com_type[32];
char addr_str[64];
char port_char[64];
@@ -4335,6 +4485,8 @@ int parse_device_cfg_web()
strncpy(dev_type, value->dev_type, sizeof(dev_type) - 1);
strncpy(dev_key, value->dev_key, sizeof(dev_key) - 1);
strncpy(dev_series, value->dev_series, sizeof(dev_series) - 1);
strncpy(com_type, value->com_type, sizeof(com_type) - 1);
com_type[sizeof(com_type) - 1] = '\0';
strncpy(addr_str, value->addr_str, sizeof(addr_str) - 1);
strncpy(port_char, value->port, sizeof(port_char) - 1);
strncpy(processNo, value->processNo, sizeof(processNo) - 1);//进程号
@@ -4400,6 +4552,14 @@ int parse_device_cfg_web()
cout << "ied_usr->dev_type:" << ied_usr->dev_type << endl;
}
//lnk20250210台账进程号
if (com_type != NULL && com_type[0] != '\0') {
apr_snprintf(ied_usr->com_type, sizeof(ied_usr->com_type), "%s", com_type);//comType
cout << "ied_usr->com_type:" << ied_usr->com_type << endl;
}
else {
apr_snprintf(ied_usr->com_type, sizeof(ied_usr->com_type), "%s", "MMS");
cout << "ied_usr->com_type(default):" << ied_usr->com_type << endl;
}
if (processNo != NULL) {
apr_snprintf(ied_usr->processNo, sizeof(ied_usr->processNo), "%s", processNo);//processNo
cout << "ied_usr->processNo:" << ied_usr->processNo << endl;
@@ -4422,6 +4582,10 @@ int parse_device_cfg_web()
apr_snprintf(ied_usr->dev_key, sizeof(ied_usr->dev_key), "%s", "");//DEV_Key
cout << "defalut dev_key:" << ied_usr->dev_key << endl;
}
//lnk20260512
init_oper_type_cache(ied_usr);
//lnk20260304
ied_usr->log_level = log_level;//日志等级
cout << "ied_usr->log_level:" << ied_usr->log_level << endl;
@@ -4495,7 +4659,7 @@ int parse_device_cfg_web()
//otl_datetime timestamp;
int monitor_log_level = 1;//监测点日志等级
//for (int j = 0; j < 10; ++j) { // 假设最多有10个监测点
for (int j = 0; value->line[j].monitor_id[0] != '\0'; ++j){
for (int j = 0; value->line[j].monitor_id[0] != '\0'; ++j){//只看有测点号的监测点,避免访问无效数据
ledger_monitor& monitor = value->line[j];
// 检查监测点 ID 是否为空以避免访问无效数据
/*if (monitor.monitor_id[0] != '\0') {
@@ -4517,27 +4681,33 @@ int parse_device_cfg_web()
strncpy(monitor_status, monitor.status, sizeof(monitor_status) - 1);//添加监测点状态
monitor_log_level = monitor.log_level;//监测点日志等级
//监测点台账处理
count_real_monitor++;
memset(&line_info, 0, sizeof(line_info));
line_info.line_id = count_real_monitor; //监测点排号
count_real_monitor++;//初始为0进入一次+1有效监测点排号从1开始
memset(&line_info, 0, sizeof(line_info));//有效测点的初始化
line_info.line_id = count_real_monitor; //监测点排号,前置本地使用
cout << "line_id:" << line_info.line_id << endl;
strcpy(line_info.mp_id, monitor_id);
strcpy(line_info.mp_id, monitor_id);//测点id
cout << "mp_id:" << line_info.mp_id << endl;
strcpy(line_info.terminal_code, terminal_code); //从上级获取的终端号
cout << "terminal_code:" << line_info.terminal_code << endl;
if (isCharPtrEmpty(logical_device_seq)) {
line_info.cpuno = 1; //默认监测点实例号1
cout << "logical_device_seq:is null,set cpuno:"<< line_info.cpuno << endl;
}
else {
line_info.cpuno = std::atoi(logical_device_seq);
line_info.cpuno = std::atoi(logical_device_seq); //台账实例号,即台账的测点号
cout << "logical_device_seq:"<< line_info.cpuno << endl;
}
//cout << "cpuno:" << line_info.cpuno << endl;
strcpy(line_info.voltage_level, voltage_level);
strcpy(line_info.voltage_level, voltage_level);//电压等级
cout << "voltage_level:" << line_info.voltage_level << endl;
strcpy(line_info.v_wiring_type, terminal_connect);
strcpy(line_info.v_wiring_type, terminal_connect);//接线方式
cout << "v_wiring_type:" << line_info.v_wiring_type << endl;
//lnk2024-8-14记录接线标志
if (strcmp(line_info.v_wiring_type, "0") != 0)
{
@@ -4545,7 +4715,7 @@ int parse_device_cfg_web()
cout << "monitor_id" << monitor_id << "v_wiring_type:" << line_info.v_wiring_type << "is delta wiring:" << isdelta_flag << endl;
DIY_WARNLOG_CODE("process",0,LOG_CODE_LEDGER,"【WARN】前置连接的监测点 %s 是角形接线,对应终端为%s 终端类型是%s",line_info.mp_id,ied_usr->terminal_id,ied_usr->dev_type);
}
strcpy(line_info.monitor_status, monitor_status);
strcpy(line_info.monitor_status, monitor_status);//测点状态
cout << "monitor_status:" << line_info.monitor_status << endl;
//// 构造struct tm对象
struct tm timeinfo;
@@ -4556,27 +4726,32 @@ int parse_device_cfg_web()
timeinfo.tm_min = timestamp.minute;
timeinfo.tm_sec = timestamp.second;
time_t time = std::mktime(&timeinfo);
line_info.time = static_cast<long long>(time);
line_info.time = static_cast<long long>(time);//测点更新时间
cout << "time:" << line_info.time << endl;
strcpy(line_info.name, monitor_name);
strcpy(line_info.name, monitor_name);//测点名
cout << "name:" << line_info.name << endl;
line_info.read_flag = ENABLE; //监测点有效
line_info.log_level = monitor_log_level; //监测点日志等级
cout << "log_level_monitor:" << line_info.log_level << endl;
//ied = find_ied_from_dev_code(line_info.terminal_code); //不需要再找上级终端了,已经在终端里了
if (ied && ied->usr_ext && line_info.cpuno && (static_cast<int>(line_info.cpuno) < 10)) {
if (ied && ied->usr_ext && line_info.cpuno && (static_cast<int>(line_info.cpuno) < 10)) {//台账测点号有效
char str[256]; //256大小
byte_t cpuno = line_info.cpuno;
byte_t cpuno = line_info.cpuno; //台账的测点号
cout << "cpuno:" << (int)line_info.cpuno << endl;
cout << "index cpuno:" << cpuno-1 << endl;
ied_usr = (ied_usr_t*)ied->usr_ext;
ied_usr->LD_info[cpuno - 1] = line_info; //cpuno默认是1
ied_usr->LD_info[cpuno - 1].ied = ied;
ied_usr->LD_info[cpuno - 1].ied = ied; //父级指针绑定
apr_snprintf(str, sizeof(str), "PQMonitorPQM%d", cpuno);//将监测点逻辑号转为PQMonitorPQM+逻辑号
//lnk20250208不使用apr_pstrdup后续直接复用
//ied_usr->LD_info[cpuno - 1].LD_name = apr_pstrdup(g_init_pool, str);//将 str 中的格式化字符串复制到内存池 g_init_pool 中。ied_usr->LD_info[cpuno - 1].LD_name 存储了这个字符串的副本LD_name 现在是 PQMonitorPQM{cpuno} 的形式。
// 从 g_init_pool 内存池中分配固定 256 字节的内存
ied_usr->LD_info[cpuno - 1].LD_name = (char *)apr_palloc(g_init_pool, 256);
ied_usr->LD_info[cpuno - 1].LD_name = (char *)apr_palloc(g_init_pool, 256);//根据台账中的测点号申请内存
//调试用,申请的地址
printf("%s分配内存地址 LD_name[%d]: %p\n", ied_usr->terminal_id, cpuno - 1, (void*)ied_usr->LD_info[cpuno - 1].LD_name);
// 清空内存,防止残留数据
@@ -4587,21 +4762,21 @@ int parse_device_cfg_web()
//这里申请的空间基于ied的数量挂载到ied上
ied_usr->LD_info[cpuno - 1].ht_fcd = apr_hash_make(g_init_pool); //这两行代码分别为 ied_usr->LD_info[cpuno - 1] 的两个成员ht_fcd 和 ht_full_fcda创建了空的哈希表。apr_hash_make(g_init_pool) 会在 g_init_pool 内存池中为这两个哈希表分配内存空间
ied_usr->LD_info[cpuno - 1].ht_full_fcda = apr_hash_make(g_init_pool);//它们的 key 值和 value 在后续的代码中可能会被填充
ied_usr->LD_info[cpuno - 1].rptcount = 0;
ied_usr->LD_info[cpuno - 1].rptcount = 0;//有效测点报告数为初始为0
cout << "rptcount:" << ied_usr->LD_info[cpuno - 1].rptcount << endl;
if (cpuno > ied->cpucount) {
ied->cpucount = cpuno;
if (cpuno > ied->cpucount) {//测点号大于测点数
ied->cpucount = cpuno;//取测点号,后续遍历测点时需要跳过无效测点
}
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////
if (count_real < count_cfg){
if (count_real < count_cfg){//读取计数器和台账数量对比,如果读取的数量小于台账数量,说明有些台账读取失败了,按照读取的数量来设置进程的台账数量,避免后续访问读取失败的台账导致崩溃
g_node->n_clients = count_real;
}
if (count_cfg != count_real){
if (count_cfg != count_real){//应该相等,如果不相等说明有些台账读取失败了,按照读取的数量来设置进程的台账数量,避免后续访问读取失败的台账导致崩溃
return APR_EBADF;
}
@@ -5301,7 +5476,7 @@ std::string base64_encode(const std::string& in) {
return out; // 返回编码后的字符串
}
void handleUploadResponse(const std::string& response, char* wavepath) {
void handleUploadResponse(const std::string& response,const std::string& localPath, char* wavepath, int type) {
// 解析 JSON 响应
cJSON* json_data = cJSON_Parse(response.c_str());
@@ -5349,11 +5524,43 @@ void handleUploadResponse(const std::string& response, char* wavepath) {
}
// 拷贝到 wavepath
if (type == 1) {
strcpy(wavepath, nameWithoutExt.c_str());
}
else {
strcpy(wavepath, name.c_str());
}
std::cout << "wavepath: " << wavepath << std::endl;
DIY_INFOLOG_CODE("process",0,LOG_CODE_TRANSIENT_COMM,"【NORMAL】前置上传文件成功,远端文件名:%s",wavepath);
// =========================
// 上传成功后删除本地文件
// =========================
if (remove(localPath.c_str()) == 0)
{
std::cout << "Delete local file success: "
<< localPath << std::endl;
DIY_INFOLOG_CODE("process",0,
LOG_CODE_TRANSIENT_COMM,
"【NORMAL】删除本地文件成功:%s",
localPath.c_str());
}
else
{
std::cout << "Delete local file failed: "
<< localPath
<< " errno=" << errno
<< " err=" << strerror(errno)
<< std::endl;
DIY_ERRORLOG_CODE("process",0,
LOG_CODE_TRANSIENT_COMM,
"【ERROR】删除本地文件失败:%s errno=%d err=%s",
localPath.c_str(),
errno,
strerror(errno));
}
}
} else {
std::cerr << "Error: Missing expected fields in JSON response." << std::endl;
@@ -5423,7 +5630,7 @@ void handleUploadResponse(const std::string& response, char* wavepath) {
}*/
//这是dataform发送方式
void SendFileWeb(const std::string& strUrl, const char* localpath, const char* cloudpath, char* wavepath) {
void SendFileWeb(const std::string& strUrl, const char* localpath, const char* cloudpath, char* wavepath,int type) {
// 初始化 curl
CURL* curl = curl_easy_init();
if (curl) {
@@ -5477,7 +5684,7 @@ void SendFileWeb(const std::string& strUrl, const char* localpath, const char* c
DIY_ERRORLOG_CODE("process",0,LOG_CODE_TRANSIENT_COMM,"【ERROR】前置上传暂态录波文件 %s 失败,请检查文件上传接口配置",localpath);
} else {
std::cout << "http web success, response: " << resPost0 << std::endl;
handleUploadResponse(resPost0, wavepath); // 处理响应
handleUploadResponse(resPost0, localpath, wavepath, type); // 处理响应
}
// 清理
@@ -5492,7 +5699,7 @@ void SendFileWeb(const std::string& strUrl, const char* localpath, const char* c
void SOEFileWeb(char* localpath,char* cloudpath, char* wavepath)
{
//示例ip更换为实际ip即可
SendFileWeb(WEB_FILEUPLOAD,localpath,cloudpath,wavepath);
SendFileWeb(WEB_FILEUPLOAD,localpath,cloudpath,wavepath,1);
}
void SOEFileWeb_test()
@@ -5545,9 +5752,9 @@ int DownloadFileWeb(const std::string& strUrl,
std::string fullUrl = strUrl;
if (fullUrl.find('?') == std::string::npos)
fullUrl += "?path=";
fullUrl += "?filePath=";
else
fullUrl += "&path=";
fullUrl += "&filePath=";
fullUrl += encodedPath;
curl_easy_setopt(curl, CURLOPT_URL, fullUrl.c_str());
@@ -5569,15 +5776,26 @@ int DownloadFileWeb(const std::string& strUrl,
if (res != CURLE_OK)
{
std::cerr << "DownloadFileWeb failed: " << curl_easy_strerror(res) << std::endl;
remove(localpath);
return -1;
}
if (http_code != 200)
{
std::cerr << "DownloadFileWeb http code: " << http_code << std::endl;
remove(localpath);
return -1;
}
if (chmod(localpath, 0777) != 0)
{
std::cerr << "chmod 777 failed: " << localpath << std::endl;
}
else
{
std::cout << "chmod 777 success: " << localpath << std::endl;
}
return 0;
}
@@ -5611,6 +5829,13 @@ int update_one_terminal_ledger(terminal* update, int i,ied_t* ied,int terminal_i
apr_snprintf(ied_usr->dev_type, sizeof(ied_usr->dev_type), "%s", update[i].dev_type);
printf("ied_usr->dev_type: %s\n", ied_usr->dev_type);
}
if (update[i].com_type != NULL && update[i].com_type[0] != '\0') {
apr_snprintf(ied_usr->com_type, sizeof(ied_usr->com_type), "%s", update[i].com_type);
printf("ied_usr->com_type: %s\n", ied_usr->com_type);
} else {
apr_snprintf(ied_usr->com_type, sizeof(ied_usr->com_type), "%s", "MMS");
printf("ied_usr->com_type (default): %s\n", ied_usr->com_type);
}
if (update[i].processNo != NULL) {
apr_snprintf(ied_usr->processNo, sizeof(ied_usr->processNo), "%s", update[i].processNo);
printf("ied_usr->processNo: %s\n", ied_usr->processNo);
@@ -5711,6 +5936,9 @@ int update_one_terminal_ledger(terminal* update, int i,ied_t* ied,int terminal_i
chnl_usr->m_state = CHANNEL_DISCONNECTED;
chnl_usr->m_ClosedMsTime = NEXT_CONNECT_TIME * (-1);
//lnk20260512
init_oper_type_cache(ied_usr);
// 将 monitorData 中的数据写入到 LD_info 中
int count_real_monitor = 0; //遍历监测点台账的计数器
int j;
@@ -6092,10 +6320,22 @@ int parse_rpt_log_ini_one(ied_t* ied)
//调试
printf("%s使用内存地址 LD_name[%d]: %p\n", ied_usr->terminal_id, cpuno, (void*)ied_usr->LD_info[cpuno].LD_name);
//添加判断有的监测点没有cpuno为2直接申请了LD_info[2-1]没申请LD_info[0]
if(ied_usr->LD_info[cpuno].LD_name == NULL){
printf("this ld_info didn't palloc space ,maybe this ledger has problem!");
DIY_ERRORLOG_CODE("process",0,LOG_CODE_RPTINIT,"【ERROR】终端%s的监测点序号为%d的监测点无法初始化报告,这个装置的台账存在缺失,请检查装置台账的监测点总数和各监测点的序号",ied_usr->terminal_id,cpuno + 1);
//添加默认值防止崩溃20260601
LD_info->rptcount = 0;
LD_info->logcount = 0;
LD_info->cpuno = 0;
LD_info->LD_name = NULL;
LD_info->rptinfo = NULL;
LD_info->loginfo = NULL;
delete[] tmp;//Get_IED中分配了内存使用后删除
continue;//跳过防止崩溃
}
@@ -6114,7 +6354,18 @@ int parse_rpt_log_ini_one(ied_t* ied)
init_rptctrl_by_count(LD_info, rpt_cfg_strlists[type]->size());
for (int i = 0; i < rpt_cfg_strlists[type]->size(); ++i) {
apr_snprintf(buf, sizeof(buf), "%s", rpt_cfg_strlists[type]->at(i).toAscii().constData());
fill_rptctrl_by_cfg(LD_info, i, buf);
//fill_rptctrl_by_cfg(LD_info, i, buf);
int ret = fill_rptctrl_by_cfg(LD_info, i, buf);
if (ret != 0) {
printf("[RPT_INIT][ERROR] fill_rptctrl_by_cfg failed dev=%s cpu=%d rpt=%d cfg=%s\n",
ied_usr->terminal_id, cpuno, i, buf);
DIY_ERRORLOG_CODE(ied_usr->terminal_id,1,LOG_CODE_RPTINIT,"【ERROR】终端%s的监测点序号为%d的监测点的第%d个报告配置解析失败请检查报告配置格式是否正确",ied_usr->terminal_id, cpuno + 1, i + 1);
if (LD_info->rptinfo && LD_info->rptinfo[i]) {
LD_info->rptinfo[i]->rptID = NULL;
LD_info->rptinfo[i]->LD_info = NULL;
}
}
}
//初始化监测点的日志控制
init_logctrl_by_count(LD_info, log_cfg_strlists[type]->size());
@@ -6415,6 +6666,7 @@ void clearIedUsr(ied_usr_t *ied_usr) {
memset(ied_usr->dev_type, 0, sizeof(ied_usr->dev_type));
memset(ied_usr->dev_key, 0, sizeof(ied_usr->dev_key));
memset(ied_usr->dev_series, 0, sizeof(ied_usr->dev_series));
memset(ied_usr->com_type, 0, sizeof(ied_usr->com_type));
memset(ied_usr->terminal_id, 0, sizeof(ied_usr->terminal_id));
memset(ied_usr->org_name, 0, sizeof(ied_usr->org_name));
memset(ied_usr->maint_name, 0, sizeof(ied_usr->maint_name));
@@ -6528,7 +6780,7 @@ bool shouldSkipTerminal(const char* terminal_id) {
void rocketmq_test_300(int mpnum,int front_index,int type) {
Ckafka_data_t data;
data.strTopic = QString::fromStdString(G_ROCKETMQ_TOPIC);
data.strTopic = QString::fromStdString(G_ROCKETMQ_TOPIC_TEST);
data.mp_id = "0";
// 读取文件内容
@@ -7141,6 +7393,8 @@ const char* get_front_msg_from_subdir() {
return "实时数据进程";
else if (std::strstr(subdir, "cfg_soe_comtrade") != NULL)
return "暂态和告警进程";
else if (std::strstr(subdir, "cfg_pqdif_data") != NULL)
return "pqdif处理进程";
else if (std::strstr(subdir, "cfg_recallhis_data") != NULL)
return "稳态补招进程";
else if (std::strstr(subdir, "cfg_stat_data") != NULL)

View File

@@ -229,6 +229,8 @@ std::string get_front_type_from_subdir() {
return "realTime";
else if (std::strstr(subdir, "cfg_soe_comtrade") != NULL)
return "comtrade";
else if (std::strstr(subdir, "cfg_pqdif_data") != NULL)
return "PQDIF";
else if (std::strstr(subdir, "cfg_recallhis_data") != NULL)
return "recall";
else if (std::strstr(subdir, "cfg_stat_data") != NULL)

1297
json/PQSMsg.cpp Normal file

File diff suppressed because it is too large Load Diff

2548
json/PQSMsg.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -32,10 +32,12 @@
#include <QMutexLocker>
#include <QMapIterator>
#include <QStringList>
#include <QByteArray>
#include <QList>
// ★MOD: 全局追踪表mp_id -> remaining times
//全局追踪表mp_id -> rpt_no -> remaining times
static QMutex g_trace_mutex;
static QHash<QString, int> g_trace_map;
static QHash<QString, QHash<int, int> > g_trace_map;
///////////////////////////////////////////////////lnk2024-10-21////////////////////////////////////////////////////////
extern void SendJsonAPI_web(const std::string& strUrl, const char* code, const std::string& json, char** ptr);
@@ -44,6 +46,7 @@ extern std::string WEB_COMFLAG;
extern std::string WEB_EVENT;
extern std::string WEB_FILEDOWNLOAD;
extern std::string G_CONNECT_TOPIC;
extern std::string DATA_TRACE_TOPIC;//lnk20260609数据追踪topic
extern int RECALL_ONLY_FLAG;
@@ -166,6 +169,7 @@ public:
QString WavePhasicB;
QString WavePhasicC;
QString TypeOfData; //闪变和统计是否合并 0-分开 1-合并
QString IEDControl; //例LD0 lnk2026-5-13
QString UnitOfTimeUnit; //暂态事件持续事件单位0 - 毫秒 1 - 秒 lnk20260127
QString ValueOfTimeUnit; //上送值的时间UTC-UTC时间 beijing-北京时间
QString WaveTimeFlag; //录波文件的时间UTC-UTC时间 beijing-北京时间
@@ -256,84 +260,142 @@ static QString escape_json_string(const QString& s)
return out;
}
// 打开追踪:次数 times比如 5
// 打开追踪:每个报告各追踪一次
void process_trace_command(const std::string& id, int times)
{
if (times <= 0) return;
(void)times;
QString qid = QString::fromStdString(id).trimmed();
if (qid.isEmpty()) return;
QList<int> rpt_nos;
QByteArray qid_bytes = qid.toLocal8Bit();
pthread_mutex_lock(&mtx);
LD_info_t *ld_info = find_LD_info_only_from_mp_id(qid_bytes.data());
if (ld_info != NULL && ld_info->rptinfo != NULL && ld_info->rptcount > 0) {
for (int i = 0; i < ld_info->rptcount; ++i) {
if (ld_info->rptinfo[i] == NULL)
continue;
int rpt_no = ld_info->rptinfo[i]->rptNo;
if (!rpt_nos.contains(rpt_no))
rpt_nos.append(rpt_no);
}
}
pthread_mutex_unlock(&mtx);
if (rpt_nos.isEmpty()) {
cout << "[TRACE] mp_id=" << id << " has no report info" << endl;
return;
}
QMutexLocker lk(&g_trace_mutex);
g_trace_map[qid] = times; // 重新打开就覆盖/重置次数
QHash<int, int>& rpt_map = g_trace_map[qid];
for (int i = 0; i < rpt_nos.size(); ++i) {
int rpt_no = rpt_nos.at(i);
rpt_map[rpt_no] = 1;
cout << "[TRACE] mp_id=" << id << " add rpt_no=" << rpt_no
<< " left=" << rpt_map.value(rpt_no) << endl;
}
}
// 查询是否要追踪
static bool trace_is_enabled(const QString& mp_id)
static bool trace_is_enabled(const QString& mp_id, int rpt_no)
{
QMutexLocker lk(&g_trace_mutex);
auto it = g_trace_map.constFind(mp_id);
return (it != g_trace_map.constEnd() && it.value() > 0);
QHash<QString, QHash<int, int> >::const_iterator mp_it = g_trace_map.constFind(mp_id);
if (mp_it == g_trace_map.constEnd())
return false;
QHash<int, int>::const_iterator rpt_it = mp_it.value().constFind(rpt_no);
return (rpt_it != mp_it.value().constEnd() && rpt_it.value() > 0);
}
// 命中一次并扣减;扣到 0 自动删
static void trace_hit_and_decrement(const QString& mp_id)
static void trace_hit_and_decrement(const QString& mp_id, int rpt_no)
{
QMutexLocker lk(&g_trace_mutex);
auto it = g_trace_map.find(mp_id);
if (it == g_trace_map.end()) return;
QHash<QString, QHash<int, int> >::iterator mp_it = g_trace_map.find(mp_id);
if (mp_it == g_trace_map.end()) return;
int left = it.value();
QHash<int, int>::iterator rpt_it = mp_it.value().find(rpt_no);
if (rpt_it == mp_it.value().end()) return;
int left = rpt_it.value();
left -= 1;
if (left <= 0) g_trace_map.erase(it);
else it.value() = left;
if (left <= 0)
mp_it.value().erase(rpt_it);
else
rpt_it.value() = left;
if (mp_it.value().isEmpty())
g_trace_map.erase(mp_it);
}
//追踪61850原始数据
static QString build_mms_multiline_text(const json_block_data* data)
static QString build_mms_json_object(const json_block_data* data)
{
QStringList lines;
QString json = "{";
QMapIterator<QString, double> it(data->mms_str_map);
bool first = true;
while (it.hasNext()) {
it.next();
lines << QString("%1 = %2").arg(it.key()).arg(it.value(), 0, 'g', 15);
if (!first)
json += ",";
json += QString("\"%1\":%2")
.arg(escape_json_string(it.key()))
.arg(QString::number(it.value(), 'g', 15));
first = false;
}
return lines.join("\n");
}
static QString build_trace_json(const json_block_data* data)
{
if (!data) return "{}";
QString mms_text = build_mms_multiline_text(data);
QString json;
json += "{";
json += QString("\"mp_id\":\"%1\",").arg(escape_json_string(data->mp_id));
json += QString("\"func_type\":%1,").arg(data->func_type);
json += QString("\"data_time\":%1,").arg(QString::number((qlonglong)data->time));
json += QString("\"voltage_level\":\"%1\",").arg(QString::number(data->voltage_level, 'f', 6));
json += QString("\"dev_type\":\"%1\",").arg(escape_json_string(data->dev_type));
json += QString("\"mms_text\":\"%1\"").arg(escape_json_string(mms_text));
json += "}";
return json;
}
static void send_trace_if_needed(json_block_data* pdata)
static QString build_trace_json(const json_block_data* data, const char *v_wiring_type, const char *rpt_id, int rpt_no)
{
if (!data) return "{}";
QString mms_json = build_mms_json_object(data);
QString wiring_type = v_wiring_type ? QString::fromLocal8Bit(v_wiring_type) : "";
QString rpt = rpt_id ? QString::fromLocal8Bit(rpt_id) : "";
QString json;
json += "{";
json += QString("\"mp_id\":\"%1\",").arg(escape_json_string(data->mp_id));
json += QString("\"v_wiring_type\":\"%1\",").arg(escape_json_string(wiring_type));
json += QString("\"rpt_id\":\"%1\",").arg(escape_json_string(rpt));
json += QString("\"rpt_no\":%1,").arg(rpt_no);
json += QString("\"func_type\":%1,").arg(data->func_type);
json += QString("\"data_time\":%1,").arg(QString::number((qlonglong)data->time));
json += QString("\"voltage_level\":\"%1\",").arg(QString::number(data->voltage_level, 'f', 6));
json += QString("\"dev_type\":\"%1\",").arg(escape_json_string(data->dev_type));
json += QString("\"mms_json\":%1").arg(mms_json);
json += "}";
return json;
}
static void send_trace_if_needed(json_block_data* pdata, const char *v_wiring_type, const char *rpt_id, int rpt_no)
{
const QString mp_id_q = pdata->mp_id;
if (trace_is_enabled(mp_id_q)) {
if (trace_is_enabled(mp_id_q, rpt_no)) {
// 1) 组 json
QString jsonText = build_trace_json(pdata);
QString jsonText = build_trace_json(pdata, v_wiring_type, rpt_id, rpt_no);
// 2) 组 KafkaData
Ckafka_data_t KafkaData;
KafkaData.monitor_id = pdata->monitorId;
KafkaData.mp_id = pdata->mp_id;
KafkaData.strTopic = "DATA_TRACE_TOPIC";
KafkaData.strTopic = QString::fromStdString(DATA_TRACE_TOPIC);
KafkaData.strText = jsonText;
kafka_data_list_mutex.lock();
@@ -341,9 +403,25 @@ static void send_trace_if_needed(json_block_data* pdata)
kafka_data_list_mutex.unlock();
// 3) 次数 -1
trace_hit_and_decrement(mp_id_q);
trace_hit_and_decrement(mp_id_q, rpt_no);
}
}
int trace_json_block_data(char v_wiring_type[], json_block_data *data, const char *rpt_id, int rpt_no)
{
send_trace_if_needed(data, v_wiring_type, rpt_id, rpt_no);
return 1;
}
int trace_json_is_enabled(const char *mp_id, int rpt_no)
{
QString mp_id_q = mp_id ? QString::fromLocal8Bit(mp_id).trimmed() : "";
if (mp_id_q.isEmpty())
return 0;
return trace_is_enabled(mp_id_q, rpt_no) ? 1 : 0;
}
////////////////////////////////////////////////////////////////////////////////////////////lnk20250710添加频率值存储
struct mp_freq_save {
double G_FREQ;
@@ -384,6 +462,7 @@ bool get_xml_config_by_dev_type(const char* dev_type, XmlConfigC* out_cfg) {
strncpy(out_cfg->TypeOfData, cfg.TypeOfData.toUtf8().constData(), sizeof(out_cfg->TypeOfData) - 1); out_cfg->TypeOfData[sizeof(out_cfg->TypeOfData) - 1] = '\0';//lnk20260127
strncpy(out_cfg->ValueOfTimeUnit, cfg.ValueOfTimeUnit.toUtf8().constData(),sizeof(out_cfg->ValueOfTimeUnit) - 1);
strncpy(out_cfg->WaveTimeFlag, cfg.WaveTimeFlag.toUtf8().constData(), sizeof(out_cfg->WaveTimeFlag) - 1);
strncpy(out_cfg->IEDControl, cfg.IEDControl.toUtf8().constData(), sizeof(out_cfg->IEDControl) - 1);//lnk2026-5-13
strncpy(out_cfg->IEDname, cfg.IEDname.toUtf8().constData(), sizeof(out_cfg->IEDname) - 1);
strncpy(out_cfg->LDevicePrefix, cfg.LDevicePrefix.toUtf8().constData(), sizeof(out_cfg->LDevicePrefix) - 1);
@@ -1036,6 +1115,10 @@ bool ParseXMLConfig2(int xml_flag, XmlConfig *cfg, list<CTopic*> *ctopiclist,QSt
cfg->WaveTimeFlag.append(e.attribute("WaveTimeFlag"));
}
if ("IEDControl" == strTag)
{
cfg->IEDControl.append(e.attribute("name"));
}
if ("IED" == strTag)
{
cfg->IEDname.append(e.attribute("name"));
@@ -1143,9 +1226,12 @@ void printCTopicList(const std::list<CTopic*>& ctopic_list) {
val->fValue);
}
}
break; // 只打印第一个 Item 的 SequenceList 和 DataValueList避免输出过多
}
break; // 只打印第一个 Monitor 的 ItemList避免输出过多
}
// 如果需要打印 SOEList可加以下
/*
int soeIndex = 0;
@@ -1255,6 +1341,7 @@ int transfer_json_block_data(char v_wiring_type[], json_block_data *data) //json
printf("TypeOfData = '%s'\n", cfg1.TypeOfData);
printf("ValueOfTimeUnit = '%s'\n", cfg1.ValueOfTimeUnit);
printf("WaveTimeFlag = '%s'\n", cfg1.WaveTimeFlag);
printf("IEDControl = '%s'\n", cfg1.IEDControl);
printf("IEDname = '%s'\n", cfg1.IEDname);
printf("LDevicePrefix = '%s'\n", cfg1.LDevicePrefix);
printf("=====================================\n");
@@ -1539,7 +1626,9 @@ int transfer_json_block_data(char v_wiring_type[], json_block_data *data) //json
kafka_data_list_mutex.unlock(); //解锁
longjumpflag = true;
}
else if (countflag < num && 0 != time_sec % 7200) {
longjumpflag = true;//不发送数据
}
//lnk20260127
if (typeofdata == false || data_have_static == false) {//不合并则处理完闪变就不处理其他数据,如果有统计数据或者数据类型区分了就继续处理其他数据
if (longjumpflag == true || shortjumpflag == true) {
@@ -2448,6 +2537,9 @@ int transfer_json_block_data(char v_wiring_type[], json_block_data *data) //json
kafka_data_list_mutex.unlock(); //解锁
longjumpflag = true;
}
else if (countflag < num && 0 != time_sec % 7200) {
longjumpflag = true;//不发送数据
}
if (typeofdata == false || data_have_static == false) {//不合并则处理完闪变就不处理其他数据
if (longjumpflag == true || shortjumpflag == true) {
return 1;
@@ -2856,7 +2948,7 @@ void processGGIO_start_data_end(char* mp_id,char* fullname,double v,long long ti
printf("transfer json ggio data: %s==%s \n", KafkaData.strText.toStdString().c_str(), fullname);
kafka_data_list_mutex.lock(); //加锁
kafka_data_list.append(KafkaData); //添加 kafka发送链表
//kafka_data_list.append(KafkaData); //添加 kafka发送链表
kafka_data_list_mutex.unlock(); //解锁
}
@@ -3102,9 +3194,9 @@ char* Get_IED(char* devtype)
}
QString ied;
ied.append(xmlinfo_list[type]->xmlcfg.IEDname); // 从配置文件读取的终端名
ied.append(xmlinfo_list[type]->xmlcfg.LDevicePrefix); //// 从配置文件读取的监测点名
ied.append("%d");
ied.append(xmlinfo_list[type]->xmlcfg.IEDname); // 从配置文件读取的终端名PQ
ied.append(xmlinfo_list[type]->xmlcfg.LDevicePrefix); //// 从配置文件读取的监测点名PQM
ied.append("%d");//最后构成PQPQM1
QByteArray byteArray = ied.toLocal8Bit();
@@ -3735,4 +3827,3 @@ void Set_xml_nodeinfo_one(char* dev_type)
}
}

View File

@@ -26,6 +26,7 @@
#define THREE_SECS_DATA_BASE_NODE_ID 200 // 三秒数据、实时数据
#define SOE_COMTRADE_BASE_NODE_ID 300 // soe 录波
#define HIS_DATA_BASE_NODE_ID 400 //历史、补招
#define PQDIF_DATA_BASE_NODE_ID 800 // pqdif数据
class json_block_data //json拼接参数类
{
@@ -74,6 +75,8 @@ public:
};
int transfer_json_block_data(char v_wiring_type[], json_block_data* data);//lnk2024-8-16添加参数
int trace_json_is_enabled(const char *mp_id, int rpt_no);
int trace_json_block_data(char v_wiring_type[], json_block_data* data, const char *rpt_id, int rpt_no);
#endif /* __cplusplus */

File diff suppressed because it is too large Load Diff

View File

@@ -688,6 +688,7 @@ struct terminal // 终端台账
char dev_type[64];
char dev_key[255];
char dev_series[255];
char com_type[32];
char processNo[64]; //lnk20250210进程号
char addr_str[64];
char port[64];

View File

@@ -113,12 +113,12 @@ extern LOG_TLS int g_log_code_tls; // 声明为 TLS 变量,定义见 log4.cpp
int __old_code__ = g_log_code_tls; \
g_log_code_tls = (int)(CODE_INT); \
\
char __msg_buf__[256]; \
char __msg_buf__[512]; \
format_log_msg(__msg_buf__, sizeof(__msg_buf__), __VA_ARGS__); \
\
const char* __key_raw__ = (KEY); \
\
char __key_buf__[256]; \
char __key_buf__[512]; \
switch ((int)(KEY_TYPE)) { \
case 0: \
snprintf(__key_buf__, sizeof(__key_buf__), "process"); \

View File

@@ -31,6 +31,7 @@
#define NEW_HIS_DATA_BASE_NODE_ID 500
#define RECALL_HIS_DATA_BASE_NODE_ID 600
#define RECALL_ALL_DATA_BASE_NODE_ID 700
#define PQDIF_DATA_BASE_NODE_ID 800
#define REPORT_TYPE_STAT 1
#define REPORT_TYPE_REAL 2
@@ -74,6 +75,12 @@ int json_block_create_data(char monid_char[], char* mms_str , double v, int flic
//lnk2024-8-16添加参数
int json_block_create_end(char v_wiring_type[], char monid_char[], int flicker_flag); //CZY 2023-08-17 测试
int json_trace_block_create_start(char voltage_level[], char monid_char[], int flicker_flag, char temcode[], int line_id, char v_wiring_type[], char rpt_id[], int rpt_no);
int json_trace_block_create_time(char monid_char[], long long Time, int flicker_flag, char rpt_id[], int rpt_no);
int json_trace_block_create_flag(char monid_char[], int flag, int flicker_flag, char rpt_id[], int rpt_no);
int json_trace_block_create_data(char monid_char[], char* mms_str, double v, int flicker_flag, char rpt_id[], int rpt_no);
int json_trace_block_create_end(char v_wiring_type[], char monid_char[], int flicker_flag, char rpt_id[], int rpt_no);
//zw 2024-01-31 补招模式优化
void add_mvl_type_ctrl(char doname[], int ctrl);
int sel_mvl_type_ctrl_flag(char doname[]);
@@ -165,6 +172,7 @@ typedef struct {
char TypeOfData[64];
char ValueOfTimeUnit[64];
char WaveTimeFlag[64];
char IEDControl[64];
char IEDname[64];
char LDevicePrefix[64];
} XmlConfigC;
@@ -183,6 +191,8 @@ typedef struct file_dir_req_t
char devid[128];
int type;
char path[256];
char remote_path[256];
time_t create_time;
} file_dir_req_t;

View File

@@ -81,6 +81,8 @@ void init_global_function_enable()
three_secs_enabled = 1;
}else if (strcmp(subdir,"cfg_soe_comtrade")==0) { //告警和录波和暂态
g_node_id = SOE_COMTRADE_BASE_NODE_ID;
}else if (strcmp(subdir,"cfg_pqdif_data")==0) {
g_node_id = PQDIF_DATA_BASE_NODE_ID;
}else if (strcmp(subdir,"cfg_his_data")==0) { //不使用
g_node_id = RECALL_ALL_DATA_BASE_NODE_ID;
g_node_id = HIS_DATA_BASE_NODE_ID;

View File

@@ -32,7 +32,7 @@ extern apr_pool_t* g_cfg_pool;
extern apr_pool_t* g_init_pool;
extern int g_DevFlag; //日志配置中读取的参数暂无特定使用lnk20250121
extern bool DEBUGOPEN;//调试开关,控制是否输出调试日志,默认关闭
extern int IED_COUNT;
extern int RECALL_ONLY_FLAG; //lnk20260309添加一个全局变量标志是否只运行补招程序
@@ -44,7 +44,13 @@ SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__;
#ifdef _OS_UNIX_
#include <sys/vfs.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#endif
#include <ctype.h>
#include <errno.h>
#include <time.h>
extern uint32_t g_dead_lock_counter;
extern uint32_t g_thread_blocked_times;
@@ -78,6 +84,26 @@ extern int recall_daily;
extern char* UDS_UPLOAD_URL;
///////////////////////////////////////////////////////////////////
static void format_time_ms(long long ms, char* buf, size_t buf_len){
time_t sec = ms / 1000;
int milli = ms % 1000;
struct tm tm_time;
localtime_r(&sec, &tm_time); // 线程安全
snprintf(buf, buf_len,
"%04d-%02d-%02d %02d:%02d:%02d.%03d",
tm_time.tm_year + 1900,
tm_time.tm_mon + 1,
tm_time.tm_mday,
tm_time.tm_hour,
tm_time.tm_min,
tm_time.tm_sec,
milli);
}
static apr_status_t ChannelCheckPQDIFFiles(chnl_usr_t *chnl_usr);
static apr_status_t ChannelRecallPQDIFFiles(chnl_usr_t *chnl_usr, LD_info_t *LD_info, long long start_sec, long long end_sec);
//lnk20250122start
apr_status_t init_rem_dib_table()
{
@@ -132,9 +158,24 @@ void CloseIECReports(chnl_usr_t *chnl_usr)
ied_usr = GET_IEDEXT_ADDR(ied);
for(cpuno=0 ; cpuno<ied->cpucount; cpuno++) {
LD_info = &(ied_usr->LD_info[cpuno]);
//添加保护lnk20260602
if (LD_info->cpuno == 0 ||
LD_info->LD_name == NULL ||
LD_info->rptcount <= 0 ||
LD_info->rptinfo == NULL)
{
continue;
}
for(rpt_no=0 ; rpt_no<LD_info->rptcount; rpt_no++) {
char rpt_inst_name[65];
rptinfo = LD_info->rptinfo[rpt_no];
//添加保护lnk20260602
if (rptinfo == NULL)
continue;
if ( !rptinfo->rpt_registered )
continue;
if ( rptinfo->chnl_id != chnl_usr->chnl_id)
@@ -260,6 +301,18 @@ void IECReport_tryGI(chnl_usr_t *chnl_usr,rptinfo_t *rptinfo)
//增加处理根据稳态,或暂态功能等,决定 报告是否需要注册、取消注册或 不做任何处理
int judge_rpt_next_should_do(rptinfo_t *rptinfo)
{
if (rptinfo == NULL) {
printf("[RPT][ERROR] rptinfo is NULL\n");
return SHOULD_DO_NOTHING;
}
if (rptinfo->LD_info == NULL) {
printf("[RPT][ERROR] rptinfo->LD_info is NULL, rptID=%s\n",
rptinfo->rptID ? rptinfo->rptID : "NULL");
return SHOULD_DO_NOTHING;
}
int should_register_state = 1; //各功能默认注册
int is_real_report = (rptinfo->report_PQ_type & REPORT_TYPE_REAL);//报告控制中包含的类型
int is_soe_report = (rptinfo->report_PQ_type & REPORT_TYPE_SOE);
@@ -302,7 +355,23 @@ void ChannelCheckIECReports(chnl_usr_t *chnl_usr)
{
LD_info = &(ied_usr->LD_info[cpuno]); //遍历监测点
if (LD_info->cpuno==0){
//添加保护,防止台账中测点号不连续导致的崩溃
if (LD_info->cpuno == 0 ||
LD_info->LD_name == NULL ||
LD_info->rptcount <= 0 ||
LD_info->rptinfo == NULL)
{
printf("[RPT][SKIP] invalid LD_info cpu=%d cpuno=%d LD_name=%p rptcount=%d rptinfo=%p\n",
cpuno,
LD_info->cpuno,
LD_info->LD_name,
LD_info->rptcount,
LD_info->rptinfo);
continue;
}
//未初始化的测点告警没有意义直接访问id还可能崩溃
/*if (LD_info->cpuno==0){
// 仅在还没达到5次上限时打印
if (!LD_info->has_logged_regist) {
LD_info->registcount++;
@@ -319,13 +388,39 @@ void ChannelCheckIECReports(chnl_usr_t *chnl_usr)
}
}
continue;
}
}*/
for(rpt_no=0 ; rpt_no<LD_info->rptcount; rpt_no++) { //遍历报告(映射文件中读取的报告控制)
rptinfo = LD_info->rptinfo[rpt_no] ;
//检查是否需要注册或取消注册报告,或不做任何处理
if(DEBUGOPEN)printf("[RPT][CHECK] ip=%s cpu=%d rpt_no=%d rptcount=%d LD_name=%s rptinfo=%p\n",
chnl_usr->ip_str,
cpuno,
rpt_no,
LD_info->rptcount,
LD_info->LD_name ? LD_info->LD_name : "NULL",
rptinfo);
if (rptinfo == NULL) {
printf("[RPT][ERROR] rptinfo NULL, skip\n");
continue;
}
if (rptinfo->LD_info == NULL) {
printf("[RPT][ERROR] rptinfo->LD_info NULL, rptID=%s, skip\n",
rptinfo->rptID ? rptinfo->rptID : "NULL");
continue;
}
if (rptinfo->rptID == NULL || rptinfo->rptID[0] == '\0') {
printf("[RPT][ERROR] rptID empty, skip\n");
continue;
}
//添加保护
if (judge_rpt_next_should_do(rptinfo)==SHOULD_DO_NOTHING)//检查是否触发
{
//DIY_DEBUGLOG_CODE(LD_info->mp_id,2,LOG_CODE_REPORT,"【DEBUG】监测点:%s - id:%s不注册报告", LD_info->name,LD_info->mp_id);
@@ -486,18 +581,34 @@ void ChannelCheckIECLogs(chnl_usr_t *chnl_usr)
LD_info_t *LD_info;
loginfo_t *loginfo = NULL;
int cpuno;
int is_pqdif_process;
double now;
static double last_check_recall_config_time = 0.0;
ied = chnl_usr->chnl->ied;
ied_usr = GET_IEDEXT_ADDR(ied);
is_pqdif_process = (g_node_id == PQDIF_DATA_BASE_NODE_ID);
for(cpuno=0 ; cpuno<ied->cpucount; cpuno++) {
LD_info = &(ied_usr->LD_info[cpuno]);
//添加保护,防止台账中测点号不连续导致的崩溃
if (LD_info->cpuno == 0 ||
LD_info->LD_name == NULL)
{
continue;
}
if (!is_pqdif_process &&
(LD_info->logcount <= 0 ||
LD_info->loginfo == NULL))
{
continue;
}
//日志控制块缺失
if (LD_info->logcount<=0){
/*if (LD_info->logcount<=0){
// 仅在还没达到5次上限时打印
if (!LD_info->has_logged_regist) {
LD_info->registcount++;
@@ -515,10 +626,16 @@ void ChannelCheckIECLogs(chnl_usr_t *chnl_usr)
}
continue;
}
}*/
if (!is_pqdif_process) {
loginfo = LD_info->loginfo[0] ;
//添加保护,防止台账中测点号不连续导致的崩溃
if (loginfo == NULL || loginfo->LD_info == NULL)
continue;
}
apr_sleep(apr_time_from_sec(1) / 10);
Check_Recall_Config(LD_info->mp_id);//尝试获取xml结构
@@ -529,22 +646,30 @@ void ChannelCheckIECLogs(chnl_usr_t *chnl_usr)
int failed_count = 0;
/////////////////////////////////////////////////////根据配置文件控制下发补招时间为北京时间还是utc时间
long long utc_or_beijing = 0;
if (!is_pqdif_process) {
printf("~~~~~~~this dev type is %s~~~~~~~",ied_usr->dev_type);
XmlConfigC cfg1;
memset(&cfg1,0,sizeof(cfg1));
if (get_xml_config_by_dev_type(ied_usr->dev_type, &cfg1)) {
printf("ValueOfTimeUnit = %s\n", cfg1.ValueOfTimeUnit);
} else {
printf("读取失败,未找到 dev_type\n");
}
long long utc_or_beijing;
utc_or_beijing = 0;
if(strcmp(cfg1.ValueOfTimeUnit, "utc") == 0){//装置时间是utc还是北京
utc_or_beijing = 28800;//秒
DIY_WARNLOG_CODE(LD_info->mp_id,2,LOG_CODE_RECALL,"【WARN】监测点:%s - id:%s开始补招数据,下发补招时间为utc时间,监测点对应装置型号:%s", LD_info->name,LD_info->mp_id,ied_usr->dev_type);
DIY_WARNLOG_CODE(LD_info->mp_id,2,LOG_CODE_RECALL,"【WARN】监测点:%s - id:%s开始补招数据,注意!下发补招时间为utc时间,监测点对应装置型号:%s", LD_info->name,LD_info->mp_id,ied_usr->dev_type);
}
else{
utc_or_beijing = 0;
DIY_WARNLOG_CODE(LD_info->mp_id,2,LOG_CODE_RECALL,"【WARN】监测点:%s - id:%s开始补招数据,下发补招时间为beijing时间,监测点对应装置型号:%s", LD_info->name,LD_info->mp_id,ied_usr->dev_type);
DIY_WARNLOG_CODE(LD_info->mp_id,2,LOG_CODE_RECALL,"【WARN】监测点:%s - id:%s开始补招数据,注意!下发补招时间为beijing时间,监测点对应装置型号:%s", LD_info->name,LD_info->mp_id,ied_usr->dev_type);
}
}
else {
DIY_WARNLOG_CODE(LD_info->mp_id,2,LOG_CODE_RECALL,"[PQDIF][RECALL] monitor:%s - id:%s start recall PQDIF files, dev_type:%s", LD_info->name,LD_info->mp_id,ied_usr->dev_type);
}
//////////////////////////////////////////////////////////////
@@ -567,6 +692,9 @@ void ChannelCheckIECLogs(chnl_usr_t *chnl_usr)
LD_info->autorecallflag = 1;//正在补招
//当前不区分稳态和暂态lnk20241030如果做区分修改Check_Recall_Config从xml文件获取数据后赋值给了LD_info
if (is_pqdif_process)
continue;
loginfo->need_steady = LD_info->autorecall[i]->need_steady; loginfo->need_voltage = LD_info->autorecall[i]->need_voltage;
loginfo->start_time = apr_time_from_sec(LD_info->autorecall[i]->start - 5);//保证时间开头
@@ -619,6 +747,24 @@ void ChannelCheckIECLogs(chnl_usr_t *chnl_usr)
//不管是否成功这个补招文件必须删除可能出现一直失败循环读取文件和循环补招导致程序崩溃202050724lnk
//if (failed_count==0) {//成功
if (is_pqdif_process) {
if (min_start_sec == LLONG_MAX || max_end_sec <= min_start_sec) {
failed_count++;
printf("[PQDIF][RECALL] invalid recall range mp_id=%s min=%lld max=%lld\n",
LD_info->mp_id, min_start_sec, max_end_sec);
}
else {
ret = ChannelRecallPQDIFFiles(chnl_usr, LD_info, min_start_sec, max_end_sec);
if (ret != APR_SUCCESS)
failed_count++;
}
g_dead_lock_counter = 0;
g_thread_blocked_times = 0;
now = sGetMsTime();
last_check_recall_config_time = now;
}
Delete_recall_Xml(LD_info->mp_id);
// ===== [新增] 组装上送的补招开始/结束时间字符串(本地时区,直接“秒转字符串”)=====
@@ -1030,6 +1176,7 @@ void process_ledger_update(trigger_update_xml_t *ledger_update_xml)
//3-写入台账内容///////////////////////////////////
//4-配置映射文件//////////////////////////////
if (g_node_id != PQDIF_DATA_BASE_NODE_ID) {
char model[64] = {0};
// 获取模型ID检查是否返回 NULL
parse_model_cfg_web_one(ied,&model);//存储在/FeProject/dat/
@@ -1064,6 +1211,11 @@ void process_ledger_update(trigger_update_xml_t *ledger_update_xml)
//5-报告块初始化///////////////////////////////////
//调试
}
else {
printf("[PQDIF] skip model xml and report init terminal=%s\n", ied_usr->terminal_id);
}
printf("ledger id: %s\n", ((ied_usr_t*)ied->channel[0].ied->usr_ext)->terminal_id);
//6-init_rem_dib_table//////////////////////////////
@@ -1133,6 +1285,7 @@ void process_ledger_update(trigger_update_xml_t *ledger_update_xml)
//3-写入台账内容////////////////////////////////////////////
//4-配置映射文件///////////////////////////////////////////
if (g_node_id != PQDIF_DATA_BASE_NODE_ID) {
char model[64] = {0};
// 获取模型ID检查是否返回 NULL
parse_model_cfg_web_one(ied,&model);//存储在/FeProject/dat/
@@ -1167,6 +1320,11 @@ void process_ledger_update(trigger_update_xml_t *ledger_update_xml)
parse_rpt_log_ini_one(ied);
//5-报告块初始化///////////////////////////////////
}
else {
printf("[PQDIF] skip model xml and report init terminal=%s\n", ied_usr->terminal_id);
}
//6-init_rem_dib_table//////////////////////////////
init_rem_dib_table_one(ied);
//6-init_rem_dib_table///////////////////////////////////
@@ -1324,6 +1482,7 @@ void print_terminal(const terminal* tmnl) {
printf("Device Type: %s\n", is_empty(tmnl->dev_type) ? "N/A" : tmnl->dev_type);
printf("Device Key: %s\n", is_empty(tmnl->dev_key) ? "N/A" : tmnl->dev_key);
printf("Device Series: %s\n", is_empty(tmnl->dev_series) ? "N/A" : tmnl->dev_series);
printf("Com Type: %s\n", is_empty(tmnl->com_type) ? "MMS" : tmnl->com_type);
printf("Address: %s\n", is_empty(tmnl->addr_str) ? "N/A" : tmnl->addr_str);
printf("Port: %s\n", is_empty(tmnl->port) ? "N/A" : tmnl->port);
printf("Timestamp: %s\n", is_empty(tmnl->timestamp) ? "N/A" : tmnl->timestamp);
@@ -1531,14 +1690,23 @@ void CheckAllConnectedChannel()
if(chnl_usr->m_state == CHANNEL_CONNECTED)
{
if(g_node_id == THREE_SECS_DATA_BASE_NODE_ID) {
InitLedrsOperTypeForChannel(chnl_usr);//写特殊控制的初始化
if(DEBUGOPEN)printf("[FILEDIR] enter HandleFileDirReqForChannel");
HandleFileDirReqForChannel(chnl_usr);//文件目录请求
}
if(g_node_id == PQDIF_DATA_BASE_NODE_ID){
ChannelCheckPQDIFFiles(chnl_usr);
}
else{
ChannelCheckIECReports(chnl_usr);//报告
}
if ( (g_node_id == SOE_COMTRADE_BASE_NODE_ID) || (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))
ChannelCheckWaveFiles(chnl_usr);//录波文件
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))
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)
|| g_node_id == PQDIF_DATA_BASE_NODE_ID)
ChannelCheckIECLogs(chnl_usr);//补招文件
if ( (sGetMsTime() - chnl_usr->m_LastPosRespTime) > 15*1000 ) //wait 15 secs隔15秒获取一次响应两次没响应后关闭
{
char** varnames ;
@@ -1881,6 +2049,361 @@ static ST_RET Write_Named_Var(LD_info_t *LD_info,chnl_usr_t *chnl_usr,char* VarN
#define TICKS_PER_MIN 60LL*1000LL*1000LL
/* PQDIF 文件召唤参数。
* C# 读取程序的默认周期是 2 小时,文件名形如:
* PQMonitor_PQM1_20190401_1400_02.pqd
* 其中 “1_20190401_1400_” 用于匹配测点序号和时间片。
* 当前进程没有单独的手动时间窗输入,所以这里实现自动模式:
* 1. 每个终端按固定扫描间隔读取一次 /PQDIF/ 目录;
* 2. 只下载最近一个完整周期内、且测点序号匹配的 .pqd 文件;
* 3. 下载前检查 /FeProject/data/pqdif/ 下是否已有同名文件,避免重复下载。
*/
#define PQDIF_REMOTE_DIR "/PQDIF/"
#define PQDIF_LOCAL_DIR "/FeProject/data/pqdif"
#define PQDIF_AUTO_INTERVAL_HOURS 2
#define PQDIF_SCAN_INTERVAL_MS (60 * 1000)
#define PQDIF_MAX_DEVICE_INDEX 4096
#define PQDIF_MAX_FETCH_PER_SCAN 32
static const char* pqdif_basename(const char* filename)
{
const char* base = filename;
const char* p;
if (filename == NULL)
return "";
for (p = filename; *p != '\0'; ++p) {
if (*p == '/' || *p == '\\')
base = p + 1;
}
return base;
}
static int pqdif_has_pqd_suffix(const char* filename)
{
size_t len;
if (filename == NULL)
return FALSE;
len = strlen(filename);
if (len < 4)
return FALSE;
return (filename[len - 4] == '.'
&& tolower((unsigned char)filename[len - 3]) == 'p'
&& tolower((unsigned char)filename[len - 2]) == 'q'
&& tolower((unsigned char)filename[len - 1]) == 'd');
}
static void pqdif_make_time_match(int cpu_no, time_t tm_value, char* out, size_t out_len)
{
struct tm tm_local;
memset(&tm_local, 0, sizeof(tm_local));
localtime_r(&tm_value, &tm_local);
/* C# 目录匹配逻辑使用 “测点号_yyyyMMdd_HH00_” 作为关键片段。
* 例如测点号 1、2026-07-17 10 点,对应 “1_20260717_1000_”。
*/
snprintf(out, out_len, "%d_%04d%02d%02d_%02d00_",
cpu_no % 10,
tm_local.tm_year + 1900,
tm_local.tm_mon + 1,
tm_local.tm_mday,
tm_local.tm_hour);
}
static int pqdif_use_interval_end_time(const char* dev_type)
{
return (dev_type != NULL && strcmp(dev_type, "PS_NET_PQDIF") == 0);
}
static time_t pqdif_align_interval_start(long long sec)
{
time_t hour_floor;
struct tm tm_local;
int hour_mod;
memset(&tm_local, 0, sizeof(tm_local));
hour_floor = (time_t)sec;
localtime_r(&hour_floor, &tm_local);
tm_local.tm_min = 0;
tm_local.tm_sec = 0;
hour_floor = mktime(&tm_local);
hour_mod = tm_local.tm_hour % PQDIF_AUTO_INTERVAL_HOURS;
return hour_floor - hour_mod * 3600;
}
static int pqdif_match_interval(const char* filename, int cpu_no, time_t interval_start, const char* dev_type)
{
time_t name_time;
char time_match[64];
if (!pqdif_has_pqd_suffix(filename))
return FALSE;
if (cpu_no <= 0)
return FALSE;
/* C# 原逻辑按设备类型决定文件名时间:
* - 普通设备用周期开始时间;
* - PS_NET_PQDIF 用周期结束时间。
* 这里保持同样规则,避免把普通设备下一周期的开始文件误认为本周期结束文件。
*/
name_time = interval_start;
if (pqdif_use_interval_end_time(dev_type))
name_time += PQDIF_AUTO_INTERVAL_HOURS * 3600;
pqdif_make_time_match(cpu_no, name_time, time_match, sizeof(time_match));
return strstr(filename, time_match) != NULL;
}
static int pqdif_match_recent_interval(const char* filename, int cpu_no, const char* dev_type)
{
time_t now_time;
time_t interval_start;
now_time = time(NULL);
interval_start = pqdif_align_interval_start((long long)now_time) - PQDIF_AUTO_INTERVAL_HOURS * 3600;
return pqdif_match_interval(filename, cpu_no, interval_start, dev_type);
}
static int pqdif_ensure_local_dir()
{
#ifdef _OS_UNIX_
if (mkdir("/FeProject/data", 0777) != 0 && errno != EEXIST)
return SD_FAILURE;
if (mkdir(PQDIF_LOCAL_DIR, 0777) != 0 && errno != EEXIST)
return SD_FAILURE;
#endif
return SD_SUCCESS;
}
static int pqdif_local_file_exists(const char* filename)
{
char local_file[512];
const char* base = pqdif_basename(filename);
if (base[0] == '\0')
return FALSE;
snprintf(local_file, sizeof(local_file), "%s/%s", PQDIF_LOCAL_DIR, base);
#ifdef _OS_UNIX_
return access(local_file, F_OK) == 0;
#else
return FALSE;
#endif
}
static void pqdif_build_remote_filename(const char* dir_filename, char* out, size_t out_len)
{
if (dir_filename == NULL || dir_filename[0] == '\0') {
out[0] = '\0';
return;
}
/* mms_mvla_fdir 的返回值有两种可能:
* 1. 带目录:/PQDIF/xxx.pqd
* 2. 仅文件名xxx.pqd
* PQDIF 必须固定从 /PQDIF/ 取,所以仅返回文件名时要补齐远端目录。
*/
if (strchr(dir_filename, '/') != NULL || strchr(dir_filename, '\\') != NULL)
snprintf(out, out_len, "%s", dir_filename);
else
snprintf(out, out_len, "%s%s", PQDIF_REMOTE_DIR, dir_filename);
}
static int pqdif_get_file_from_device(chnl_usr_t *chnl_usr, const char *rem_filename, char *only_filename_ret, size_t only_filename_ret_len)
{
int ret;
char loc_file_fullname[512];
char rem_file_fullname[512];
const char *only_filename;
if (chnl_usr == NULL || chnl_usr->net_info == NULL || rem_filename == NULL || rem_filename[0] == '\0')
return SD_FAILURE;
only_filename = pqdif_basename(rem_filename);
if (only_filename[0] == '\0')
return SD_FAILURE;
if (pqdif_ensure_local_dir() != SD_SUCCESS) {
echo_warn1("[PQDIF] create local dir failed: %s\n", PQDIF_LOCAL_DIR);
return SD_FAILURE;
}
memset(loc_file_fullname,0,sizeof(loc_file_fullname));
memset(rem_file_fullname,0,sizeof(rem_file_fullname));
apr_snprintf(loc_file_fullname,sizeof(loc_file_fullname),"%s/%s",PQDIF_LOCAL_DIR,only_filename);
apr_snprintf(rem_file_fullname,sizeof(rem_file_fullname),"%s",rem_filename);
if (only_filename_ret != NULL && only_filename_ret_len > 0)
apr_snprintf(only_filename_ret,only_filename_ret_len,"%s",only_filename);
printf("[PQDIF] mms_getFile local=%s remote=%s\n", loc_file_fullname, rem_file_fullname);
ret = mms_getFile(chnl_usr->net_info,loc_file_fullname,rem_file_fullname,g_pt61850app->mmsOpTimeout);
if (ret != SD_SUCCESS) {
echo_warn3("[PQDIF] mms_getFile failed IP=%s local=%s remote=%s\n",
chnl_usr->ip_str, loc_file_fullname, rem_file_fullname);
return ret;
}
#ifdef _OS_UNIX_
{
struct stat st;
if (stat(loc_file_fullname, &st) != 0 || st.st_size <= 0) {
unlink(loc_file_fullname);
echo_warn2("[PQDIF] file is empty or invalid: %s from %s\n",
loc_file_fullname, rem_file_fullname);
return SD_FAILURE;
}
}
#endif
return SD_SUCCESS;
}
static apr_status_t ChannelRecallPQDIFFiles(chnl_usr_t *chnl_usr, LD_info_t *LD_info, long long start_sec, long long end_sec)
{
ied_t *ied;
ied_usr_t *ied_usr;
char **filenames = NULL;
int filenum = 0;
int ret;
int file_idx;
int fetched = 0;
int existed = 0;
int missing = 0;
int failed = 0;
time_t interval_start;
time_t interval_step;
if (chnl_usr == NULL || chnl_usr->chnl == NULL || chnl_usr->chnl->ied == NULL ||
chnl_usr->chnl->ied->usr_ext == NULL || chnl_usr->net_info == NULL ||
LD_info == NULL) {
printf("[PQDIF][RECALL] invalid context, skip\n");
return APR_EGENERAL;
}
if (LD_info->cpuno == 0 || LD_info->LD_name == NULL || LD_info->mp_id[0] == '\0') {
printf("[PQDIF][RECALL] invalid LD_info, skip\n");
return APR_EGENERAL;
}
if (end_sec <= start_sec) {
printf("[PQDIF][RECALL] invalid time range mp_id=%s start=%lld end=%lld\n",
LD_info->mp_id, start_sec, end_sec);
return APR_EGENERAL;
}
ied = chnl_usr->chnl->ied;
ied_usr = (ied_usr_t*)ied->usr_ext;
if (pqdif_ensure_local_dir() != SD_SUCCESS) {
echo_warn1("[PQDIF][RECALL] create local dir failed: %s\n", PQDIF_LOCAL_DIR);
return APR_EGENERAL;
}
/* 单点补招与 C# 手动/补招逻辑保持一致:
* 1. 先读取装置 /PQDIF/ 目录;
* 2. 按 PQDIF_AUTO_INTERVAL_HOURS 将补招时间范围切成多个文件周期;
* 3. 普通设备用周期开始时间匹配文件名PS_NET_PQDIF 用周期结束时间匹配文件名;
* 4. 每个周期只取一份匹配文件,下载接口直接调用 mms_getFile。
*/
ret = mms_mvla_fdir(chnl_usr->net_info,(ST_CHAR*)PQDIF_REMOTE_DIR,
3*g_pt61850app->mmsOpTimeout,&filenames,&filenum,g_pt61850app->tmp_pool);
if (ret != SD_SUCCESS) {
echo_warn2("[PQDIF][RECALL] mms_mvla_fdir failed, IED=%d IP=%s\n", ied->id, chnl_usr->ip_str);
return APR_EGENERAL;
}
if (filenames == NULL || filenum <= 0) {
printf("[PQDIF][RECALL] no file in remote dir %s, mp_id=%s IP=%s\n",
PQDIF_REMOTE_DIR, LD_info->mp_id, chnl_usr->ip_str);
return APR_EGENERAL;
}
interval_step = PQDIF_AUTO_INTERVAL_HOURS * 3600;
interval_start = pqdif_align_interval_start(start_sec);
printf("[PQDIF][RECALL] begin mp_id=%s cpuno=%d range=%lld~%lld interval_start=%lld files=%d dev_type=%s\n",
LD_info->mp_id, LD_info->cpuno, start_sec, end_sec, (long long)interval_start,
filenum, ied_usr->dev_type);
while ((long long)interval_start < end_sec) {
int found_this_interval = FALSE;
for (file_idx = 0; file_idx < filenum; file_idx++) {
const char *base_name;
char remote_filename[256];
char only_filename_ret[256];
int ret_get;
if (filenames[file_idx] == NULL)
continue;
base_name = pqdif_basename(filenames[file_idx]);
if (base_name[0] == '\0')
continue;
if (!pqdif_match_interval(base_name, LD_info->cpuno, interval_start, ied_usr->dev_type))
continue;
found_this_interval = TRUE;
if (pqdif_local_file_exists(base_name)) {
existed++;
printf("[PQDIF][RECALL] local file exists, skip mp_id=%s interval=%lld file=%s\n",
LD_info->mp_id, (long long)interval_start, base_name);
break;
}
memset(remote_filename,0,sizeof(remote_filename));
memset(only_filename_ret,0,sizeof(only_filename_ret));
pqdif_build_remote_filename(filenames[file_idx], remote_filename, sizeof(remote_filename));
if (remote_filename[0] == '\0') {
failed++;
break;
}
ret_get = pqdif_get_file_from_device(chnl_usr, remote_filename, only_filename_ret, sizeof(only_filename_ret));
if (ret_get == SD_SUCCESS) {
fetched++;
chnl_usr->m_LastPosRespTime = sGetMsTime();
printf("[PQDIF][RECALL] fetch success mp_id=%s interval=%lld remote=%s local=%s/%s\n",
LD_info->mp_id, (long long)interval_start, remote_filename, PQDIF_LOCAL_DIR, only_filename_ret);
}
else {
failed++;
echo_warn3("[PQDIF][RECALL] fetch failed mp_id=%s remote=%s IP=%s\n",
LD_info->mp_id, remote_filename, chnl_usr->ip_str);
}
break;
}
if (!found_this_interval) {
missing++;
printf("[PQDIF][RECALL] no matched file mp_id=%s interval=%lld dev_type=%s\n",
LD_info->mp_id, (long long)interval_start, ied_usr->dev_type);
}
interval_start += interval_step;
}
printf("[PQDIF][RECALL] finish mp_id=%s fetched=%d existed=%d missing=%d failed=%d\n",
LD_info->mp_id, fetched, existed, missing, failed);
if (missing > 0 || failed > 0)
return APR_EGENERAL;
return APR_SUCCESS;
}
int pt61850_write_cn_file(chnl_usr_t *chnl_usr, ied_t *ied, char *rem_filename, char *only_filename_ret)
{
@@ -1926,6 +2449,140 @@ int pt61850_write_cn_file(chnl_usr_t *chnl_usr, ied_t *ied, char *rem_filename,
return ret;
}
static apr_status_t ChannelCheckPQDIFFiles(chnl_usr_t *chnl_usr)
{
ied_t *ied;
ied_usr_t *ied_usr;
LD_info_t *LD_info;
char **filenames = NULL;
int filenum = 0;
int ret;
int cpuno;
int file_idx;
int fetched = 0;
int matched = 0;
int scan_idx;
double now_ms;
static double s_last_scan_ms[PQDIF_MAX_DEVICE_INDEX] = {0};
if (chnl_usr == NULL || chnl_usr->chnl == NULL || chnl_usr->chnl->ied == NULL ||
chnl_usr->chnl->ied->usr_ext == NULL || chnl_usr->net_info == NULL) {
printf("[PQDIF] invalid channel context, skip scan\n");
return APR_EGENERAL;
}
ied = chnl_usr->chnl->ied;
ied_usr = (ied_usr_t*)ied->usr_ext;
/* CheckAllConnectedChannel 会轮询通道;这里按终端做节流,避免每次循环都读一次
* /PQDIF/ 目录。目录读和文件下载都走 MMS同一个终端一分钟扫描一次即可。
*/
scan_idx = ied_usr->dev_idx;
if (scan_idx < 0 || scan_idx >= PQDIF_MAX_DEVICE_INDEX)
scan_idx = (int)chnl_usr->chnl_id;
if (scan_idx < 0 || scan_idx >= PQDIF_MAX_DEVICE_INDEX)
scan_idx = 0;
now_ms = sGetMsTime();
if (s_last_scan_ms[scan_idx] > 0 &&
(now_ms - s_last_scan_ms[scan_idx]) < PQDIF_SCAN_INTERVAL_MS) {
return APR_SUCCESS;
}
s_last_scan_ms[scan_idx] = now_ms;
/* 本函数只做 PQDIF 文件获取:
* 1. 从装置固定目录 /PQDIF/ 取文件列表;
* 2. 用台账里的 LD_info->cpuno 匹配文件名里的 “测点号_yyyyMMdd_HH00_”
* 3. 只下载 .pqd 文件,且本地 /FeProject/data/pqdif/ 已存在同名文件时跳过;
* 4. 真正下载直接调用 mms_getFile不改动原录波文件下载函数。
*/
if (pqdif_ensure_local_dir() != SD_SUCCESS) {
echo_warn1("[PQDIF] create local dir failed: %s\n", PQDIF_LOCAL_DIR);
return APR_EGENERAL;
}
ret = mms_mvla_fdir(chnl_usr->net_info,(ST_CHAR*)PQDIF_REMOTE_DIR,
3*g_pt61850app->mmsOpTimeout,&filenames,&filenum,g_pt61850app->tmp_pool);
if (ret != SD_SUCCESS) {
echo_warn2("[PQDIF] mms_mvla_fdir failed, IED=%d IP=%s\n", ied->id, chnl_usr->ip_str);
return APR_EGENERAL;
}
if (filenames == NULL || filenum <= 0) {
printf("[PQDIF] no file in remote dir %s, IED=%d IP=%s\n", PQDIF_REMOTE_DIR, ied->id, chnl_usr->ip_str);
return APR_SUCCESS;
}
printf("[PQDIF] scan remote dir %s success, IED=%d IP=%s file_count=%d\n",
PQDIF_REMOTE_DIR, ied->id, chnl_usr->ip_str, filenum);
for (cpuno = 0; cpuno < ied->cpucount; cpuno++) {
LD_info = &(ied_usr->LD_info[cpuno]);
if (LD_info->cpuno == 0 ||
LD_info->LD_name == NULL ||
LD_info->mp_id[0] == '\0') {
continue;
}
for (file_idx = 0; file_idx < filenum; file_idx++) {
const char *base_name;
char remote_filename[256];
char only_filename_ret[256];
int ret_get;
if (filenames[file_idx] == NULL)
continue;
base_name = pqdif_basename(filenames[file_idx]);
if (base_name[0] == '\0')
continue;
/* C# 读取程序的兼容逻辑是读目录后按片段匹配:
* cpuNo%10_yyyyMMdd_HH00_
* 这里保持同样的匹配方式,不依赖厂家前缀,也兼容普通设备用
* 周期开始时间命名、振兴类设备用周期结束时间命名的差异。
*/
if (!pqdif_match_recent_interval(base_name, LD_info->cpuno, ied_usr->dev_type))
continue;
matched++;
if (pqdif_local_file_exists(base_name)) {
printf("[PQDIF] local file exists, skip mp_id=%s file=%s\n", LD_info->mp_id, base_name);
continue;
}
memset(remote_filename,0,sizeof(remote_filename));
memset(only_filename_ret,0,sizeof(only_filename_ret));
pqdif_build_remote_filename(filenames[file_idx], remote_filename, sizeof(remote_filename));
if (remote_filename[0] == '\0')
continue;
ret_get = pqdif_get_file_from_device(chnl_usr, remote_filename, only_filename_ret, sizeof(only_filename_ret));
if (ret_get == SD_SUCCESS) {
fetched++;
chnl_usr->m_LastPosRespTime = sGetMsTime();
printf("[PQDIF] fetch success mp_id=%s name=%s remote=%s local=%s/%s\n",
LD_info->mp_id, LD_info->name, remote_filename, PQDIF_LOCAL_DIR, only_filename_ret);
}
else {
echo_warn3("[PQDIF] fetch failed mp_id=%s remote=%s IP=%s\n",
LD_info->mp_id, remote_filename, chnl_usr->ip_str);
}
if (fetched >= PQDIF_MAX_FETCH_PER_SCAN) {
printf("[PQDIF] reach fetch limit per scan=%d, IED=%d IP=%s\n",
PQDIF_MAX_FETCH_PER_SCAN, ied->id, chnl_usr->ip_str);
return APR_SUCCESS;
}
}
}
printf("[PQDIF] scan finish IED=%d IP=%s matched=%d fetched=%d\n",
ied->id, chnl_usr->ip_str, matched, fetched);
return APR_SUCCESS;
}
apr_status_t prepare_call_cn_wavelist(LD_info_t *LD_info, int FltNum)
{
int i;
@@ -2065,6 +2722,8 @@ apr_status_t call_cn_wavelist(LD_info_t *LD_info )
printf(">>>>>>>> extract_timestamp_from_cfg_file success \n");
qvvr = find_qvvr_by_trig_tm(LD_info,trig_tm); //根据文件的触发时间查找检测点记录的匹配上的暂态事件
if (qvvr) {
DIY_INFOLOG_CODE(LD_info->mp_id,2,LOG_CODE_COMTRADE_FILE,"【NORMAL】监测点:%s - id:%s波形文件匹配暂态事件成功", LD_info->name,LD_info->mp_id);
char* uuid_cfg = (char*)malloc(65 * sizeof(char));//上传文件后获取到的路径
char* uuid_dat = (char*)malloc(65 * sizeof(char));
char* filename_cfg = (char*)malloc(100 * sizeof(char));//上传文件后获取到的文件名
@@ -2277,19 +2936,45 @@ apr_status_t call_cn_wavelist(LD_info_t *LD_info )
//获取时间类型lnk20250520
XmlConfigC cfg;
if (get_xml_config_by_dev_type(ied_usr->dev_type, &cfg)) {
if(strcmp(cfg.UnitOfTimeUnit, "1") == 0){//持续时间上送的是秒1还是毫秒0
DIY_ERRORLOG_CODE(LD_info->mp_id,2,LOG_CODE_COMTRADE_FILE,"【ERROR】监测点:%s - id:%s 匹配录波文件失败,录波号段:%d,录波文件的开始时间:%lld触发时间:%lld映射配置的暂态持续时间单位是s", LD_info->name,LD_info->mp_id,LD_info->FltNum[i],start_tm,trig_tm);
}
else{
DIY_ERRORLOG_CODE(LD_info->mp_id,2,LOG_CODE_COMTRADE_FILE,"【ERROR】监测点:%s - id:%s 匹配录波文件失败,录波号段:%d,录波文件的开始时间:%lld触发时间:%lld映射配置的暂态持续时间单位是ms", LD_info->name,LD_info->mp_id,LD_info->FltNum[i],start_tm,trig_tm);
char trig_time_str[64] = {0};
format_time_ms(trig_tm, trig_time_str, sizeof(trig_time_str));
if(strcmp(cfg.WaveTimeFlag, "utc") == 0) {
DIY_ERRORLOG_CODE(
LD_info->mp_id,
2,
LOG_CODE_COMTRADE_FILE,
"【ERROR】监测点:%s - id:%s 匹配录波文件失败,录波号段:%d录波触发时间戳:%lld录波触发时间:%s映射配置的暂态持续时间单位是s",
LD_info->name,
LD_info->mp_id,
LD_info->FltNum[i],
trig_tm,
trig_time_str);
} else {
DIY_ERRORLOG_CODE(
LD_info->mp_id,
2,
LOG_CODE_COMTRADE_FILE,
"【ERROR】监测点:%s - id:%s 匹配录波文件失败,录波号段:%d录波触发时间戳:%lld录波触发时间:%s映射配置的暂态持续时间单位是ms",
LD_info->name,
LD_info->mp_id,
LD_info->FltNum[i],
trig_tm,
trig_time_str);
}
}
else {
printf("读取失败,未找到 dev_type\n");
DIY_ERRORLOG_CODE(LD_info->mp_id,2,LOG_CODE_COMTRADE_FILE,"【ERROR】监测点:%s - id:%s 匹配录波文件失败,录波号段:%d,录波文件的开始时间:%lld触发时间:%lld映射配置的暂态持续时间单位未配置", LD_info->name,LD_info->mp_id,LD_info->FltNum[i],start_tm,trig_tm);
DIY_ERRORLOG_CODE(LD_info->mp_id,2,LOG_CODE_COMTRADE_FILE,"【ERROR】监测点:%s - id:%s 匹配录波文件失败,录波号段:%d,录波文件的开始时间:%lld触发时间:%lld映射配置的录波文件时间单位未配置", LD_info->name,LD_info->mp_id,LD_info->FltNum[i],start_tm,trig_tm);
}
}
else {
DIY_INFOLOG_CODE(LD_info->mp_id,2,LOG_CODE_COMTRADE_FILE,"【INFO】监测点:%s - id:%s的录波文件没有任何暂态事件可以匹配录波号段%d", LD_info->name,LD_info->mp_id,LD_info->FltNum[i]);
}
}
}
@@ -2338,4 +3023,3 @@ char* convertMsToDateTimeString(int64_t usTime) {
return buffer;
}

View File

@@ -993,6 +993,7 @@ ST_VOID u_iec_rpt_ind_data_by_devtype(MVL_VAR_ASSOC** info_va,
ied = find_ied_from_dev_code(LD_info->terminal_code);
ied_usr_t* ied_usr = (ied_usr_t*)ied->usr_ext;
json_trace_block_create_start(LD_info->voltage_level, LD_info->mp_id, rptinfo->flickerflag, ied_usr->dev_type, LD_info->line_id, LD_info->v_wiring_type, rcb_info->RptID, rptinfo->rptNo);
if (rptinfo->flickerflag==1)//CZY 2023-08-17 WW 2022-11-14 只有闪变和第一次进入才会初始化 json_block_create_start(LD_info->line_id);
json_block_create_start( LD_info->voltage_level, LD_info->mp_id, rptinfo->flickerflag, ied_usr->dev_type, LD_info->line_id);
@@ -1057,6 +1058,7 @@ ST_VOID u_iec_rpt_ind_data_by_devtype(MVL_VAR_ASSOC** info_va,
}
else {
json_block_create_flag(LD_info->mp_id, flag, rptinfo->flickerflag);
json_trace_block_create_flag(LD_info->mp_id, flag, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
not_set_rpt_q_this = FALSE;
}
}
@@ -1076,6 +1078,7 @@ ST_VOID u_iec_rpt_ind_data_by_devtype(MVL_VAR_ASSOC** info_va,
}
else {
json_block_create_time(LD_info->mp_id, t / 1000, rptinfo->flickerflag);
json_trace_block_create_time(LD_info->mp_id, t / 1000, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
printf("rcb_info->RptID=%s ,LineId=%d , Timestamp= %lld \n", rcb_info->RptID, LD_info->line_id, t / 1000);
not_set_rpt_TimeID_this = FALSE;
/*if (strstr(rcb_info->RptID, "LLN0$RP$urcbRealData")) {//lnk 20250624
@@ -1108,8 +1111,10 @@ ST_VOID u_iec_rpt_ind_data_by_devtype(MVL_VAR_ASSOC** info_va,
else if (strstr(rcb_info->RptID, "RDRE")) {//CZY 2023-08-17 WW 2022-11-14 修改判断LLN0$BR$brcbRDRE
processRDRE_data(LD_info, FULL_FCDA_Name, v);
}
else
else {
json_block_create_data(LD_info->mp_id, FULL_FCDA_Name, v, rptinfo->flickerflag);
json_trace_block_create_data(LD_info->mp_id, FULL_FCDA_Name, v, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
}
}//else
}
}
@@ -1126,6 +1131,7 @@ ST_VOID u_iec_rpt_ind_data_by_devtype(MVL_VAR_ASSOC** info_va,
}
else {
printf("%d : %d", LD_info->rptRecvFlag, LD_info->rptRecvCheckFlag);
json_trace_block_create_end(LD_info->v_wiring_type, LD_info->mp_id, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
//append_db_records(RPT_IDX);
if (rptinfo->flickerflag==1)//CZY 2023-08-17 WW 2022-11-14 增加闪变标志
{
@@ -1376,6 +1382,7 @@ ST_VOID u_iec_rpt_ind_data(MVL_VAR_ASSOC** info_va,
ied = find_ied_from_dev_code(LD_info->terminal_code);
ied_usr_t* ied_usr = (ied_usr_t*)ied->usr_ext;
json_trace_block_create_start(LD_info->voltage_level, LD_info->mp_id, rptinfo->flickerflag, ied_usr->dev_type, LD_info->line_id, LD_info->v_wiring_type, rcb_info->RptID, rptinfo->rptNo);
if (rptinfo->flickerflag == 1)//CZY 2023-08-17 WW 2022-11-14 只有闪变和第一次进入才会初始化 json_block_create_start(LD_info->line_id);
json_block_create_start(LD_info->voltage_level, LD_info->mp_id, rptinfo->flickerflag, ied_usr->dev_type, LD_info->line_id);
else if (rptinfo->flickerflag == 0) {
@@ -1461,6 +1468,7 @@ ST_VOID u_iec_rpt_ind_data(MVL_VAR_ASSOC** info_va,
}
else {
json_block_create_flag(LD_info->mp_id, flag, rptinfo->flickerflag);
json_trace_block_create_flag(LD_info->mp_id, flag, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
not_set_rpt_q_this = FALSE;
}
}
@@ -1484,6 +1492,7 @@ ST_VOID u_iec_rpt_ind_data(MVL_VAR_ASSOC** info_va,
}
else {
json_block_create_time(LD_info->mp_id, t / 1000, rptinfo->flickerflag);
json_trace_block_create_time(LD_info->mp_id, t / 1000, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
printf("rcb_info->RptID=%s ,LineId=%d , Timestamp= %lld \n", rcb_info->RptID, LD_info->line_id, t / 1000);
not_set_rpt_TimeID_this = FALSE;
//if (strstr(rcb_info->RptID, "LLN0$RP$urcbRealData")) {
@@ -1497,6 +1506,7 @@ ST_VOID u_iec_rpt_ind_data(MVL_VAR_ASSOC** info_va,
printf("rtdata RptID match");
if (urcbRealDataHasReceived(ied_usr->dev_idx,rpt_no,LD_info, t / 1000)){//判断时间重复
printf("this rt report Time repeats");
//json_trace_block_create_end(LD_info->v_wiring_type, LD_info->mp_id, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
return;
}
}
@@ -1531,8 +1541,10 @@ ST_VOID u_iec_rpt_ind_data(MVL_VAR_ASSOC** info_va,
ied_usr_t* ied_usr = GET_IEDEXT_ADDR(ied);
processGGIO_start_data_end(LD_info->mp_id, FULL_FCDA_Name, v, time, ied_usr->dev_type, LD_info->line_id);//GGIO数据全套处理流程
}
else
else {
json_block_create_data(LD_info->mp_id, FULL_FCDA_Name, v, rptinfo->flickerflag);
json_trace_block_create_data(LD_info->mp_id, FULL_FCDA_Name, v, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
}
}//else
}
@@ -1550,6 +1562,7 @@ ST_VOID u_iec_rpt_ind_data(MVL_VAR_ASSOC** info_va,
}
else {
printf("%d : %d", LD_info->rptRecvFlag, LD_info->rptRecvCheckFlag);
json_trace_block_create_end(LD_info->v_wiring_type, LD_info->mp_id, rptinfo->flickerflag, rcb_info->RptID, rptinfo->rptNo);
//append_db_records(RPT_IDX);
if (rptinfo->flickerflag == 1)//CZY 2023-08-17 WW 2022-11-14 增加闪变标志
{

View File

@@ -80,6 +80,9 @@
#include <ctype.h> //lnk20241119
#include "../cfg_parse/custom_printf.h"//lnk20250225
#include "../log4cplus/log4.h"
extern uint32_t g_node_id;
extern char subdir[128];
unsigned int g_no_auth = 0;
@@ -132,6 +135,8 @@ IDENT_RESP_INFO identify_response_info =
/************************************************************************/
extern pt61850app_t *g_pt61850app;
extern TP0_CONN *tp0_conn_arr; /* ptr to array of "max_num_conns" structs */
static ST_VOID disc_ind_fun (MVL_NET_INFO *cc, ST_INT discType);
@@ -269,12 +274,25 @@ MY_CONTROL_INFO my_control_info;
ST_INT mms_var_type_id_create (MVL_NET_INFO *clientNetInfo, ST_INT scope,
ST_CHAR *dom_name, ST_CHAR *var_name, int iTimeOut)
{
MVL_REQ_PEND *reqCtrl;
//MVL_REQ_PEND *reqCtrl;
//reqCtrl 必须初始化为 NULL防止 mvla_getvar 失败后释放野指针
MVL_REQ_PEND *reqCtrl = NULL;
GETVAR_REQ_INFO getvar_req;
ST_INT type_id = -1; /* start with invalid type id */
ST_RET ret;
//参数合法性检查
if (clientNetInfo == NULL || dom_name == NULL || var_name == NULL)
{
printf("[GETVAR] invalid arg netInfo=%p dom=%p var=%p\n",
clientNetInfo, dom_name, var_name);
return -1;
}
//结构体清零,避免未初始化字段导致异常
memset(&getvar_req, 0, sizeof(getvar_req));
/* Get the type of this "Oper" attribute & create type. */
/* Would be more efficient to do this just once before this function.*/
getvar_req.req_tag = GETVAR_NAME;
@@ -284,18 +302,52 @@ ST_RET ret;
getvar_req.name.domain_id= dom_name;
getvar_req.name.obj_name.vmd_spec = var_name;
//增加调试打印,确认崩溃点
printf("[GETVAR] start dom=%s var=%s\n", dom_name, var_name);
ret = mvla_getvar (clientNetInfo, &getvar_req, &reqCtrl);
if (ret == SD_SUCCESS)
//打印 mvla_getvar 返回值和 reqCtrl
printf("[GETVAR] mvla_getvar ret=0x%X reqCtrl=%p\n", ret, reqCtrl);
if (ret == SD_SUCCESS){
ret = waitReqDone (reqCtrl, iTimeOut);
if (ret != SD_SUCCESS)
//打印 waitReqDone 返回值
printf("[GETVAR] wait ret=0x%X\n", ret);
}
if (ret != SD_SUCCESS){
echo_warn2 ("Error getting type of variable '%s' in domain '%s'\n", var_name, dom_name);
}
else
{
/* Don't care about name so pass NULL. */
type_id = mvl_type_id_create (NULL, reqCtrl->u.getvar.resp_info->type_spec.data,
//type_id = mvl_type_id_create (NULL, reqCtrl->u.getvar.resp_info->type_spec.data,
// reqCtrl->u.getvar.resp_info->type_spec.len);
//严格检查 resp_info/type_spec避免空指针崩溃
if (ret == SD_SUCCESS &&
reqCtrl != NULL &&
reqCtrl->u.getvar.resp_info != NULL &&
reqCtrl->u.getvar.resp_info->type_spec.data != NULL &&
reqCtrl->u.getvar.resp_info->type_spec.len > 0)
{
type_id = mvl_type_id_create(
NULL,
reqCtrl->u.getvar.resp_info->type_spec.data,
reqCtrl->u.getvar.resp_info->type_spec.len);
printf("[GETVAR] create type_id=%d len=%d\n",
type_id,
reqCtrl->u.getvar.resp_info->type_spec.len);
}
mvl_free_req_ctrl (reqCtrl); /* Done with request struct */
else
{
printf("[GETVAR] failed dom=%s var=%s ret=0x%X\n",
dom_name, var_name, ret);
}
}
//只有 reqCtrl 非空才释放
if (reqCtrl != NULL)mvl_free_req_ctrl (reqCtrl); /* Done with request struct */
return (type_id);
}
@@ -1683,20 +1735,20 @@ static ST_VOID *my_realloc_err (ST_VOID *old, ST_UINT size)
}
/////////////////////////////////////////////////////////////////////////
//#define MAX_FILE_HANDLE_NUM (256)
//static FILE *fp_arr[MAX_FILE_HANDLE_NUM];
//static ST_INT32 cur_handle = 0;
//ST_INT32 set_file_pointer( FILE *fp)
//{
// ST_INT32 the_handle = cur_handle;
// fp_arr[cur_handle++] = fp;
// cur_handle %= MAX_FILE_HANDLE_NUM;
// return the_handle;
//}
//FILE* get_file_pointer(ST_INT32 handle)
//{
// return fp_arr[handle];
//}
#define MAX_FILE_HANDLE_NUM (256)
static FILE *fp_arr[MAX_FILE_HANDLE_NUM];
static ST_INT32 cur_handle = 0;
ST_INT32 set_file_pointer( FILE *fp)
{
ST_INT32 the_handle = cur_handle;
fp_arr[cur_handle++] = fp;
cur_handle %= MAX_FILE_HANDLE_NUM;
return the_handle;
}
FILE* get_file_pointer(ST_INT32 handle)
{
return fp_arr[handle];
}
///////////////////////////////////////////////////////////////////////
#if (MMS_FOPEN_EN & RESP_EN)
@@ -1705,39 +1757,39 @@ static ST_VOID *my_realloc_err (ST_VOID *old, ST_UINT size)
/************************************************************************/
ST_VOID u_mvl_fopen_ind (MVL_IND_PEND *indCtrl)
{
//FILE *fp;
//FOPEN_RESP_INFO resp_info;
//struct stat stat_buf;
FILE *fp;
FOPEN_RESP_INFO resp_info;
struct stat stat_buf;
//fp = fopen (indCtrl->u.fopen.filename, "rb"); /* CRITICAL: use "b" flag for binary transfer*/
//if (fp == NULL)
// {
// _mplas_err_resp (indCtrl,11,6); /* File-access denied */
// return;
// }
//if (fseek (fp, indCtrl->u.fopen.init_pos, SEEK_SET))
// {
// _mplas_err_resp (indCtrl,11,5); /* Position invalid */
// return;
// }
fp = fopen (indCtrl->u.fopen.filename, "rb"); /* CRITICAL: use "b" flag for binary transfer*/
if (fp == NULL)
{
_mplas_err_resp (indCtrl,11,6); /* File-access denied */
return;
}
if (fseek (fp, indCtrl->u.fopen.init_pos, SEEK_SET))
{
_mplas_err_resp (indCtrl,11,5); /* Position invalid */
return;
}
///* WARNING: this only works if (FILE *) is a 32-bit pointer. */
//resp_info.frsmid = set_file_pointer(fp); //(ST_INT32) fp;
/* WARNING: this only works if (FILE *) is a 32-bit pointer. */
resp_info.frsmid = set_file_pointer(fp); //(ST_INT32) fp;
//if (fstat (fileno (fp), &stat_buf))
// { /* Can't get file size or time */
// _mplas_err_resp (indCtrl,11,0); /* File Problem, Other */
// return;
// }
//else
// {
// resp_info.ent.fsize = stat_buf.st_size;
// resp_info.ent.mtimpres = SD_TRUE;
// resp_info.ent.mtime = stat_buf.st_mtime;
// }
if (fstat (fileno (fp), &stat_buf))
{ /* Can't get file size or time */
_mplas_err_resp (indCtrl,11,0); /* File Problem, Other */
return;
}
else
{
resp_info.ent.fsize = stat_buf.st_size;
resp_info.ent.mtimpres = SD_TRUE;
resp_info.ent.mtime = stat_buf.st_mtime;
}
//indCtrl->u.fopen.resp_info = &resp_info;
//mplas_fopen_resp (indCtrl);
indCtrl->u.fopen.resp_info = &resp_info;
mplas_fopen_resp (indCtrl);
}
#endif /* MMS_FOPEN_EN & RESP_EN */
@@ -1748,31 +1800,31 @@ ST_VOID u_mvl_fopen_ind (MVL_IND_PEND *indCtrl)
/************************************************************************/
ST_VOID u_mvl_fread_ind (MVL_IND_PEND *indCtrl)
{
//FILE *fp;
//ST_UCHAR *tmp_buf;
//MVLAS_FREAD_CTRL *fread_ctrl = &indCtrl->u.fread;
//FREAD_RESP_INFO resp_info;
FILE *fp;
ST_UCHAR *tmp_buf;
MVLAS_FREAD_CTRL *fread_ctrl = &indCtrl->u.fread;
FREAD_RESP_INFO resp_info;
//fp = get_file_pointer(fread_ctrl->req_info->frsmid);// (FILE *) fread_ctrl->req_info->frsmid;
///* Do NOT read more than "max_size". */
//tmp_buf = (ST_UCHAR *) chk_malloc (fread_ctrl->max_size);
fp = get_file_pointer(fread_ctrl->req_info->frsmid);// (FILE *) fread_ctrl->req_info->frsmid;
/* Do NOT read more than "max_size". */
tmp_buf = (ST_UCHAR *) chk_malloc (fread_ctrl->max_size);
//resp_info.fd_len = fread (tmp_buf, 1, fread_ctrl->max_size, fp);
//if (resp_info.fd_len == 0 && ferror (fp))
// {
// _mplas_err_resp (indCtrl, 3, 0);
// return;
// }
resp_info.fd_len = fread (tmp_buf, 1, fread_ctrl->max_size, fp);
if (resp_info.fd_len == 0 && ferror (fp))
{
_mplas_err_resp (indCtrl, 3, 0);
return;
}
//resp_info.filedata = tmp_buf;
//if (resp_info.fd_len == fread_ctrl->max_size)
// resp_info.more_follows = SD_TRUE;
//else
// resp_info.more_follows = SD_FALSE;
resp_info.filedata = tmp_buf;
if (resp_info.fd_len == fread_ctrl->max_size)
resp_info.more_follows = SD_TRUE;
else
resp_info.more_follows = SD_FALSE;
//fread_ctrl->resp_info = &resp_info;
//mplas_fread_resp (indCtrl);
//chk_free (tmp_buf); /* Temporary buffer */
fread_ctrl->resp_info = &resp_info;
mplas_fread_resp (indCtrl);
chk_free (tmp_buf); /* Temporary buffer */
}
#endif /* #if (MMS_FREAD_EN & RESP_EN) */
@@ -1782,15 +1834,15 @@ ST_VOID u_mvl_fread_ind (MVL_IND_PEND *indCtrl)
/************************************************************************/
ST_VOID u_mvl_fclose_ind (MVL_IND_PEND *indCtrl)
{
//FILE *fp;
//MVLAS_FCLOSE_CTRL *fclose_ctrl = &indCtrl->u.fclose;
FILE *fp;
MVLAS_FCLOSE_CTRL *fclose_ctrl = &indCtrl->u.fclose;
//fp = get_file_pointer(fclose_ctrl->req_info->frsmid);//(FILE *) fclose_ctrl->req_info->frsmid;
fp = get_file_pointer(fclose_ctrl->req_info->frsmid);//(FILE *) fclose_ctrl->req_info->frsmid;
//if (fclose (fp))
// _mplas_err_resp (indCtrl, 11, 0); /* File problem, other */
//else
// mplas_fclose_resp (indCtrl);
if (fclose (fp))
_mplas_err_resp (indCtrl, 11, 0); /* File problem, other */
else
mplas_fclose_resp (indCtrl);
}
#endif /* #if (MMS_FCLOSE_EN & RESP_EN) */
@@ -1945,6 +1997,295 @@ MVL_REQ_PEND *reqCtrl;
return (ret);
}
//lnk20260508添加重启装置函数
int BuildResetDomName(ied_usr_t *ied_usr, char *domName, size_t domNameSize)
{
if (ied_usr == NULL || domName == NULL || domNameSize == 0)
return -1;
domName[0] = '\0';
XmlConfigC cfg1;
memset(&cfg1, 0, sizeof(cfg1));
if (get_xml_config_by_dev_type(ied_usr->dev_type, &cfg1))
{
printf("========== XmlConfigC dump ==========\n");
printf("IEDControl = '%s'\n", cfg1.IEDControl);
printf("IEDname = '%s'\n", cfg1.IEDname);
printf("LDevicePrefix = '%s'\n", cfg1.LDevicePrefix);
printf("=====================================\n");
if (cfg1.IEDControl[0] != '\0')
{
snprintf(domName, domNameSize, "%s", cfg1.IEDControl);
printf("[RESET] use cfg1.IEDControl=%s\n", domName);
return 0;
}
}
if (ied_usr->LD_info && ied_usr->LD_info[0].LD_name)
{
snprintf(domName, domNameSize, "%s", ied_usr->LD_info[0].LD_name);
int len = strlen(domName);
if (len > 0 && isdigit(domName[len - 1]))
domName[len - 1] = '0';
printf("[RESET] use LD_name domName=%s\n", domName);
DIY_WARNLOG_CODE(ied_usr->terminal_id, 1, LOG_CODE_FILE_CONTROL,
"【WARN】未取到 IEDControl 信息,使用 LD_name=%s terminal_id=%s",
domName, ied_usr->terminal_id);
return -2;
}
snprintf(domName, domNameSize, "%s", "PQMonitorPQM0");
printf("[RESET] use default domName=%s\n", domName);
DIY_ERRORLOG_CODE(ied_usr->terminal_id, 1, LOG_CODE_FILE_CONTROL,
"【ERROR】未取到 LD 信息,使用默认 domName=%s terminal_id=%s",
domName, ied_usr->terminal_id);
return -1;
}
static ST_INT ledrs_var_type_create(MVL_NET_INFO* net_info,
OBJECT_NAME* varObj,
ST_INT timeOut)
{
MVL_REQ_PEND* reqCtrl;
GETVAR_REQ_INFO getvar_req;
VAR_ACC_TSPEC* type_spec;
ST_INT type_id = -1;
ST_RET ret;
memset(&getvar_req, 0, sizeof(getvar_req));
getvar_req.req_tag = GETVAR_NAME;
getvar_req.name = *varObj;
ret = mvla_getvar(net_info, &getvar_req, &reqCtrl);
if (ret == SD_SUCCESS)
ret = waitReqDone(reqCtrl, timeOut);
if (ret == SD_SUCCESS)
{
type_spec = &reqCtrl->u.getvar.resp_info->type_spec;
type_id = mvl_type_id_create(NULL,
type_spec->data,
type_spec->len);
}
if (reqCtrl)
mvl_free_req_ctrl(reqCtrl);
return type_id;
}
static ST_INT create_oper_type_id(MVL_NET_INFO *net_info,
ST_CHAR *domName,
const char *ctlName,
ST_INT timeOut)
{
OBJECT_NAME obj;
ST_CHAR varName[MAX_IDENT_LEN + 1];
if (net_info == NULL || domName == NULL || ctlName == NULL)
return -1;
memset(&obj, 0, sizeof(obj));
apr_snprintf(varName,
sizeof(varName),
"LLN0$%s",
ctlName);
obj.object_tag = DOM_SPEC;
obj.domain_id = domName;
obj.obj_name.vmd_spec = varName;
printf("[CTRL_INIT] create type dom=%s var=%s\n",
domName, varName);
return ledrs_var_type_create(net_info, &obj, timeOut);
}
void InitLedrsOperTypeForChannel(chnl_usr_t *chnl_usr)
{
printf("[CTRL_INIT] enter\n");
if (chnl_usr == NULL || chnl_usr->chnl == NULL ||
chnl_usr->chnl->ied == NULL || chnl_usr->net_info == NULL)
{
printf("[CTRL_INIT] invalid chnl_usr\n");
return;
}
ied_t *ied = chnl_usr->chnl->ied;
ied_usr_t *ied_usr = GET_IEDEXT_ADDR(ied);
if (ied_usr == NULL || ied_usr->LD_info == NULL)
{
printf("[CTRL_INIT] invalid ied_usr or LD_info\n");
return;
}
printf("[CTRL_INIT] current inited=%d ledrs=%d reboot=%d reset=%d\n",
(int)ied_usr->oper_type_cache.inited,
ied_usr->oper_type_cache.ledrs_oper_type_id,
ied_usr->oper_type_cache.reboot_oper_type_id,
ied_usr->oper_type_cache.reset_oper_type_id);
if (ied_usr->oper_type_cache.inited == SD_TRUE)
{
printf("[CTRL_INIT] already inited\n");
return;
}
char domName[256] = {0};
BuildResetDomName(ied_usr,
domName,
sizeof(domName));
printf("[CTRL_INIT] final dom=%s\n", domName);
ied_usr->oper_type_cache.ledrs_oper_type_id =
create_oper_type_id(chnl_usr->net_info,
domName,
"CO$LEDRs$Oper",
g_pt61850app->mmsOpTimeout);
printf("[CTRL_INIT] LEDRs type_id=%d\n",
ied_usr->oper_type_cache.ledrs_oper_type_id);
ied_usr->oper_type_cache.reboot_oper_type_id =
create_oper_type_id(chnl_usr->net_info,
domName,
"CO$Reboot$Oper",
g_pt61850app->mmsOpTimeout);
printf("[CTRL_INIT] Reboot type_id=%d\n",
ied_usr->oper_type_cache.reboot_oper_type_id);
ied_usr->oper_type_cache.reset_oper_type_id =
create_oper_type_id(chnl_usr->net_info,
domName,
"ST$Mod$stVal",
g_pt61850app->mmsOpTimeout);
printf("[CTRL_INIT] Reset type_id=%d\n",
ied_usr->oper_type_cache.reset_oper_type_id);
/* 无论成功失败,都不再重复初始化 */
ied_usr->oper_type_cache.inited = SD_TRUE;
printf("[CTRL_INIT] finish inited=%d\n",
(int)ied_usr->oper_type_cache.inited);
}
ST_RET write_common_oper(chnl_usr_t *chnl_usr,
ST_CHAR *domName,
const char *ctlName,
ST_INT oper_type_id,
ST_INT timeOut)
{
if (chnl_usr == NULL ||
chnl_usr->net_info == NULL ||
domName == NULL ||
ctlName == NULL ||
oper_type_id < 0)
{
printf("[OPER_WRITE] invalid param\n");
return SD_FAILURE;
}
ST_CHAR varName[MAX_IDENT_LEN + 1];
apr_snprintf(varName,
sizeof(varName),
"LLN0$%s",
ctlName);
Control_Oper_t oper;
memset(&oper, 0, sizeof(oper));
oper.ctlVal = SD_TRUE;
oper.origin.orCat = 3;
oper.origin.orIdent.len = 0;
oper.ctlNum = 1;
u_get_current_utc_time(&oper.T);
oper.Test = SD_FALSE;
oper.Check[0] = 0x00;
oper.Check[1] = 0x00;
if ((int)sizeof(Control_Oper_t) !=
mvl_type_ctrl[oper_type_id].data_size)
{
printf("[OPER_WRITE] SIZE MISMATCH ctl=%s local=%d runtime=%d\n",
ctlName,
(int)sizeof(Control_Oper_t),
mvl_type_ctrl[oper_type_id].data_size);
return SD_FAILURE;
}
printf("[OPER_WRITE] dom=%s var=%s type_id=%d\n",
domName, varName, oper_type_id);
return mms_named_var_write(chnl_usr->net_info,
varName,
DOM_SPEC,
domName,
oper_type_id,
(ST_CHAR *)&oper,
timeOut);
}
ST_RET mms_conclude_disconnect(MVL_NET_INFO *net_info, ST_INT timeOut)
{
MVL_REQ_PEND *reqCtrl = NULL;
ST_RET ret;
if (net_info == NULL)
return SD_FAILURE;
printf("[RESET] before mvl_concl\n");
ret = mvl_concl(net_info, &reqCtrl);
printf("[RESET] after mvl_concl ret=0x%X reqCtrl=%p\n",
ret, reqCtrl);
if (ret == SD_SUCCESS && reqCtrl != NULL)
{
ret = waitReqDone(reqCtrl, timeOut);
printf("[RESET] conclude wait ret=0x%X\n", ret);
}
if (reqCtrl != NULL)
mvl_free_req_ctrl(reqCtrl);
return ret;
}
ST_RET write_mod_stval(chnl_usr_t *chnl_usr,
ST_CHAR *domName,
ST_INT timeOut)
{
ST_CHAR varName[MAX_IDENT_LEN + 1];
ST_INT16 value = 88;
apr_snprintf(varName, sizeof(varName), "LLN0$ST$Mod$stVal");
return mms_named_var_write(chnl_usr->net_info,
varName,
DOM_SPEC,
domName,
14, //int
(ST_CHAR *)&value,
timeOut);
}
/************************************************************************/
/* init_log_cfg */

View File

@@ -135,6 +135,15 @@ int fill_rptctrl_by_cfg(LD_info_t* LD_info,int rptno,char *buf)
assert( (rptno+1) <= LD_info->rptcount ) ;
rptinfo = LD_info->rptinfo[rptno];
if (rptinfo == NULL) {
printf("[RPT_INIT][ERROR] rptinfo NULL, rptno=%d\n", rptno);
return 1;
}
rptinfo->LD_info = LD_info;
if(!(str = strtok(buf,",")))
return 1;
@@ -262,6 +271,13 @@ int fill_logctrl_by_cfg(LD_info_t* LD_info,int logno,char *buf,char* devtype)
assert( (logno+1) <= LD_info->logcount ) ;
loginfo = LD_info->loginfo[logno];
if (loginfo == NULL) {
printf("[LOG_INIT][ERROR] loginfo NULL, logno=%d\n", logno);
return 1;
}
loginfo->LD_info = LD_info;
if(!(str = strtok(buf,",")))
return 1;
tmp_str = apr_pstrdup(g_init_pool,str);

View File

@@ -205,6 +205,7 @@ apr_status_t init_rdb()
//台账读取过后初始化各级的日志
init_loggers();
if (g_node_id != PQDIF_DATA_BASE_NODE_ID) {
rv = parse_model_cfg_web();
if (rv != APR_SUCCESS) {//不可能
echo_errg("Parsed model with error,try to run! \n");
@@ -228,6 +229,10 @@ apr_status_t init_rdb()
return rv;
}
}
else {
printf("[PQDIF] skip model xml and report init in init_rdb\n");
}
}
if (app_get_private_config(g_my_conf_fname) != APR_SUCCESS) {
echo_errg("Failed when processing private configuration\n");
@@ -311,6 +316,9 @@ apr_status_t run_protocol()
else if (g_node_id == SOE_COMTRADE_BASE_NODE_ID) {//暂态录波
ServerPort = SOCKET_PORT + SOE_COMTRADE_BASE_NODE_ID + g_front_seg_index;
}
else if (g_node_id == PQDIF_DATA_BASE_NODE_ID) {
ServerPort = SOCKET_PORT + PQDIF_DATA_BASE_NODE_ID + g_front_seg_index;
}
struct sockaddr_in server_sockaddr;
memset(&server_sockaddr, 0, sizeof(server_sockaddr));
@@ -351,6 +359,9 @@ apr_status_t run_protocol()
else if (g_node_id == SOE_COMTRADE_BASE_NODE_ID) {//暂态录波
HTTP_PORT = HTTP_PORT + SOE_COMTRADE_BASE_NODE_ID + g_front_seg_index;
}
else if (g_node_id == PQDIF_DATA_BASE_NODE_ID) {
HTTP_PORT = HTTP_PORT + PQDIF_DATA_BASE_NODE_ID + g_front_seg_index;
}
printf("try_start_web_http_thread \n");
try_start_web_http_thread();
printf("try_start_http_thread \n");

View File

@@ -320,12 +320,24 @@ struct LD_info_t{
//录波
};
//装置控制初始化
typedef struct
{
ST_BOOLEAN inited;
ST_INT ledrs_oper_type_id;
ST_INT reboot_oper_type_id;
ST_INT reset_oper_type_id;
} MMS_OPER_TYPE_CACHE;
struct ied_usr_t{
LD_info_t *LD_info; /**< LD数组 */
int dev_idx; /**< 设备序号 */
char dev_type[256]; /**< 设备类型 */
char dev_key[256]; /**< 设备秘钥 */
char dev_series[256]; /**< 设备识别码 */
char com_type[32]; /**< 通讯类型 mms/pqdif */
int dev_flag; /**< 设备标志 */
void *cookie;
double last_call_wavelist_time ; //上次召录波列表时间
@@ -346,6 +358,8 @@ struct ied_usr_t{
bool lastconnectstat;//lnk20250704
bool has_logged_disconnect;//lnk20250704
MMS_OPER_TYPE_CACHE oper_type_cache;
};
@@ -533,6 +547,35 @@ int parse_file_names_by_fltnum(int fltnum, char* domname, char** filenames, int
QVVR_t* find_qvvr_by_trig_tm(LD_info_t* LD_info,long long trig_tm);
void HandleFileDirReqForChannel(chnl_usr_t *chnl_usr);
void InitLedrsOperTypeForChannel(chnl_usr_t *chnl_usr);
//lnk20250508添加重启装置函数
//根据抓包显示oper的data结构有6个item
typedef struct
{
ST_BOOLEAN ctlVal;
struct
{
ST_INT16 orCat;
struct
{
ST_INT16 len;
ST_UINT8 data[64];
} orIdent;
} origin;
ST_UINT32 ctlNum;
MMS_UTC_TIME T;
ST_BOOLEAN Test;
ST_UCHAR Check[2];
} Control_Oper_t;
//////////////////////////////////////////////////////////////////

View File

@@ -145,12 +145,26 @@ RCB_INFO* FindRcbInfo(MVL_NET_INFO *net_info,ST_CHAR *dom_name, ST_CHAR *rcb_nam
for(cpuno=0 ; cpuno<ied->cpucount; cpuno++) {
LD_info = &(ied_usr->LD_info[cpuno]);
if (!LD_info->LD_name)
//if (!LD_info->LD_name)
// continue;
//添加保护lnk20260602
if (LD_info->cpuno == 0 ||
LD_info->LD_name == NULL ||
LD_info->rptcount <= 0 ||
LD_info->rptinfo == NULL)
{
continue;
}
if ( strcmp(LD_info->LD_name,dom_name)!=0 )
continue;
for(rpt_no=0 ; rpt_no<LD_info->rptcount; rpt_no++) {
rptinfo = LD_info->rptinfo[rpt_no];
//添加保护lnk20260602
if (rptinfo == NULL || rptinfo->rptID == NULL)
continue;
get_rpt_inst_name(rptinfo,rpt_inst_name);
if ( strcmp(rpt_inst_name,rcb_name)==0 ) {
if (rptinfo->rpt_registered)
@@ -181,8 +195,22 @@ rptinfo_t* find_rptinfo_from_net_rpt_info_name(MVL_NET_INFO *net_info, RCB_INFO
for(cpuno=0 ; cpuno<ied->cpucount; cpuno++) {
LD_info = &(ied_usr->LD_info[cpuno]);
//添加保护lnk20260602
if (LD_info->cpuno == 0 ||
LD_info->LD_name == NULL ||
LD_info->rptcount <= 0 ||
LD_info->rptinfo == NULL)
{
continue;
}
for(rpt_no=0 ; rpt_no<LD_info->rptcount; rpt_no++) {
rptinfo = LD_info->rptinfo[rpt_no];
//添加保护lnk20260602
if (rptinfo == NULL || rptinfo->rptID == NULL || rcb_info == NULL || rcb_info->RptID == NULL)
continue;
printf("%d rptinfo %s,rcbinfo %s ", rpt_no, rptinfo->rptID, rcb_info->RptID);
if (strcmp(rcb_info->RptID,rptinfo->rptID)==0)//WW 修改为匹配字符串
return rptinfo;
@@ -211,8 +239,21 @@ rptinfo_t* find_rptinfo_from_net_rcb_info(MVL_NET_INFO *net_info,RCB_INFO *rcb_i
for(cpuno=0 ; cpuno<ied->cpucount; cpuno++) {
LD_info = &(ied_usr->LD_info[cpuno]);
//添加保护lnk20260602
if (LD_info->cpuno == 0 ||
LD_info->LD_name == NULL ||
LD_info->rptcount <= 0 ||
LD_info->rptinfo == NULL)
{
continue;
}
for(rpt_no=0 ; rpt_no<LD_info->rptcount; rpt_no++) {
rptinfo = LD_info->rptinfo[rpt_no];
if (rptinfo == NULL)
continue;
if (rcb_info==rptinfo->m_rcb_info)
return rptinfo;
}
@@ -221,7 +262,7 @@ rptinfo_t* find_rptinfo_from_net_rcb_info(MVL_NET_INFO *net_info,RCB_INFO *rcb_i
}
////////////////////////////////////////
//WW 2023-08-29 注释 end
void get_rpt_inst_name(rptinfo_t *rptinfo, char * rpt_inst_name )
/*void get_rpt_inst_name(rptinfo_t *rptinfo, char * rpt_inst_name )
{
strcpy(rpt_inst_name,rptinfo->rptID);
if (rptinfo->instanceNeedSuffix) {
@@ -229,6 +270,55 @@ void get_rpt_inst_name(rptinfo_t *rptinfo, char * rpt_inst_name )
apr_snprintf(rpt_suffix_str,sizeof(rpt_suffix_str),"%02d",rptinfo->m_curRptSuffix);
strcat(rpt_inst_name,rpt_suffix_str);
}
} */
void get_rpt_inst_name(rptinfo_t *rptinfo, char *rpt_inst_name)
{
if (rpt_inst_name == NULL)
{
printf("get_rpt_inst_name: rpt_inst_name is NULL\n");
return;
}
rpt_inst_name[0] = '\0';
if (rptinfo == NULL)
{
printf("get_rpt_inst_name: rptinfo is NULL\n");
strcpy(rpt_inst_name, "NULL_RPT");
return;
}
if (rptinfo->rptID == NULL)
{
printf("get_rpt_inst_name: rptID is NULL\n");
strcpy(rpt_inst_name, "NULL_RPT");
return;
}
printf("[RPT] rptinfo=%p rptID=%s suffix=%d needSuffix=%d\n",
rptinfo,
rptinfo->rptID,
rptinfo->m_curRptSuffix,
rptinfo->instanceNeedSuffix);
// 原代码
strncpy(rpt_inst_name, rptinfo->rptID, 64);
rpt_inst_name[64] = '\0';
if (rptinfo->instanceNeedSuffix)
{
char rpt_suffix_str[8] = {0};
apr_snprintf(rpt_suffix_str,
sizeof(rpt_suffix_str),
"%02d",
rptinfo->m_curRptSuffix);
// 防止越界
strncat(rpt_inst_name,
rpt_suffix_str,
64 - strlen(rpt_inst_name));
}
}
@@ -359,12 +449,20 @@ LD_info_t* find_LD_info_only_from_line_id(int line_id)
LD_info_t* find_LD_info_from_mp_id(ied_t* ied, char* mp_id)
{
if (mp_id == NULL)
return NULL;
LD_info_t* LD_info = NULL;
ied_usr_t* ied_usr = GET_IEDEXT_ADDR(ied);
int cpuno;
for (cpuno = 0; cpuno < ied->cpucount; cpuno++) {
LD_info = &(ied_usr->LD_info[cpuno]);
if (LD_info->cpuno == 0 || LD_info->LD_name == NULL)
continue;
if (LD_info && strcmp(LD_info->mp_id, mp_id) == 0)
return LD_info;
}
@@ -662,10 +760,10 @@ void processQVVR_end(LD_info_t* LD_info)
//mq日志
if(strcmp(cfg.UnitOfTimeUnit, "1") == 0){//持续时间上送的是秒1还是毫秒0
DIY_WARNLOG_CODE(LD_info->mp_id,2,LOG_CODE_TRANSIENT,"【WARN】监测点:%s - id:%s记录了一个暂态事件但是没有匹配到事件的发生时间,装置型号:%s 配置的暂态持续时间单位是",LD_info->name,LD_info->mp_id,ied_usr->dev_type);
DIY_WARNLOG_CODE(LD_info->mp_id,2,LOG_CODE_TRANSIENT,"【WARN】监测点:%s - id:%s发生了暂态事件但是没有记录,可能是配置的暂态持续时间不匹配,当前装置型号:%s ,当前使用的配置的暂态持续时间单位是s",LD_info->name,LD_info->mp_id,ied_usr->dev_type);
}
else{
DIY_WARNLOG_CODE(LD_info->mp_id,2,LOG_CODE_TRANSIENT,"【WARN】监测点:%s - id:%s记录了一个暂态事件但是没有匹配到事件的发生时间,装置型号:%s 配置的暂态持续时间单位是毫秒",LD_info->name,LD_info->mp_id,ied_usr->dev_type);
DIY_WARNLOG_CODE(LD_info->mp_id,2,LOG_CODE_TRANSIENT,"【WARN】监测点:%s - id:%s发生了暂态事件但是没有记录,可能是配置的暂态持续时间不匹配,当前装置型号:%s ,当前使用的配置的暂态持续时间单位是ms",LD_info->name,LD_info->mp_id,ied_usr->dev_type);
}
}
@@ -684,27 +782,45 @@ void processQVVR_end(LD_info_t* LD_info)
// - 如果没有找到符合条件的 QVVR 数据,返回 NULL。
QVVR_t* find_qvvr_by_trig_tm(LD_info_t* LD_info, long long trig_tm)
{
long long diff; // 用于计算时间戳差异
int i; // 循环计数器
const long long EIGHT_HOURS_MS = 8LL * 60 * 60 * 1000;
// 遍历 LD_info 中的 QVVR 数据数组
for (i = 0; i < QVVR_NUM; i++) {
// 计算当前 QVVR 数据的时间戳与触发时间戳之间的差异
diff = abs(LD_info->qvvr[i].QVVR_time - trig_tm);
long long candidates[3] = {
trig_tm,
trig_tm - EIGHT_HOURS_MS,
trig_tm + EIGHT_HOURS_MS
};
//调试用
printf("QVVRTIME:%lld >>>>> COMTRADE trig_tm:%lld >>>>> diff:%lld\n",LD_info->qvvr[i].QVVR_time,trig_tm,diff);
for (int c = 0; c < 3; ++c) {
long long cmp_tm = candidates[c];
printf("===== try trig_tm:%lld offset:%lld =====\n",
cmp_tm,
cmp_tm - trig_tm);
for (int i = 0; i < QVVR_NUM; i++) {
long long diff =
llabs(LD_info->qvvr[i].QVVR_time - cmp_tm);
printf("QVVRTIME:%lld >>>>> COMTRADE trig_tm:%lld >>>>> diff:%lld\n",
LD_info->qvvr[i].QVVR_time,
cmp_tm,
diff);
if ((LD_info->qvvr[i].used_status == QVVR_DATA_PAIRED)
&& (diff <= 1))
{
printf(">>>>> pair QVVR success>>>>>> offset=%lld\n",
cmp_tm - trig_tm);
// 如果该 QVVR 数据的状态是 "已配对"QVVR_DATA_PAIRED并且时间差小于等于 1单位毫秒
if ((LD_info->qvvr[i].used_status == QVVR_DATA_PAIRED) && (diff <= 1)) {
// 如果匹配,返回该 QVVR 数据的指针
printf(">>>>> pair QVVR success>>>>>> \n");
return &(LD_info->qvvr[i]);
}
}
}
printf(">>>>> pair QVVR fail>>>>>>\n");
// 如果没有找到匹配的 QVVR 数据,返回 NULL
return NULL;
}
#if 0

View File

@@ -74,7 +74,7 @@ FileFlag=4
FrontInst=884d132ac3a01225fcacc8c10da07d09
FrontIP=192.168.1.167
SendFlag=3
RecallOnlyFlag=
RecallOnlyFlag=0
[Ledger]
TerminalStatus="[0]"
@@ -117,37 +117,43 @@ WriteUrl=
[RocketMq]
producer=Group_producer
Ipport=192.168.1.68:9876
Topic=TEST_Topic
Tag=Test_Tag
Key=Test_Keys
TESTTopic=TEST_Topic
TESTTag=884d132ac3a01225fcacc8c10da07d09
TESTKey=Test_Keys
Queuenum=4
Testflag=1
Testnum=100
Testtype=1
Testnum=0
Testtype=0
TestPort=11000
TestList=
consumer=Group_consumer
ConsumerIpport=192.168.1.68:9876
ConsumerTopicRT=ask_real_data_topic
ConsumerTagRT=Test_Tag
ConsumerTagRT=884d132ac3a01225fcacc8c10da07d09
ConsumerKeyRT=Test_Keys
ConsumerAccessKey=rmqroot
ConsumerSecretKey=001@#njcnmq
ConsumerChannel=
ConsumerTopicUD=control_Topic
ConsumerTagUD=Test_Tag
ConsumerTagUD=884d132ac3a01225fcacc8c10da07d09
ConsumerKeyUD=Test_Keys
ConsumerTopicRC=recall_Topic
ConsumerTagRC=Test_Tag
ConsumerTagRC=884d132ac3a01225fcacc8c10da07d09
ConsumerKeyRC=Test_Keys
ConsumerTopicSET=process_Topic
ConsumerTagSET=Test_Tag
ConsumerTagSET=884d132ac3a01225fcacc8c10da07d09
ConsumerKeySET=Test_Keys
ConsumerTopicLOG=ask_log_Topic
ConsumerTagLOG=Test_Tag
ConsumerTagLOG=884d132ac3a01225fcacc8c10da07d09
ConsumerKeyLOG=Test_Keys
ConsumerTopicFILE=File_Topic
ConsumerTagFILE=884d132ac3a01225fcacc8c10da07d09
ConsumerKeyFILE=Test_Keys
ConsumerTopicTEST=File_Topic
LOGTopic=log_Topic
LOGTag=Test_Tag
LOGKey=Test_Keys

View File

@@ -329,6 +329,7 @@ handle_reset() {
sed -i '/cfg_recallhis_data/d' /FeProject/etc/runtime.cf
sed -i '/cfg_3s_data/d' /FeProject/etc/runtime.cf
sed -i '/cfg_soe_comtrade/d' /FeProject/etc/runtime.cf
sed -i '/cfg_pqdif_data/d' /FeProject/etc/runtime.cf
sed -i '/fe_watchdog/d' /FeProject/etc/runtime.cf
# 根据进程号添加对应进程配置
@@ -340,6 +341,7 @@ handle_reset() {
sed -i "2a\\$(printf '/FeProject/bin/ ^ pt61850netd_pqfe -d cfg_recallhis_data -s 1_1^ ^ ^ 1 ^ ^\n')" /FeProject/etc/runtime.cf
sed -i "2a\\$(printf '/FeProject/bin/ ^ pt61850netd_pqfe -d cfg_3s_data^ ^ ^ 1 ^ ^\n')" /FeProject/etc/runtime.cf
sed -i "2a\\$(printf '/FeProject/bin/ ^ pt61850netd_pqfe -d cfg_soe_comtrade^ ^ ^ 1 ^ ^\n')" /FeProject/etc/runtime.cf
sed -i "2a\\$(printf '/FeProject/bin/ ^ pt61850netd_pqfe -d cfg_pqdif_data^ ^ ^ 1 ^ ^\n')" /FeProject/etc/runtime.cf
else
#看门狗固定放在第一个防止stop时会把要杀死的进程重启
@@ -353,6 +355,7 @@ handle_reset() {
#以下部分没有多进程
sed -i "2a\\$(printf '/FeProject/bin/ ^ pt61850netd_pqfe -d cfg_3s_data^ ^ ^ 1 ^ ^\n')" /FeProject/etc/runtime.cf
sed -i "2a\\$(printf '/FeProject/bin/ ^ pt61850netd_pqfe -d cfg_soe_comtrade^ ^ ^ 1 ^ ^\n')" /FeProject/etc/runtime.cf
sed -i "2a\\$(printf '/FeProject/bin/ ^ pt61850netd_pqfe -d cfg_pqdif_data^ ^ ^ 1 ^ ^\n')" /FeProject/etc/runtime.cf
fi