modify log4 function and add data trace function

This commit is contained in:
lnk
2026-03-06 16:27:58 +08:00
parent d5916f5559
commit 748f8481bc
9 changed files with 888 additions and 424 deletions

View File

@@ -21,7 +21,7 @@ using namespace std;
#define OTL_ODBC_UNIX
#include <unistd.h>
#include "otlv4.h"
//#include "otlv4.h"
#include <stdio.h>
#include <sstream> //lnk 2024-10-16
@@ -40,6 +40,17 @@ using namespace std;
#include "../log4cplus/log4.h"//lnk添加log4
#include <cstring>
//同步arm移除otlv头文件
class otl_datetime {
public:
int year;
int month;
int day;
int hour;
int minute;
int second;
};
//用于测试时防止数据激增
#ifndef apr_time_from_msec
#define apr_time_from_msec(ms) ((apr_time_t)(ms) * 1000)
@@ -128,6 +139,8 @@ public:
char timestamp[64];
char status[255];
char count_cfg[64]; //不是台账的一部分,用来记录数据库或业务中台的台账数量
int log_level; //0 ERROR 1 WARN 2 NORMAL 3 DEBUG
};
class terminal_dev //终端台账
@@ -154,6 +167,8 @@ public:
ledger_monitor line[10];
char count_cfg[64]; //不是台账的一部分,用来记录数据库或业务中台的台账数量
int log_level; //0 ERROR 1 WARN 2 NORMAL 3 DEBUG
};
class icd_model //icd模型
@@ -1603,6 +1618,22 @@ void parse_terminal_from_data(trigger_update_xml_t* trigger_update_xml, const st
strcpy(work_terminal.port, extract_value(data, "port").c_str());
strcpy(work_terminal.timestamp, extract_value(data, "updateTime").c_str());
//添加日志等级
std::string level = extract_value(data, "loglevel");
int tmp_level = -1;
if (!level.empty()) {
try {
tmp_level = std::stoi(level);
} catch (...) {
tmp_level = -1;
}
}
if (tmp_level >= 0 && tmp_level <= 3) {
work_terminal.log_level = tmp_level;
} else {
work_terminal.log_level = 1; // 默认 warn
}
//添加guid20250506
strncpy(work_terminal.guid, guid_value.c_str(), sizeof(work_terminal.guid) - 1);
work_terminal.guid[sizeof(work_terminal.guid) - 1] = '\0';
@@ -1637,6 +1668,27 @@ void parse_terminal_from_data(trigger_update_xml_t* trigger_update_xml, const st
strcpy(work_monitor.timestamp, timestamp.empty() ? "N/A" : timestamp.c_str());
strcpy(work_monitor.terminal_code, terminal_code.empty() ? "N/A" : terminal_code.c_str());
strcpy(work_monitor.status, status.empty() ? "N/A" : status.c_str());
std::string log_level = extract_value(data, "loglevel");
int tmp_level = -1;
// 尝试解析 monitor loglevel
if (!log_level.empty()) {
try {
tmp_level = std::stoi(log_level);
} catch (...) {
tmp_level = -1;
}
}
// 判断是否合法
if (tmp_level >= 0 && tmp_level <= 3) {
work_monitor.log_level = tmp_level;
}
else if (work_terminal.log_level >= 0 && work_terminal.log_level <= 3) {
work_monitor.log_level = work_terminal.log_level; // 继承 terminal
}
else {
work_monitor.log_level = 1; // 默认 warn
}
// 将提取的 monitor 数据存入 work_terminal.line[monitor_count]
work_terminal.line[monitor_count] = work_monitor;
@@ -3270,6 +3322,7 @@ void printTerminalDevMap(const QMap<QString, terminal_dev*>& terminal_dev_map) {
<< ", Device maxProcessNum:" << QString(dev->maxProcessNum)
<< ", Address:" << QString(dev->addr_str)
<< ", Port:" << QString(dev->port)
<< ", log_level:" << QString(dev->log_level)
<< ", Timestamp:" << QString(dev->timestamp);
// 打印监测点信息
@@ -3281,6 +3334,7 @@ void printTerminalDevMap(const QMap<QString, terminal_dev*>& terminal_dev_map) {
<< ", Voltage Level:" << QString(dev->line[i].voltage_level)
<< ", Terminal Connect:" << QString(dev->line[i].terminal_connect)
<< ", Timestamp:" << QString(dev->line[i].timestamp)
<< ", log_level:" << QString(dev->line[i].log_level)
<< ", Status:" << QString(dev->line[i].status);
}
} else {
@@ -3310,6 +3364,7 @@ void printLedger(const ied_usr_t& ied_usr) {
std::cout << "|-- tmnl_status: " << ied_usr.tmnl_status << std::endl;
std::cout << "|-- terminal_code: " << ied_usr.terminal_code << std::endl;
std::cout << "|-- update_flag: " << ied_usr.update_flag << std::endl;
std::cout << "|-- log_level: " << ied_usr.log_level << std::endl;
// 打印每个LD_info的内容
for (int i = 0; i < 10; ++i) {
@@ -3321,6 +3376,7 @@ void printLedger(const ied_usr_t& ied_usr) {
<< (strlen(ied_usr.LD_info[i].LD_name) == 0 ? "NA" : ied_usr.LD_info[i].LD_name)
<< std::endl;
std::cout << " |-- read_flag: " << ied_usr.LD_info[i].read_flag << std::endl;
std::cout << " |-- log_level: " << ied_usr.LD_info[i].log_level << std::endl;
//index
std::cout << " |-- line_id: " << ied_usr.LD_info[i].line_id << std::endl;
//monitorledger
@@ -3401,6 +3457,7 @@ void printLedgerinshell(const ied_usr_t& ied_usr, QIODevice* outputDevice) {
outputDevice->write("\r\x1B[K");outputDevice->write("|-- tmnl_status: " + QByteArray(ied_usr.tmnl_status) + "\n");
outputDevice->write("\r\x1B[K");outputDevice->write("|-- terminal_code: " + QByteArray(ied_usr.terminal_code) + "\n");
outputDevice->write("\r\x1B[K");outputDevice->write("|-- update_flag: " + QByteArray::number(ied_usr.update_flag) + "\n");
outputDevice->write("\r\x1B[K");outputDevice->write("|-- log_level: " + QByteArray::number(ied_usr.log_level) + "\n");
// 打印每个LD_info的内容
for (int i = 0; i < 10; ++i) {
@@ -3412,7 +3469,9 @@ void printLedgerinshell(const ied_usr_t& ied_usr, QIODevice* outputDevice) {
outputDevice->write("\r\x1B[K");outputDevice->write(" |-- LD_name: "
+ (strlen(ied_usr.LD_info[i].LD_name) == 0 ? QByteArray("NA") : QByteArray(ied_usr.LD_info[i].LD_name))
+ "\n");
outputDevice->write("\r\x1B[K");outputDevice->write(" |-- read_flag: " + QByteArray::number(ied_usr.LD_info[i].read_flag) + "\n");
outputDevice->write("\r\x1B[K");outputDevice->write(" |-- read_flag: " + QByteArray::number(ied_usr.LD_info[i].read_flag) + "\n");
outputDevice->write("\r\x1B[K");outputDevice->write(" |-- log_level: " + QByteArray::number(ied_usr.LD_info[i].log_level) + "\n");
// index
outputDevice->write("\r\x1B[K");outputDevice->write(" |-- line_id: " + QByteArray::number(ied_usr.LD_info[i].line_id) + "\n");
@@ -4004,6 +4063,22 @@ int terminal_ledger_web(QMap<QString, terminal_dev*>* terminal_dev_map,
if (updateTime && updateTime->type == cJSON_String) strncpy(dev->timestamp, updateTime->valuestring, sizeof(dev->timestamp) - 1);
else strncpy(dev->timestamp, "N/A", sizeof(dev->timestamp) - 1);
cJSON* logLevel = cJSON_GetObjectItem(item, "log_level"); // log_level
int tmp_level = -1;
if (logLevel && logLevel->type == cJSON_Number) {
tmp_level = logLevel->valueint;
}
else if (logLevel && logLevel->type == cJSON_String && logLevel->valuestring) {
tmp_level = atoi(logLevel->valuestring);
}
// 判断是否合法 0~3
if (tmp_level >= 0 && tmp_level <= 3) {
dev->log_level = tmp_level;
} else {
dev->log_level = 1; // 默认 WARN
}
printf("dev->log_level: %d\n", dev->log_level);
// 解析 monitorData 数组
cJSON* monitorData = cJSON_GetObjectItem(item, "monitorData");
if (monitorData && monitorData->type == cJSON_Array) {
@@ -4040,6 +4115,22 @@ int terminal_ledger_web(QMap<QString, terminal_dev*>* terminal_dev_map,
if (monitorstatus && monitorstatus->type == cJSON_String) strncpy(dev->line[j].status, monitorstatus->valuestring, sizeof(dev->line[j].status) - 1);
else strncpy(dev->line[j].status, "N/A", sizeof(dev->line[j].status) - 1);
cJSON* logLevel_m = cJSON_GetObjectItem(item, "log_level"); // log_level
int tmp_level = -1;
if (logLevel_m && logLevel_m->type == cJSON_Number) {
tmp_level = logLevel_m->valueint;
}
else if (logLevel_m && logLevel_m->type == cJSON_String && logLevel_m->valuestring) {
tmp_level = atoi(logLevel_m->valuestring);
}
// 判断是否合法 (0~3)
if (tmp_level >= 0 && tmp_level <= 3) {
dev->line[j].log_level = tmp_level;
} else {
dev->line[j].log_level = 1; // 默认 WARN
}
printf("line[%d].log_level: %d\n", j, dev->line[j].log_level);
j++;
}
}
@@ -4213,6 +4304,9 @@ int parse_device_cfg_web()
//lnk20250210添加进程号
char processNo[64];
//lnk20260304添加日志等级
int log_level;
otl_datetime timestamp;
// 遍历终端台账容器
@@ -4238,305 +4332,279 @@ int parse_device_cfg_web()
strncpy(processNo, value->processNo, sizeof(processNo) - 1);//进程号
timestamp = parseTimestamp(value->timestamp);
//处理终端台账
ied = g_node->clients[count_real++];
//这里申请的空间基于ied的数量挂载到ied上
ied_usr = (ied_usr_t*)apr_pcalloc(g_init_pool, sizeof(ied_usr_t));//终端台账在initpool中申请空间
ied->usr_ext = ied_usr;
log_level = value->log_level;//日志等级
} else {
std::cerr << "Warning: terminal_dev pointer is null for key: " << it.key().toStdString() << std::endl;
continue; // 跳过空指针
}
if (ied_usr == NULL)
return APR_ENOMEM;
ied_usr->last_call_wavelist_time = sGetMsTime() + g_pt61850app->giTime * 1000;
//这里申请的空间基于ied的数量挂载到ied上
ied_usr->LD_info = (LD_info_t*)apr_pcalloc(g_init_pool, MAX_CPUNO * sizeof(LD_info_t));//监测点台账在initpool中申请空间
if (ied_usr->LD_info == NULL)
return APR_ENOMEM;
ied_usr->dev_flag = ENABLE;//终端有效
ied->chncount = 1;//设备通信端口总数
//这里申请的空间基于ied的数量挂载到ied上
ied->channel = (channel_t*)apr_pcalloc(g_cfg_pool, sizeof(channel_t) * ied->chncount);//通信结构在g_cfg_pool中申请空间终端的ip端口等
ied->channel[0].ied = ied;
ied->channel[0].status = STATUS_BREAKOFF;
ied->cpucount = 0;
if (strlen(terminal_id) != 0) {
apr_snprintf(ied_usr->terminal_id, sizeof(ied_usr->terminal_id), "%s", terminal_id);//terminal_id
cout << "ied_usr->terminal_id:" << ied_usr->terminal_id << endl;
//处理终端台账
ied = g_node->clients[count_real++];
//这里申请的空间基于ied的数量挂载到ied上
ied_usr = (ied_usr_t*)apr_pcalloc(g_init_pool, sizeof(ied_usr_t));//终端台账在initpool中申请空间
ied->usr_ext = ied_usr;
if (ied_usr == NULL)
return APR_ENOMEM;
ied_usr->last_call_wavelist_time = sGetMsTime() + g_pt61850app->giTime * 1000;
//这里申请的空间基于ied的数量挂载到ied上
ied_usr->LD_info = (LD_info_t*)apr_pcalloc(g_init_pool, MAX_CPUNO * sizeof(LD_info_t));//监测点台账在initpool中申请空间
if (ied_usr->LD_info == NULL)
return APR_ENOMEM;
ied_usr->dev_flag = ENABLE;//终端有效
ied->chncount = 1;//设备通信端口总数
//这里申请的空间基于ied的数量挂载到ied上
ied->channel = (channel_t*)apr_pcalloc(g_cfg_pool, sizeof(channel_t) * ied->chncount);//通信结构在g_cfg_pool中申请空间终端的ip端口等
ied->channel[0].ied = ied;
ied->channel[0].status = STATUS_BREAKOFF;
ied->cpucount = 0;
if (strlen(terminal_id) != 0) {
apr_snprintf(ied_usr->terminal_id, sizeof(ied_usr->terminal_id), "%s", terminal_id);//terminal_id
cout << "ied_usr->terminal_id:" << ied_usr->terminal_id << endl;
}
if (terminal_code != NULL) {
apr_snprintf(ied_usr->terminal_code, sizeof(ied_usr->terminal_code), "%s", terminal_code);//terminal_code
cout << "ied_usr->terminal_code:" << ied_usr->terminal_code << endl;
}
/*不需要这三个信息
if (org_name != NULL) {
apr_snprintf(ied_usr->org_name, sizeof(ied_usr->org_name), "%s", org_name);//org_name
cout << "ied_usr->org_name:" << ied_usr->org_name << endl;
}
if (maint_name != NULL) {
apr_snprintf(ied_usr->maint_name, sizeof(ied_usr->maint_name), "%s", maint_name);//maint_name
cout << "ied_usr->maint_name:" << ied_usr->maint_name << endl;
}
if (station_name != NULL) {
apr_snprintf(ied_usr->station_name, sizeof(ied_usr->station_name), "%s", station_name);//station_name
cout << "ied_usr->station_name:" << ied_usr->station_name << endl;
}
*/
if (tmnl_factory != NULL) {
apr_snprintf(ied_usr->tmnl_factory, sizeof(ied_usr->tmnl_factory), "%s", tmnl_factory);//tmnl_factory
cout << "ied_usr->tmnl_factory:" << ied_usr->tmnl_factory << endl;
}
if (tmnl_status != NULL) {
apr_snprintf(ied_usr->tmnl_status, sizeof(ied_usr->tmnl_status), "%s", tmnl_status);//tmnl_status
cout << "ied_usr->tmnl_status:" << ied_usr->tmnl_status << endl;
}
if (dev_type != NULL) {
apr_snprintf(ied_usr->dev_type, sizeof(ied_usr->dev_type), "%s", dev_type);//dev_type
cout << "ied_usr->dev_type:" << ied_usr->dev_type << endl;
}
//lnk20250210台账进程号
if (processNo != NULL) {
apr_snprintf(ied_usr->processNo, sizeof(ied_usr->processNo), "%s", processNo);//processNo
cout << "ied_usr->processNo:" << ied_usr->processNo << endl;
}
if (dev_series != NULL) {
apr_snprintf(ied_usr->dev_series, sizeof(ied_usr->dev_series), "%s", dev_series);//DEV_Series
cout << "defalut dev_series:" << ied_usr->dev_series << endl;
}
else
{
apr_snprintf(ied_usr->dev_series, sizeof(ied_usr->dev_series), "%s", "");//DEV_Series
cout << "defalut dev_series:" << ied_usr->dev_series << endl;
}
if (dev_key != NULL) {
apr_snprintf(ied_usr->dev_key, sizeof(ied_usr->dev_key), "%s", dev_key);//DEV_Key
cout << "defalut dev_key:" << ied_usr->dev_key << endl;
}
else
{
apr_snprintf(ied_usr->dev_key, sizeof(ied_usr->dev_key), "%s", "");//DEV_Key
cout << "defalut dev_key:" << ied_usr->dev_key << endl;
}
//lnk20260304
ied_usr->log_level = log_level;//日志等级
cout << "ied_usr->log_level:" << ied_usr->log_level << endl;
//lnk20241125实时数据用
ied_usr->dev_idx = count_real;
cout << "dev_idx:" << ied_usr->dev_idx << endl;
ied->channel[0].channel_type = CHANNEL_TYPE_IPV4;//channel
ied->channel[0].addr_str[LONGNAME - 1] = 0;//DEV_IP
if (addr_str != NULL) {
ied->channel[0].addr = ntohl(inet_addr(addr_str));//DEV_IP
strncpy(ied->channel[0].addr_str, addr_str, LONGNAME - 1);//DEV_IP
cout << "ied_usr->addr_str:" << ied->channel[0].addr_str << endl;
}
else
{
ied->channel[0].addr = ntohl(inet_addr("0.0.0.0"));//DEV_IP
strncpy(ied->channel[0].addr_str, addr_str, LONGNAME - 1);//DEV_IP
cout << "ied_usr->addr_str:" << ied->channel[0].addr_str << endl;
}
if (port_char != NULL) {
int port = 102;
if (stringToInt(port_char, &port)) {
// 转换成功portStr全为数字并且已经转换为int类型的port
ied->channel[0].port = port;//DEV_PortID
cout << "ied_usr->port:" << ied->channel[0].port << endl;//DEV_PortID
}
if (terminal_code != NULL) {
apr_snprintf(ied_usr->terminal_code, sizeof(ied_usr->terminal_code), "%s", terminal_code);//terminal_code
cout << "ied_usr->terminal_code:" << ied_usr->terminal_code << endl;
else {
ied->channel[0].port = 102;//DEV_PortID
cout << "ied_usr->port:" << port_char << ",非合法端口.使用默认端口:" << ied->channel[0].port << endl;//DEV_PortID
}
/*不需要这三个信息
if (org_name != NULL) {
apr_snprintf(ied_usr->org_name, sizeof(ied_usr->org_name), "%s", org_name);//org_name
cout << "ied_usr->org_name:" << ied_usr->org_name << endl;
}
if (timestamp.year != 0) {
//// 构造struct tm对象
struct tm timeinfo;
timeinfo.tm_year = timestamp.year - 1900; // 年份需要减去1900
timeinfo.tm_mon = timestamp.month - 1; // 月份需要减去1
timeinfo.tm_mday = timestamp.day;
timeinfo.tm_hour = timestamp.hour;
timeinfo.tm_min = timestamp.minute;
timeinfo.tm_sec = timestamp.second;
time_t time = std::mktime(&timeinfo);
ied_usr->time = static_cast<long long>(time);
cout << "ied_usr->time:" << ied_usr->time << endl;
}
//这里申请的空间基于ied的数量挂载到ied上
chnl_usr = (chnl_usr_t*)apr_pcalloc(g_init_pool, sizeof(chnl_usr_t));//拓展定义的通信结构在g_init_pool中申请空间拓展记录一些开关时间信息
ied->channel[0].connect = chnl_usr;
chnl_usr->chnl = &(ied->channel[0]);
chnl_usr->chnl_id = 0;
chnl_usr->m_state = CHANNEL_DISCONNECTED;
chnl_usr->m_ClosedMsTime = NEXT_CONNECT_TIME * (-1);
g_pt61850app->chnl_counts++;
//调试用
//std::cout << "value:" << terminal_id <<" "<<terminal_code <<" "<<tmnl_factory <<" "<<dev_series <<" "<<dev_key <<" "<< tmnl_status <<" "<<dev_type <<" "<<addr_str <<" "<<port_char <<" "<< timestamp.year << std::endl;
// 遍历监测点信息
LD_info_t line_info;
int count_real_monitor = 0; //遍历监测点台账的计数器
//调试用
//std::cout << "n_clients:" << g_node->n_clients << std::endl;
if (g_node->n_clients <= 0) {
std::cout << "no terminal exist " << std::endl;
return APR_EBADF;
}
char monitor_id[64];
//char terminal_code[64];
char monitor_name[64];
char logical_device_seq[64];
char voltage_level[64];
char terminal_connect[64];
char monitor_status[64];
//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){
ledger_monitor& monitor = value->line[j];
// 检查监测点 ID 是否为空以避免访问无效数据
/*if (monitor.monitor_id[0] != '\0') {
std::cout << " Monitor ID: " << monitor.monitor_id << std::endl;
std::cout << " Terminal Code: " << monitor.terminal_code << std::endl; //应该为空json不带
std::cout << " Monitor Name: " << monitor.monitor_name << std::endl;
std::cout << " Logical Device Seq: " << monitor.logical_device_seq << std::endl;
std::cout << " Voltage Level: " << monitor.voltage_level << std::endl;
std::cout << " Terminal Connect: " << monitor.terminal_connect << std::endl;
std::cout << " Timestamp: " << monitor.timestamp << std::endl; //应该为空json不带
std::cout << " monitor_status: " << monitor.status << std::endl;*/
strncpy(monitor_id, monitor.monitor_id, sizeof(monitor_id) - 1);
//strncpy(terminal_code, monitor.terminal_code, sizeof(terminal_code) - 1); //从上级获取
strncpy(monitor_name, monitor.monitor_name, sizeof(monitor_name) - 1);
strncpy(logical_device_seq, monitor.logical_device_seq, sizeof(logical_device_seq) - 1);
strncpy(voltage_level, monitor.voltage_level, sizeof(voltage_level) - 1);
strncpy(terminal_connect, monitor.terminal_connect, sizeof(terminal_connect) - 1);
//timestamp = parseTimestamp(monitor.timestamp); //从上级获取
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; //监测点排号
cout << "line_id:" << line_info.line_id << endl;
strcpy(line_info.mp_id, monitor_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;
}
if (maint_name != NULL) {
apr_snprintf(ied_usr->maint_name, sizeof(ied_usr->maint_name), "%s", maint_name);//maint_name
cout << "ied_usr->maint_name:" << ied_usr->maint_name << endl;
else {
line_info.cpuno = std::atoi(logical_device_seq);
cout << "logical_device_seq:"<< line_info.cpuno << endl;
}
if (station_name != NULL) {
apr_snprintf(ied_usr->station_name, sizeof(ied_usr->station_name), "%s", station_name);//station_name
cout << "ied_usr->station_name:" << ied_usr->station_name << endl;
}
*/
if (tmnl_factory != NULL) {
apr_snprintf(ied_usr->tmnl_factory, sizeof(ied_usr->tmnl_factory), "%s", tmnl_factory);//tmnl_factory
cout << "ied_usr->tmnl_factory:" << ied_usr->tmnl_factory << endl;
}
if (tmnl_status != NULL) {
apr_snprintf(ied_usr->tmnl_status, sizeof(ied_usr->tmnl_status), "%s", tmnl_status);//tmnl_status
cout << "ied_usr->tmnl_status:" << ied_usr->tmnl_status << endl;
}
if (dev_type != NULL) {
apr_snprintf(ied_usr->dev_type, sizeof(ied_usr->dev_type), "%s", dev_type);//dev_type
cout << "ied_usr->dev_type:" << ied_usr->dev_type << endl;
}
//lnk20250210台账进程号
if (processNo != NULL) {
apr_snprintf(ied_usr->processNo, sizeof(ied_usr->processNo), "%s", processNo);//processNo
cout << "ied_usr->processNo:" << ied_usr->processNo << endl;
}
if (dev_series != NULL) {
apr_snprintf(ied_usr->dev_series, sizeof(ied_usr->dev_series), "%s", dev_series);//DEV_Series
cout << "defalut dev_series:" << ied_usr->dev_series << endl;
}
else
//cout << "cpuno:" << line_info.cpuno << endl;
strcpy(line_info.voltage_level, voltage_level);
cout << "voltage_level:" << line_info.voltage_level << endl;
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)
{
apr_snprintf(ied_usr->dev_series, sizeof(ied_usr->dev_series), "%s", "");//DEV_Series
cout << "defalut dev_series:" << ied_usr->dev_series << endl;
isdelta_flag = 1; //存在一个监测点为角型接线则这个前置就要启动第二个配置列表
cout << "monitor_id" << monitor_id << "v_wiring_type:" << line_info.v_wiring_type << "is delta wiring:" << isdelta_flag << endl;
DIY_WARNLOG_CODE("process",LOG_CODE_LEDGER,"【WARN】前置连接的监测点 %s 是角形接线,对应终端为%s 终端类型是%s",line_info.mp_id,ied_usr->terminal_id,ied_usr->dev_type);
}
if (dev_key != NULL) {
apr_snprintf(ied_usr->dev_key, sizeof(ied_usr->dev_key), "%s", dev_key);//DEV_Key
cout << "defalut dev_key:" << ied_usr->dev_key << endl;
}
else
{
apr_snprintf(ied_usr->dev_key, sizeof(ied_usr->dev_key), "%s", "");//DEV_Key
cout << "defalut dev_key:" << ied_usr->dev_key << endl;
}
//lnk20241125实时数据用
ied_usr->dev_idx = count_real;
cout << "dev_idx:" << ied_usr->dev_idx << endl;
ied->channel[0].channel_type = CHANNEL_TYPE_IPV4;//channel
ied->channel[0].addr_str[LONGNAME - 1] = 0;//DEV_IP
if (addr_str != NULL) {
ied->channel[0].addr = ntohl(inet_addr(addr_str));//DEV_IP
strncpy(ied->channel[0].addr_str, addr_str, LONGNAME - 1);//DEV_IP
cout << "ied_usr->addr_str:" << ied->channel[0].addr_str << endl;
}
else
{
ied->channel[0].addr = ntohl(inet_addr("0.0.0.0"));//DEV_IP
strncpy(ied->channel[0].addr_str, addr_str, LONGNAME - 1);//DEV_IP
cout << "ied_usr->addr_str:" << ied->channel[0].addr_str << endl;
}
if (port_char != NULL) {
int port = 102;
if (stringToInt(port_char, &port)) {
// 转换成功portStr全为数字并且已经转换为int类型的port
ied->channel[0].port = port;//DEV_PortID
cout << "ied_usr->port:" << ied->channel[0].port << endl;//DEV_PortID
strcpy(line_info.monitor_status, monitor_status);
cout << "monitor_status:" << line_info.monitor_status << endl;
//// 构造struct tm对象
struct tm timeinfo;
timeinfo.tm_year = timestamp.year - 1900; // 年份需要减去1900 //从上级获取的timestamp
timeinfo.tm_mon = timestamp.month - 1; // 月份需要减去1
timeinfo.tm_mday = timestamp.day;
timeinfo.tm_hour = timestamp.hour;
timeinfo.tm_min = timestamp.minute;
timeinfo.tm_sec = timestamp.second;
time_t time = std::mktime(&timeinfo);
line_info.time = static_cast<long long>(time);
cout << "time:" << line_info.time << endl;
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)) {
char str[256]; //256大小
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;
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);
//调试用,申请的地址
printf("%s分配内存地址 LD_name[%d]: %p\n", ied_usr->terminal_id, cpuno - 1, (void*)ied_usr->LD_info[cpuno - 1].LD_name);
// 清空内存,防止残留数据
memset(ied_usr->LD_info[cpuno - 1].LD_name, 0, 256);
// 将 str 中的内容复制到预先分配的内存中,最多复制 256 字节(包含结束符)
apr_cpystrn(ied_usr->LD_info[cpuno - 1].LD_name, str, 256);
//这里申请的空间基于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;
cout << "rptcount:" << ied_usr->LD_info[cpuno - 1].rptcount << endl;
if (cpuno > ied->cpucount) {
ied->cpucount = cpuno;
}
else {
ied->channel[0].port = 102;//DEV_PortID
cout << "ied_usr->port:" << port_char << ",非合法端口.使用默认端口:" << ied->channel[0].port << endl;//DEV_PortID
}
}
if (timestamp.year != 0) {
//// 构造struct tm对象
struct tm timeinfo;
timeinfo.tm_year = timestamp.year - 1900; // 年份需要减去1900
timeinfo.tm_mon = timestamp.month - 1; // 月份需要减去1
timeinfo.tm_mday = timestamp.day;
timeinfo.tm_hour = timestamp.hour;
timeinfo.tm_min = timestamp.minute;
timeinfo.tm_sec = timestamp.second;
time_t time = std::mktime(&timeinfo);
ied_usr->time = static_cast<long long>(time);
cout << "ied_usr->time:" << ied_usr->time << endl;
}
//这里申请的空间基于ied的数量挂载到ied上
chnl_usr = (chnl_usr_t*)apr_pcalloc(g_init_pool, sizeof(chnl_usr_t));//拓展定义的通信结构在g_init_pool中申请空间拓展记录一些开关时间信息
ied->channel[0].connect = chnl_usr;
chnl_usr->chnl = &(ied->channel[0]);
chnl_usr->chnl_id = 0;
chnl_usr->m_state = CHANNEL_DISCONNECTED;
chnl_usr->m_ClosedMsTime = NEXT_CONNECT_TIME * (-1);
g_pt61850app->chnl_counts++;
//调试用
//std::cout << "value:" << terminal_id <<" "<<terminal_code <<" "<<tmnl_factory <<" "<<dev_series <<" "<<dev_key <<" "<< tmnl_status <<" "<<dev_type <<" "<<addr_str <<" "<<port_char <<" "<< timestamp.year << std::endl;
// 遍历监测点信息
LD_info_t line_info;
int count_real_monitor = 0; //遍历监测点台账的计数器
//调试用
//std::cout << "n_clients:" << g_node->n_clients << std::endl;
if (g_node->n_clients <= 0) {
std::cout << "no terminal exist " << std::endl;
return APR_EBADF;
}
char monitor_id[64];
//char terminal_code[64];
char monitor_name[64];
char logical_device_seq[64];
char voltage_level[64];
char terminal_connect[64];
char monitor_status[64];
//otl_datetime timestamp;
//for (int j = 0; j < 10; ++j) { // 假设最多有10个监测点
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') {
std::cout << " Monitor ID: " << monitor.monitor_id << std::endl;
std::cout << " Terminal Code: " << monitor.terminal_code << std::endl; //应该为空json不带
std::cout << " Monitor Name: " << monitor.monitor_name << std::endl;
std::cout << " Logical Device Seq: " << monitor.logical_device_seq << std::endl;
std::cout << " Voltage Level: " << monitor.voltage_level << std::endl;
std::cout << " Terminal Connect: " << monitor.terminal_connect << std::endl;
std::cout << " Timestamp: " << monitor.timestamp << std::endl; //应该为空json不带
std::cout << " monitor_status: " << monitor.status << std::endl;*/
strncpy(monitor_id, monitor.monitor_id, sizeof(monitor_id) - 1);
//strncpy(terminal_code, monitor.terminal_code, sizeof(terminal_code) - 1); //从上级获取
strncpy(monitor_name, monitor.monitor_name, sizeof(monitor_name) - 1);
strncpy(logical_device_seq, monitor.logical_device_seq, sizeof(logical_device_seq) - 1);
strncpy(voltage_level, monitor.voltage_level, sizeof(voltage_level) - 1);
strncpy(terminal_connect, monitor.terminal_connect, sizeof(terminal_connect) - 1);
//timestamp = parseTimestamp(monitor.timestamp); //从上级获取
strncpy(monitor_status, monitor.status, sizeof(monitor_status) - 1);//添加监测点状态
//监测点台账处理
count_real_monitor++;
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);
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);
cout << "logical_device_seq:"<< line_info.cpuno << endl;
}
//cout << "cpuno:" << line_info.cpuno << endl;
strcpy(line_info.voltage_level, voltage_level);
cout << "voltage_level:" << line_info.voltage_level << endl;
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)
{
isdelta_flag = 1; //存在一个监测点为角型接线则这个前置就要启动第二个配置列表
cout << "monitor_id" << monitor_id << "v_wiring_type:" << line_info.v_wiring_type << "is delta wiring:" << isdelta_flag << endl;
DIY_WARNLOG_CODE("process",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);
cout << "monitor_status:" << line_info.monitor_status << endl;
//// 构造struct tm对象
struct tm timeinfo;
timeinfo.tm_year = timestamp.year - 1900; // 年份需要减去1900 //从上级获取的timestamp
timeinfo.tm_mon = timestamp.month - 1; // 月份需要减去1
timeinfo.tm_mday = timestamp.day;
timeinfo.tm_hour = timestamp.hour;
timeinfo.tm_min = timestamp.minute;
timeinfo.tm_sec = timestamp.second;
time_t time = std::mktime(&timeinfo);
line_info.time = static_cast<long long>(time);
cout << "time:" << line_info.time << endl;
strcpy(line_info.name, monitor_name);
cout << "name:" << line_info.name << endl;
line_info.read_flag = ENABLE; //监测点有效
//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)) {
char str[256]; //256大小
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;
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);
//调试用,申请的地址
printf("%s分配内存地址 LD_name[%d]: %p\n", ied_usr->terminal_id, cpuno - 1, (void*)ied_usr->LD_info[cpuno - 1].LD_name);
// 清空内存,防止残留数据
memset(ied_usr->LD_info[cpuno - 1].LD_name, 0, 256);
// 将 str 中的内容复制到预先分配的内存中,最多复制 256 字节(包含结束符)
apr_cpystrn(ied_usr->LD_info[cpuno - 1].LD_name, str, 256);
//这里申请的空间基于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;
cout << "rptcount:" << ied_usr->LD_info[cpuno - 1].rptcount << endl;
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;
}
cout << "dev init create count:" << count_real;
return APR_SUCCESS;
}
catch (otl_exception& e)
catch (...)
{
printf("\n device error,ERROR code= %d,msg= %s \n", e.code, e.msg);
return e.code;
printf("\n device error \n");
return APR_EBADF;
}
}
@@ -4727,10 +4795,10 @@ int parse_model_cfg_web()
delete_icd_model_map(icd_model_map);
return APR_SUCCESS;
}
catch (otl_exception& e)
catch (...)
{
printf("\n icd model error,ERROR code= %d,msg= %s \n", e.code, e.msg);
return e.code;
printf("\n icd model error\n");
return APR_EBADF;
}
}
////////////////////////////////////////////////////////////icd模型重构函数lnk20250116
@@ -4798,9 +4866,9 @@ char* parse_model_cfg_web_one(ied_t* ied, char* out_model)
delete_icd_model_map(icd_model_map);//lnk20250701
return NULL;
}
catch (otl_exception& e)
catch (...)
{
printf("\n icd model error,ERROR code= %d,msg= %s \n", e.code, e.msg);
printf("\n icd model error\n");
return NULL;
}
}
@@ -5404,6 +5472,16 @@ int update_one_terminal_ledger(terminal* update, int i,ied_t* ied,int terminal_i
apr_snprintf(ied_usr->processNo, sizeof(ied_usr->processNo), "%s", update[i].processNo);
printf("ied_usr->processNo: %s\n", ied_usr->processNo);
}
//log_level拷贝到ied_usr中
if (update[i].log_level >= 0 && update[i].log_level <= 3) {
ied_usr->log_level = update[i].log_level;
printf("ied_usr->log_level: %d\n", ied_usr->log_level);
} else {
ied_usr->log_level = 1; // 默认为1表示warn级别
printf("ied_usr->log_level (default): %d\n", ied_usr->log_level);
}
if (update[i].dev_series != NULL) {
apr_snprintf(ied_usr->dev_series, sizeof(ied_usr->dev_series), "%s", update[i].dev_series);
printf("ied_usr->dev_series: %s\n", ied_usr->dev_series);
@@ -5510,6 +5588,19 @@ int update_one_terminal_ledger(terminal* update, int i,ied_t* ied,int terminal_i
strncpy(line_info.monitor_status, monitor_data.status, sizeof(line_info.monitor_status) - 1);
strncpy(line_info.terminal_code, monitor_data.terminal_code, sizeof(line_info.terminal_code) - 1);
strncpy(logical_device_seq, monitor_data.logical_device_seq, sizeof(logical_device_seq) - 1);
//log_level拷贝到line_info中
if (monitor_data.log_level >= 0 && monitor_data.log_level <= 3) { //优先使用监测点的log_level
line_info.log_level = monitor_data.log_level;
printf("line_info.log_level (monitor): %d\n", line_info.log_level);
} else if (ied_usr->log_level >= 0 && ied_usr->log_level <= 3) { //继承终端的log_level
line_info.log_level = ied_usr->log_level;
printf("line_info.log_level (inherit terminal): %d\n", line_info.log_level);
} else {
line_info.log_level = 1;
printf("line_info.log_level (default): %d\n", line_info.log_level);
}
if (isCharPtrEmpty(logical_device_seq)) {
line_info.cpuno = 1; // 默认监测点实例号1
printf("logical_device_seq: is null, set cpuno: %d\n", (int)line_info.cpuno);
@@ -5526,6 +5617,7 @@ int update_one_terminal_ledger(terminal* update, int i,ied_t* ied,int terminal_i
printf("v_wiring_type: %s\n", line_info.v_wiring_type);
printf("monitor_status: %s\n", line_info.monitor_status);
printf("name: %s\n", line_info.name);
printf("log_level: %d\n", line_info.log_level);
//lnk20250214角形
if (strcmp(line_info.v_wiring_type, "0") != 0)
@@ -6137,6 +6229,8 @@ void clearLDInfo(LD_info_t *ld_info) {
memset(ld_info->FltNum, 0, sizeof(ld_info->FltNum));
ld_info->RDRE_FltNum = 0; //录波号清零
ld_info->log_level = 1; //log_level设为1默认日志级别为1后续根据需要调整
}
@@ -6185,6 +6279,7 @@ void clearIedUsr(ied_usr_t *ied_usr) {
memset(ied_usr->tmnl_factory, 0, sizeof(ied_usr->tmnl_factory));
memset(ied_usr->tmnl_status, 0, sizeof(ied_usr->tmnl_status));
memset(ied_usr->terminal_code, 0, sizeof(ied_usr->terminal_code));
ied_usr->log_level = 1; //log_level设为1默认日志级别为1后续根据需要调整
}
// 清空 channel 和 cpuinfo 的非指针部分

View File

@@ -4,7 +4,7 @@
#include "../log4cplus/fileappender.h"
#include "../log4cplus/layout.h"
#include "../log4cplus/ndc.h"
#include "../log4cplus/log4.h"
//#include "../log4cplus/log4.h"
#include "../log4cplus/spi/loggingevent.h"
#include <iostream>
#include <map>
@@ -18,21 +18,47 @@
//目录创建
#include <sys/stat.h>
#include <sys/types.h>
//kafka结构定义
#include "../json/mms_json_inter.h"
#include "../mms/rdb_client.h"
#include "../include/node.h"//lnk20241223
#include "../log4cplus/log4.h"//后移防止min/max定义冲突
///////////////////////////////////////////////lnk20260303添加日志上送控制
#include <unordered_map>
#include <chrono>
#include <mutex>
#include <atomic>
#include <memory>
struct LogLevelCache { //记录日志等级的缓存减少频繁访问全局map的开销
std::unordered_map<std::string, int> term_min; // terminal_id -> min level
std::unordered_map<std::string, int> mp_min; // mp_id -> min level
};
// append线程只读不加锁
static std::shared_ptr<LogLevelCache> g_level_cache_sp;
///////////////////////////////////////////////
//用来控制日志上送的结构
struct LOGEntry {
std::string id;
std::string level; // terminal / measurepoint
int code; //code
int min_grade;
int countdown;
};
//日志上送map管理
std::map<std::string, LOGEntry> g_log_entries;
pthread_mutex_t g_log_mutex = PTHREAD_MUTEX_INITIALIZER;
/////////////////////////////////////////////外部定义
extern unsigned int g_node_id;
extern int g_front_seg_index;
extern std::string FRONT_INST;
extern char subdir[128];
extern node_t* g_node;
//mq
extern QMutex kafka_data_list_mutex; //Kafka发送数据锁
extern QList<Ckafka_data_t> kafka_data_list; //kafka发送数据链表
@@ -42,12 +68,153 @@ extern std::string intToString(int number);
//日志主题
extern std::string G_LOG_TOPIC;
/////////////////////////////////////////////////////////
//log4命名空间
using namespace log4cplus;
using namespace log4cplus::helpers;
///////////////////////////////////////////////////////////////
static std::string extract_logger_id(const std::string& logger_name);
////////////////////////////////////////////////////////辅助函数
///////////////////////////////////////////////////////lnk20260303添加日志上送控制
//用于在台账中查询日志上送控制项的日志等级字符串转换为整数
static int int_to_loglevel(int v, int default_level = WARN_LOG_LEVEL)
{
switch (v) {
case 0: return ERROR_LOG_LEVEL;
case 1: return WARN_LOG_LEVEL;
case 2: return INFO_LOG_LEVEL;
case 3: return DEBUG_LOG_LEVEL;
default: return default_level;
}
}
// 构建日志等级缓存,减少 append 线程访问全局 map 的开销
static LogLevelCache* build_cache_unlocked()
{
LogLevelCache* nc = new LogLevelCache;
// 没 node / 没 clients返回空 cache查不到会兜底 WARN
if (!g_node || g_node->n_clients <= 0 || !g_node->clients) {
return nc;
}
// 你 LD_info 是固定 10 个就用 10如果不是改成实际长度
const int MAX_LD = 10;
for (int iedno = 0; iedno < g_node->n_clients; ++iedno) {
ied_t* ied = g_node->clients[iedno];
if (!ied || !ied->usr_ext) continue;
ied_usr_t* ied_usr = (ied_usr_t*)ied->usr_ext;
// terminal_id 必须有效
const char* tid = ied_usr->terminal_id;
if (!tid || tid[0] == '\0') continue;
const std::string terminal_id(tid);
// 装置级阈值0~3 -> log4cplus level默认 WARN
const int term_lv = int_to_loglevel(ied_usr->log_level, WARN_LOG_LEVEL);
nc->term_min[terminal_id] = term_lv;
// 监测点阈值:若非法/缺失,兜底用装置级 term_lv
for (int i = 0; i < MAX_LD; ++i) {
const char* mp = ied_usr->LD_info[i].mp_id;
if (!mp || mp[0] == '\0') continue;
const std::string mp_id(mp);
// mp 的 log_level 优先;非法则用 term_lv
const int mp_lv = int_to_loglevel(ied_usr->LD_info[i].log_level, term_lv);
nc->mp_min[mp_id] = mp_lv;
}
}
return nc;
}
//用于更新日志等级缓存的函数,获取最新的日志上送控制项并更新到缓存中,调用时需持锁
void refresh_log_level_cache_locked()
{
std::shared_ptr<LogLevelCache> nc(build_cache_unlocked());
std::atomic_store(&g_level_cache_sp, nc);
}
const int LOGTYPE_DEFAULT = LOG_CODE_OTHER; // 默认日志类型,表示不区分具体类型的日志上送控制
static const int LOGTYPE_WILDCARD = 999; // 用于匹配任意日志类型的特殊值
static const char* ID_WILDCARD = "all"; // 用于匹配任意 ID 的特殊值
static std::string build_debug_key(const std::string& id,
const std::string& level, // terminal/measurepoint/process
int code) // 日志类型
{
std::ostringstream oss;
oss << id << "|" << level << "|" << code;
return oss.str();
}
static bool find_entry_allow(const std::string& key, int level_val)
{
std::map<std::string, LOGEntry>::iterator it = g_log_entries.find(key);
if (it == g_log_entries.end() || it->second.countdown <= 0) return false;
return level_val >= it->second.min_grade;
}
static bool allow_low_level_send(const std::string& id,
const std::string& level_str,
int code,
int level_val)
{
pthread_mutex_lock(&g_log_mutex);
// 1) 精确
if (find_entry_allow(build_debug_key(id, level_str, code), level_val)) {
pthread_mutex_unlock(&g_log_mutex);
return true;
}
// 2) logtype 通配
if (find_entry_allow(build_debug_key(id, level_str, LOGTYPE_WILDCARD), level_val)) {
pthread_mutex_unlock(&g_log_mutex);
return true;
}
// 3) id 通配
if (find_entry_allow(build_debug_key(ID_WILDCARD, level_str, code), level_val)) {
pthread_mutex_unlock(&g_log_mutex);
return true;
}
// 4) 双通配
if (find_entry_allow(build_debug_key(ID_WILDCARD, level_str, LOGTYPE_WILDCARD), level_val)) {
pthread_mutex_unlock(&g_log_mutex);
return true;
}
pthread_mutex_unlock(&g_log_mutex);
return false;
}
static int get_min_send_level_cached(const std::string& level_str, const std::string& logger_name)
{
const int DEFAULT_LEVEL = WARN_LOG_LEVEL;
if (level_str == "process") return DEFAULT_LEVEL;
const std::string id = extract_logger_id(logger_name); //terminal.<id> / monitor.<mp>
if (id.empty()) return DEFAULT_LEVEL;
std::shared_ptr<LogLevelCache> c = std::atomic_load(&g_level_cache_sp);
if (!c) return DEFAULT_LEVEL;
if (level_str == "terminal") {
auto it = c->term_min.find(id);
return (it != c->term_min.end()) ? it->second : DEFAULT_LEVEL;
}
if (level_str == "measurepoint") {
auto it = c->mp_min.find(id);
return (it != c->mp_min.end()) ? it->second : DEFAULT_LEVEL;
}
return DEFAULT_LEVEL;
}
///////////////////////////////////////////////////////lnk20260303添加日志上送控制
std::string get_front_type_from_subdir() {
if (std::strstr(subdir, "cfg_3s_data") != NULL)
return "realTime";
@@ -79,12 +246,11 @@ bool create_directory_recursive(const std::string& path) {
}
//////////////////////////////////////////////////////////////////////
std::string extract_logger_id(const std::string& logger_name) {
size_t first = logger_name.find('.');
size_t last = logger_name.rfind('.');
if (first != std::string::npos && last != std::string::npos && first + 1 < last) {
return logger_name.substr(first + 1, last - first - 1); // 去掉开头"terminal."和结尾".COM"
}
return "";
if (logger_name == "process") return "process";
size_t pos = logger_name.find('.');
if (pos == std::string::npos) return "";
// 取第一个 '.' 后面的全部terminal.<id> / monitor.<mp>
return logger_name.substr(pos + 1);
}
std::string get_level_str(int level) {
@@ -98,7 +264,7 @@ std::string get_level_str(int level) {
}
//////////////////////////////////////////////////////////////////////
TypedLogger::TypedLogger() {}
TypedLogger::TypedLogger(const Logger& l, int t) : logger(l), logtype(t) {}
TypedLogger::TypedLogger(const Logger& l, int t) : logger(l), code(t) {}
DebugSwitch::DebugSwitch() : debug_open(false), min_level(WARN_LOG_LEVEL) {}
void DebugSwitch::open() { debug_open = true; }
@@ -132,46 +298,65 @@ LOG_TLS int g_log_code_tls = 0;
class SendAppender : public Appender {
protected:
void append(const spi::InternalLoggingEvent& event) {
void append(const spi::InternalLoggingEvent& event) override {
std::string logger_name = event.getLoggerName();
int level = event.getLogLevel();
std::string msg = event.getMessage();
int logtype = (logger_name.find(".COM") != std::string::npos) ? LOGTYPE_COM : LOGTYPE_DATA;
std::string level_str;
if (logger_name.find("process") == 0)
level_str = "process";
else if (logger_name.find("monitor") != std::string::npos)
level_str = "measurepoint";
else
else if (logger_name.find("terminal") != std::string::npos)
level_str = "terminal";
else
level_str = "process";
// ★读取 TLS 中的 code(在打日志的线程里由宏设定)
int code = g_log_code_tls; // 若未显式传入,则为 0
int code = g_log_code_tls; // TLS code
int safe_logtype = code; // ★关键:用 code 当 logtype 维度
if (level == ERROR_LOG_LEVEL || level == WARN_LOG_LEVEL || g_debug_switch.match(logger_name, level, logtype)) {
std::ostringstream oss;
oss << "{\"processNo\":\"" << intToString(g_front_seg_index)
<< "\",\"nodeId\":\"" << FRONT_INST
<< "\",\"businessId\":\"" << extract_logger_id(logger_name)
<< "\",\"level\":\"" << level_str
<< "\",\"grade\":\"" << get_level_str(level)
<< "\",\"logtype\":\"" << (logtype == LOGTYPE_COM ? "com" : "data")
<< "\",\"frontType\":\"" << get_front_type_from_subdir()
// ★新增:输出 code 字段(整型)
<< "\",\"code\":\"" << code
<< "\",\"log\":\"" << escape_json(msg) << "\"}";
bool allow_send = false;
int min_send_level = get_min_send_level_cached(level_str, logger_name);
std::string jsonString = oss.str();
// ① 高于台账阈值:直接上送
if (level >= min_send_level) {
allow_send = true;
} else {
// ② 低等级:默认不上送,除非命令打开
std::string ctrl_id;
if (level_str == "process") ctrl_id = "process";
else ctrl_id = extract_logger_id(logger_name);
Ckafka_data_t connect_info;
connect_info.strTopic = QString::fromStdString(G_LOG_TOPIC);
connect_info.strText = QString::fromStdString(jsonString);
kafka_data_list_mutex.lock();
kafka_data_list.append(connect_info);
kafka_data_list_mutex.unlock();
if (!ctrl_id.empty()) {
allow_send = allow_low_level_send(ctrl_id, level_str, safe_logtype, level);
}
}
if (!allow_send) return;
// ③ 限频:同一条日志
const std::string rkey = make_key(logger_name, level, code, msg);
if (!should_emit(rkey)) return;
std::ostringstream oss;
oss << "{\"processNo\":\"" << intToString(g_front_seg_index)
<< "\",\"nodeId\":\"" << FRONT_INST
<< "\",\"businessId\":\"" << extract_logger_id(logger_name)
<< "\",\"level\":\"" << level_str
<< "\",\"grade\":\"" << get_level_str(level)
<< "\",\"logtype\":\"" << safe_logtype
<< "\",\"frontType\":\"" << get_front_type_from_subdir()
<< "\",\"code\":" << code
<< ",\"log\":\"" << escape_json(msg) << "\"}";
Ckafka_data_t connect_info;
connect_info.strTopic = QString::fromStdString(G_LOG_TOPIC);
connect_info.strText = QString::fromStdString(oss.str());
kafka_data_list_mutex.lock();
kafka_data_list.append(connect_info);
kafka_data_list_mutex.unlock();
}
std::string escape_json(const std::string& input) {
@@ -198,25 +383,58 @@ public:
virtual ~SendAppender() {
destructorImpl(); // 重要!释放 log4cplus 基类资源
}
//////////////////////////////////////////////////////////////////20260303添加日志上送控制 - 频率限制实现
private:
struct RateState {
uint64_t hit_count;
std::chrono::steady_clock::time_point last_emit;
bool has_emit;
RateState() : hit_count(0), last_emit(), has_emit(false) {}
};
static std::unordered_map<std::string, RateState> s_rate_map;
static std::mutex s_rate_mutex;
static std::string make_key(const std::string& logger_name, int level, int code, const std::string& msg) {
std::ostringstream oss;
oss << logger_name << "|" << level << "|" << code << "|" << msg;
return oss.str();
}
static bool should_emit(const std::string& key) {
using namespace std::chrono;
const auto now = steady_clock::now();
std::lock_guard<std::mutex> lk(s_rate_mutex);
RateState& st = s_rate_map[key];
const int RESET_SEC = 3600;
if (st.has_emit) {
auto idle = duration_cast<seconds>(now - st.last_emit).count();
if (idle >= RESET_SEC) st.hit_count = 0;
}
st.hit_count++;
const int period_sec = (st.hit_count > 3) ? 300 : 1;
if (!st.has_emit) {
st.last_emit = now;
st.has_emit = true;
return true;
}
const auto elapsed = duration_cast<seconds>(now - st.last_emit).count();
if (elapsed >= period_sec) {
st.last_emit = now;
return true;
}
return false;
}
};
//用来控制日志上送的结构
struct LOGEntry {
std::string id;
std::string level; // terminal / measurepoint
int logtype; // com / data
int min_grade;
int countdown;
};
//日志上送map管理
std::map<std::string, LOGEntry> g_log_entries;
pthread_mutex_t g_log_mutex = PTHREAD_MUTEX_INITIALIZER;
// 生成唯一 key
std::string build_debug_key(const std::string& id, const std::string& level, int logtype) {
return id + "|" + level + "|" + (logtype == 1 ? "COM" : "DATA");
}
//////////////////////////////////////////////////////////////////20260303添加日志上送控制 - 频率限制实现
std::unordered_map<std::string, SendAppender::RateState> SendAppender::s_rate_map;
std::mutex SendAppender::s_rate_mutex;
// 外部线程中调用每秒更新所有倒计时0 则删除
void update_log_entries_countdown() {
@@ -236,25 +454,25 @@ void update_log_entries_countdown() {
pthread_mutex_unlock(&g_log_mutex);
}
void process_log_command(const std::string& id, const std::string& level, const std::string& grade, const std::string& logtype_str) {
if (level != "terminal" && level != "measurepoint") return;
void process_log_command(const std::string& id,
const std::string& level,
const std::string& grade,
int code)
{
if (level != "terminal" && level != "measurepoint" && level != "process") return;
int type = (logtype_str == "com") ? LOGTYPE_COM : LOGTYPE_DATA;
int grade_level = (grade == "DEBUG") ? DEBUG_LOG_LEVEL : INFO_LOG_LEVEL;
std::string key = build_debug_key(id, level, type);
std::string key = build_debug_key(id, level, code);
pthread_mutex_lock(&g_log_mutex);
LOGEntry& entry = g_log_entries[key]; // 会自动 insert 或取已有
LOGEntry& entry = g_log_entries[key];
entry.id = id;
entry.level = level;
entry.logtype = type;
entry.code = code;
entry.min_grade = grade_level;
entry.countdown = 60; // 重置倒计时
entry.countdown = 60;
pthread_mutex_unlock(&g_log_mutex);
}
Logger init_logger(const std::string& full_name, const std::string& file_dir, const std::string& base_file, SharedAppenderPtr fileAppender) {
@@ -286,7 +504,7 @@ log4cplus::Logger init_logger(const std::string& full_name,
//进程的日志
void init_logger_process() {
std::string base_dir = std::string("/FeProject/") + subdir + "/processNo" + intToString(g_front_seg_index) + "/log";
logger_map["process"] = TypedLogger(init_logger(std::string("process"), base_dir, std::string("process")), LOGTYPE_DATA);
logger_map["process"] = TypedLogger(init_logger(std::string("process"), base_dir, std::string("process")), LOGTYPE_DEFAULT);
std::cout << "process log init ok" << std::endl;
}
@@ -321,12 +539,10 @@ void init_loggers_bydevid(const char* dev_id)
std::string device_dir = base_dir + "/" + ip_str;
std::string device_key_c = std::string("terminal.") + dev_id + ".COM";
std::string device_key_d = std::string("terminal.") + dev_id + ".DATA";
std::string device_key = std::string("terminal.") + dev_id;
// 添加判断:终端日志 logger 是否已存在
if (logger_map.find(device_key_c) == logger_map.end() &&
logger_map.find(device_key_d) == logger_map.end()) {
if (logger_map.find(device_key) == logger_map.end()) {
// 所有终端日志com 和 data写到同一个 device 日志文件中
std::string file_path_t = device_dir + "/" + dev_id + ".log";
@@ -335,27 +551,23 @@ void init_loggers_bydevid(const char* dev_id)
SharedAppenderPtr device_appender = SharedAppenderPtr(new RollingFileAppender(file_path_t, 1 * 1024 * 1024, 2));
device_appender->setLayout(std::auto_ptr<Layout>(new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n")));
Logger device_logger_c = init_logger(device_key_c, device_dir, dev_id, device_appender); //用终端id作为日志文件名
Logger device_logger_d = init_logger(device_key_d, device_dir, dev_id, device_appender); //用终端id作为日志文件名
logger_map[device_key_c] = TypedLogger(device_logger_c, LOGTYPE_COM);
logger_map[device_key_d] = TypedLogger(device_logger_d, LOGTYPE_DATA);
Logger device_logger = init_logger(device_key, device_dir, dev_id, device_appender); //用终端id作为日志文件名
logger_map[device_key] = TypedLogger(device_logger, LOGTYPE_DEFAULT);
DIY_INFOLOG(device_key_d.c_str(),"【NORMAL】终端id:%s终端级日志初始化完毕", ied_usr->terminal_id);
DIY_INFOLOG(device_key.c_str(),"【NORMAL】终端id:%s终端级日志初始化完毕", ied_usr->terminal_id);
}
// 初始化监测点
// 监测点 logger 名称格式monitor.<mp_id>.COM / .DATA
// 监测点 logger 名称格式monitor.<mp_id>
for (int i = 0; i < 10; ++i) {
if (strlen(ied_usr->LD_info[i].mp_id) > 0){
std::ostringstream mon_key_c, mon_key_d, mon_path, mon_name;
mon_key_c << "monitor." << ied_usr->LD_info[i].mp_id << ".COM";
mon_key_d << "monitor." << ied_usr->LD_info[i].mp_id << ".DATA";
std::ostringstream mon_key, mon_path, mon_name;
mon_key << "monitor." << ied_usr->LD_info[i].mp_id;
mon_path << device_dir << "/monitor" << i;//终端路径下用monitor+序号作为目录
mon_name << ied_usr->LD_info[i].mp_id;
// 添加判断:监测点 logger 是否已存在
if (logger_map.find(mon_key_c.str()) == logger_map.end() &&
logger_map.find(mon_key_d.str()) == logger_map.end()) {
if (logger_map.find(mon_key.str()) == logger_map.end()) {
// 所有监测点日志com 和 data写到同一个 monitor 日志文件中
std::string file_path_m = mon_path.str() + "/" + mon_name.str() + ".log";
@@ -364,12 +576,10 @@ void init_loggers_bydevid(const char* dev_id)
SharedAppenderPtr monitor_appender = SharedAppenderPtr(new RollingFileAppender(file_path_m, 1 * 1024 * 1024, 2));
monitor_appender->setLayout(std::auto_ptr<Layout>(new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n")));
Logger mon_logger_c = init_logger(mon_key_c.str(), mon_path.str(), mon_name.str(),monitor_appender);//用监测点号作为日志文件名
Logger mon_logger_d = init_logger(mon_key_d.str(), mon_path.str(), mon_name.str(),monitor_appender);
logger_map[mon_key_c.str()] = TypedLogger(mon_logger_c, LOGTYPE_COM);
logger_map[mon_key_d.str()] = TypedLogger(mon_logger_d, LOGTYPE_DATA);
Logger mon_logger = init_logger(mon_key.str(), mon_path.str(), mon_name.str(),monitor_appender);//用监测点号作为日志文件名
logger_map[mon_key.str()] = TypedLogger(mon_logger, LOGTYPE_DEFAULT);
DIY_INFOLOG(mon_key_d.str().c_str(),"【NORMAL】监测点:%s - id:%s监测点级日志初始化完毕", ied_usr->LD_info[i].name,ied_usr->LD_info[i].mp_id);
DIY_INFOLOG(mon_key.str().c_str(),"【NORMAL】监测点:%s - id:%s监测点级日志初始化完毕", ied_usr->LD_info[i].name,ied_usr->LD_info[i].mp_id);
}
}
@@ -377,6 +587,9 @@ void init_loggers_bydevid(const char* dev_id)
break; // 只匹配一个 terminal_id
}
//lnk20260303添加日志上送控制 - 初始化时构建日志等级缓存
refresh_log_level_cache_locked();
}
void init_loggers() {
@@ -402,8 +615,7 @@ void init_loggers() {
std::string device_dir = base_dir + "/" + ip_str;
std::string device_key_c = std::string("terminal.") + ied_usr->terminal_id + ".COM";
std::string device_key_d = std::string("terminal.") + ied_usr->terminal_id + ".DATA";
std::string device_key = std::string("terminal.") + ied_usr->terminal_id;
// 所有终端日志com 和 data写到同一个 device 日志文件中
std::string file_path_t = device_dir + "/" + ied_usr->terminal_id + ".log";
@@ -412,21 +624,18 @@ void init_loggers() {
SharedAppenderPtr device_appender = SharedAppenderPtr(new RollingFileAppender(file_path_t, 1 * 1024 * 1024, 2));
device_appender->setLayout(std::auto_ptr<Layout>(new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n")));
Logger device_logger_c = init_logger(device_key_c, device_dir, ied_usr->terminal_id, device_appender); //用终端id作为日志文件名
Logger device_logger_d = init_logger(device_key_d, device_dir, ied_usr->terminal_id, device_appender); //用终端id作为日志文件名
Logger device_logger = init_logger(device_key, device_dir, ied_usr->terminal_id, device_appender); //用终端id作为日志文件名
logger_map[device_key_c] = TypedLogger(device_logger_c, LOGTYPE_COM);
logger_map[device_key_d] = TypedLogger(device_logger_d, LOGTYPE_DATA);
logger_map[device_key] = TypedLogger(device_logger, LOGTYPE_DEFAULT);
DIY_INFOLOG(device_key_d.c_str(),"【NORMAL】终端id:%s终端级日志初始化完毕", ied_usr->terminal_id);
DIY_INFOLOG(device_key.c_str(),"【NORMAL】终端id:%s终端级日志初始化完毕", ied_usr->terminal_id);
// 初始化监测点
// 监测点 logger 名称格式monitor.<mp_id>.COM / .DATA
// 监测点 logger 名称格式monitor.<mp_id>
for (int i = 0; i < 10; ++i) {
if (strlen(ied_usr->LD_info[i].mp_id) > 0){
std::ostringstream mon_key_c, mon_key_d, mon_path, mon_name;
mon_key_c << "monitor." << ied_usr->LD_info[i].mp_id << ".COM";
mon_key_d << "monitor." << ied_usr->LD_info[i].mp_id << ".DATA";
std::ostringstream mon_key, mon_path, mon_name;
mon_key << "monitor." << ied_usr->LD_info[i].mp_id;
mon_path << device_dir << "/monitor" << i;//终端路径下用monitor+序号作为目录
mon_name << ied_usr->LD_info[i].mp_id;
@@ -437,19 +646,19 @@ void init_loggers() {
SharedAppenderPtr monitor_appender = SharedAppenderPtr(new RollingFileAppender(file_path_m, 1 * 1024 * 1024, 2));
monitor_appender->setLayout(std::auto_ptr<Layout>(new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n")));
Logger mon_logger_c = init_logger(mon_key_c.str(), mon_path.str(), mon_name.str(), monitor_appender);
Logger mon_logger_d = init_logger(mon_key_d.str(), mon_path.str(), mon_name.str(), monitor_appender);
Logger mon_logger = init_logger(mon_key.str(), mon_path.str(), mon_name.str(), monitor_appender);
logger_map[mon_key_c.str()] = TypedLogger(mon_logger_c, LOGTYPE_COM);
logger_map[mon_key_d.str()] = TypedLogger(mon_logger_d, LOGTYPE_DATA);
logger_map[mon_key.str()] = TypedLogger(mon_logger, LOGTYPE_DEFAULT);
DIY_INFOLOG(mon_key_d.str().c_str(),"【NORMAL】监测点:%s - id:%s监测点级日志初始化完毕", ied_usr->LD_info[i].name,ied_usr->LD_info[i].mp_id);
DIY_INFOLOG(mon_key.str().c_str(),"【NORMAL】监测点:%s - id:%s监测点级日志初始化完毕", ied_usr->LD_info[i].name,ied_usr->LD_info[i].mp_id);
}
}
}
//lnk20260303添加日志上送控制 - 初始化时构建日志等级缓存
refresh_log_level_cache_locked();
}
void remove_loggers_by_terminal_id(const char* terminal_id_cstr) {
@@ -465,37 +674,24 @@ void remove_loggers_by_terminal_id(const char* terminal_id_cstr) {
if (strcmp(ied_usr->terminal_id, terminal_id.c_str()) != 0) continue;
// 删除终端日志 logger
std::string com_key = "terminal." + terminal_id + ".COM";
std::string data_key = "terminal." + terminal_id + ".DATA";
std::string terminal_key = "terminal." + terminal_id;;
if (logger_map.count(com_key)) {
logger_map[com_key].logger.removeAllAppenders();
logger_map.erase(com_key);
}
if (logger_map.count(data_key)) {
logger_map[data_key].logger.removeAllAppenders();
logger_map.erase(data_key);
if (logger_map.count(terminal_key)) {
logger_map[terminal_key].logger.removeAllAppenders();
logger_map.erase(terminal_key);
}
// 删除监测点日志 logger
for (int i = 0; i < 10; ++i) {
const char* mp_id = ied_usr->LD_info[i].mp_id;
if (strlen(mp_id) > 0) {
std::string mon_prefix = std::string("monitor.") + mp_id;
std::string mon_key = std::string("monitor.") + mp_id;
std::string mon_com_key = mon_prefix + ".COM";
std::string mon_data_key = mon_prefix + ".DATA";
if (logger_map.count(mon_com_key)) {
logger_map[mon_com_key].logger.removeAllAppenders();
logger_map.erase(mon_com_key);
if (logger_map.count(mon_key)) {
logger_map[mon_key].logger.removeAllAppenders();
logger_map.erase(mon_key);
}
if (logger_map.count(mon_data_key)) {
logger_map[mon_data_key].logger.removeAllAppenders();
logger_map.erase(mon_data_key);
}
}
}

View File

@@ -25,6 +25,18 @@
#include "../json/cjson.h"
#include "../log4cplus/log4.h"//lnk添加log4
///////////////////////////////////////////////////////////////////////lnk20260305数据追踪相关
#include <QHash>
#include <QMutex>
#include <QMutexLocker>
#include <QMapIterator>
#include <QStringList>
// ★MOD: 全局追踪表mp_id -> remaining times
static QMutex g_trace_mutex;
static QHash<QString, int> g_trace_map;
///////////////////////////////////////////////////lnk2024-10-21////////////////////////////////////////////////////////
extern void SendJsonAPI_web(const std::string& strUrl, const char* code, const std::string& json, char** ptr);
extern std::string WEB_INTEGRITY;
@@ -230,7 +242,106 @@ extern int isdelta_flag;//lnk2024-8-16 角型接线标志
void connectlog_pgsql(char* id,char* datetime,int status);
///////////////////////////////////////////////lnk20241021替换web接口//////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////lnk20260305数据追踪
static QString escape_json_string(const QString& s)
{
QString out = s;
out.replace("\\", "\\\\");
out.replace("\"", "\\\"");
out.replace("\r", "\\r");
out.replace("\n", "\\n");
out.replace("\t", "\\t");
return out;
}
// 打开追踪:次数 times比如 5
void process_trace_command(const std::string& id, int times)
{
if (times <= 0) return;
QString qid = QString::fromStdString(id).trimmed();
if (qid.isEmpty()) return;
QMutexLocker lk(&g_trace_mutex);
g_trace_map[qid] = times; // 重新打开就覆盖/重置次数
}
// 查询是否要追踪
static bool trace_is_enabled(const QString& mp_id)
{
QMutexLocker lk(&g_trace_mutex);
auto it = g_trace_map.constFind(mp_id);
return (it != g_trace_map.constEnd() && it.value() > 0);
}
// 命中一次并扣减;扣到 0 自动删
static void trace_hit_and_decrement(const QString& mp_id)
{
QMutexLocker lk(&g_trace_mutex);
auto it = g_trace_map.find(mp_id);
if (it == g_trace_map.end()) return;
int left = it.value();
left -= 1;
if (left <= 0) g_trace_map.erase(it);
else it.value() = left;
}
//追踪61850原始数据
static QString build_mms_multiline_text(const json_block_data* data)
{
QStringList lines;
QMapIterator<QString, double> it(data->mms_str_map);
while (it.hasNext()) {
it.next();
lines << QString("%1 = %2").arg(it.key()).arg(it.value(), 0, 'g', 15);
}
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)
{
const QString mp_id_q = pdata->mp_id;
if (trace_is_enabled(mp_id_q)) {
// 1) 组 json
QString jsonText = build_trace_json(pdata);
// 2) 组 KafkaData
Ckafka_data_t KafkaData;
KafkaData.monitor_id = pdata->monitorId;
KafkaData.mp_id = pdata->mp_id;
KafkaData.strTopic = "DATA_TRACE_TOPIC";
KafkaData.strText = jsonText;
kafka_data_list_mutex.lock();
kafka_data_list.append(KafkaData);
kafka_data_list_mutex.unlock();
// 3) 次数 -1
trace_hit_and_decrement(mp_id_q);
}
}
////////////////////////////////////////////////////////////////////////////////////////////lnk20250710添加频率值存储
struct mp_freq_save {
double G_FREQ;

View File

@@ -41,6 +41,8 @@ public:
bool data_have_statistic;//是否有统计数据0没有1有
QMap<QString, double> mms_str_map; //数据值(61850数据属性名, 数据值)
bool data_trace_flag; //数据追踪标记0不追踪1追踪
};
class Ckafka_data_t //kafka发送数据结构类

View File

@@ -1167,9 +1167,13 @@ std::string prepare_update(const std::string& code_str, const terminal& json_dat
add_indent(xmlStream, indentLevel);
xmlStream << "<processNo>" << json_data.processNo << "</processNo>" << std::endl;
//lnk20250305
add_indent(xmlStream, indentLevel);
xmlStream << "<devKey>" << json_data.dev_key << "</devKey>" << std::endl;
add_indent(xmlStream, indentLevel);
xmlStream << "<loglevel>" << json_data.log_level << "</loglevel>" << std::endl;
// monitorData 部分
for (int i = 0; json_data.line[i].monitor_id[0] != '\0'; i++) {
const monitor& monitor = json_data.line[i];
@@ -1202,6 +1206,9 @@ std::string prepare_update(const std::string& code_str, const terminal& json_dat
add_indent(xmlStream, indentLevel);
xmlStream << "<status>" << monitor.status << "</status>" << std::endl;
add_indent(xmlStream, indentLevel);
xmlStream << "<loglevel>" << monitor.log_level << "</loglevel>" << std::endl;
indentLevel--;
add_indent(xmlStream, indentLevel);
xmlStream << "</monitorData>" << std::endl;
@@ -1376,7 +1383,7 @@ int parse_log(const std::string& json_str) {
return 1;
}
std::string logtype = logtypestr->valuestring;
int logtype = logtypestr->valueint;
// 获取 frontType 字段
cJSON* frontTypestr = cJSON_GetObjectItem(messageBody, "frontType");
@@ -1413,13 +1420,16 @@ int parse_log(const std::string& json_str) {
//校验数据
if((level == "terminal" || level == "measurepoint") &&
(grade == "NORMAL" || grade == "DEBUG") &&
(logtype == "com" || logtype == "data") &&
(!id.empty() && !is_blank(id))){
//开启开关
//开启日志临时开关
process_log_command(id, level, grade, logtype);
}
else if((level == "measurepoint") && (grade == "TRACE") && (!id.empty() && !is_blank(id))){ //数据追踪
//打开监测点数据追踪开关
process_trace_command(id,5); //5表示追踪次数
}
else{
std::cout << "type doesnt match" <<std::endl;
//记录warm
@@ -1611,6 +1621,25 @@ int parse_control(const std::string& json_str, const std::string& output_dir) {
else
std::strncpy(json_data.timestamp, "N/A", sizeof(json_data.timestamp) - 1);
//添加loglevel字段
cJSON* loglevel = cJSON_GetObjectItem(item, "loglevel"); // log_level
int tmp_level = -1;
// 先读取 loglevel
if (loglevel && loglevel->type == cJSON_Number) {
tmp_level = loglevel->valueint;
}
else if (loglevel && loglevel->type == cJSON_String) {
tmp_level = atoi(loglevel->valuestring);
}
// 判断是否合法 (0~3)
if (tmp_level >= 0 && tmp_level <= 3) {
json_data.log_level = tmp_level;
} else {
json_data.log_level = 1; // 默认日志级别
}
printf("terminal log_level: %d\n", json_data.log_level);
// monitorData 解析,填充到 line 数组中
cJSON* monitorData = cJSON_GetObjectItem(item, "monitorData");
if (monitorData != nullptr && monitorData->type == cJSON_Array) {
@@ -1626,6 +1655,27 @@ int parse_control(const std::string& json_str, const std::string& output_dir) {
else
std::strncpy(monitor_data.monitor_id, "N/A", sizeof(monitor_data.monitor_id) - 1);
//添加loglevel字段
cJSON* loglevel = cJSON_GetObjectItem(monitor_item, "loglevel"); // log_level
int tmp_level = -1;
// 先尝试读取 loglevel
if (loglevel && loglevel->type == cJSON_Number) {
tmp_level = loglevel->valueint;
}
else if (loglevel && loglevel->type == cJSON_String) {
tmp_level = atoi(loglevel->valuestring);
}
// 判断是否合法 (0~3)
if (tmp_level >= 0 && tmp_level <= 3) {
monitor_data.log_level = tmp_level;
}
else if (json_data.log_level >= 0 && json_data.log_level <= 3) { // 继承 terminal loglevel
monitor_data.log_level = json_data.log_level;
}
else {
monitor_data.log_level = 1; // 默认 warn
}
cJSON* monitor_name = cJSON_GetObjectItem(monitor_item, "name"); // monitor_name
if (monitor_name && monitor_name->type == cJSON_String)
std::strncpy(monitor_data.monitor_name, monitor_name->valuestring, sizeof(monitor_data.monitor_name) - 1);

View File

@@ -670,6 +670,8 @@ struct monitor // 监测点台账
char timestamp[64];
char status[255];
int log_level;//日志级别0ERROR1WARN2NORMAL3DEBUG
};
struct terminal // 终端台账
{
@@ -692,6 +694,7 @@ struct terminal // 终端台账
char timestamp[64];
monitor line[10]; // 最多 10 个监测点
int log_level;//日志级别0ERROR1WARN2NORMAL3DEBUG
};
#ifdef __cplusplus

View File

@@ -24,12 +24,9 @@
#include "appender.h"
#define LOGTYPE_COM 1
#define LOGTYPE_DATA 2
struct TypedLogger {
log4cplus::Logger logger;
int logtype;
int code;
TypedLogger();
TypedLogger(const log4cplus::Logger& l, int t);
};
@@ -47,7 +44,7 @@ struct DebugSwitch {
void set_level(int level);
void enable_type(int type);
void disable_type(int type);
bool match(const std::string& logger_name, int level, int logtype);
bool match(const std::string& logger_name, int level, int code);
};
extern std::map<std::string, TypedLogger> logger_map;
@@ -71,11 +68,14 @@ log4cplus::Logger init_logger(const std::string& full_name,
const std::string& base_file,
log4cplus::SharedAppenderPtr fileAppender);
void process_log_command(const std::string& id, const std::string& level, const std::string& grade, const std::string& logtype_str);
void process_log_command(const std::string& id, const std::string& level, const std::string& grade, int code);
void update_log_entries_countdown();
/////////////////////////////////////////////////////////////////////lnk20260306数据追踪
void process_trace_command(const std::string& id, int times);
extern "C" {
#endif
void remove_loggers_by_terminal_id(const char* terminal_id_cstr);
@@ -89,7 +89,7 @@ void log_warn(const char* key, const char* msg);
void log_error(const char* key, const char* msg);
void send_reply_to_kafka_c(const char* guid, const char* step, const char* result);
void send_reply_to_kafka_recall(const char* guid, const char* step, const char* result,const char* lineIndex,const char* recallStartDate,const char* recallEndDate);
void send_reply_to_kafka_recall_c(const char* guid, const char* step, const char* result,const char* lineIndex,const char* recallStartDate,const char* recallEndDate);
void format_log_msg(char* buf, size_t buf_size, const char* fmt, ...);
// ====================== ★新增:线程局部变量透传 code ======================

View File

@@ -1328,6 +1328,7 @@ void print_monitor(const monitor* mon) {
printf("Terminal Connect: %s\n", is_empty(mon->terminal_connect) ? "N/A" : mon->terminal_connect);
printf("Timestamp: %s\n", is_empty(mon->timestamp) ? "N/A" : mon->timestamp);
printf("Status: %s\n", is_empty(mon->status) ? "N/A" : mon->status);
printf("Log Level: %d\n", mon->log_level);
}
// 打印 terminal 结构体信息
@@ -1346,6 +1347,7 @@ void print_terminal(const terminal* tmnl) {
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);
printf("Log Level: %d\n", tmnl->log_level);
// 打印监测点信息,如果监测点字段为空,则打印 N/A
int i;

View File

@@ -255,6 +255,9 @@ struct LD_info_t{
rptinfo_t **rptinfo; /**< rptinfo_t* 数组 */
int read_flag ; //CZY 2023-02-28 判断是否将监测点是否有效
//测点日志等级
int log_level; //0 ERROR 1 WARN 2 NORMAL 3 DEBUG
char mp_id[256];//CZY 2023-08-20 监测点编码8afaa
char terminal_code[256];//CZY 2023-08-20 终端编码
//int ld_ins;//CZY 2023-08-20 逻辑设备实例号1
@@ -273,8 +276,8 @@ struct LD_info_t{
int registcount;//lnk20250812
bool has_logged_regist;//lnk20250812
//不使用
int iUnitOfTime;//CZY 2023-08-17 WW 2022年12月7日15:43:34 装置上送事件持续时间单位切换(0-ms; 1-s)
//不使用
int iStatOfTime;//CZY 2023-08-17 WW 2022年12月7日15:48:33 统计数据时间 0-北京时间 1-UTC时间
int iJournalTime;//CZY 2023-08-17 WW 2022年12月7日15:52:32 补招日志时间(0-UTC时间(日志、录波文件均为UTC时间); 1-北京时间(日志北京时间、录波文件UTC时间 注:仅四川地区+8小时读取补招日志))
//不使用
@@ -327,6 +330,8 @@ struct ied_usr_t{
void *cookie;
double last_call_wavelist_time ; //上次召录波列表时间
int log_level; //0 ERROR 1 WARN 2 NORMAL 3 DEBUG
char terminal_id[256];//CZY 2023-08-20 终端id8afaa9a15707483a0157262f8e78077d
char org_name[256];//CZY 2023-08-20 所属单位,例:南京供公司
char maint_name[256];//CZY 2023-08-20 运维单位,例:南京供公司