Files
microser/json/PQSMsg.cpp
2026-05-22 11:34:14 +08:00

1298 lines
44 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "../json/cjson.h"
#include "../json/mms_json_inter.h"
#include "../json/PQSMsg.h"
#include <array>
#include <string>
#include <cstdlib>
#include <ctime>
#include <string>
#include <vector>
#include <map>
#ifndef EMPTY_FLOAT_VALUE
#define EMPTY_FLOAT_VALUE 3.14159f
#endif
extern QMutex kafka_data_list_mutex;
extern QList<Ckafka_data_t> kafka_data_list;
// statType: 0=95值, 1=平均值, 2=最大值, 3=最小值
static std::string MakeStatKey(int statType, const std::string& base)
{
switch (statType) {
case 0: return "G_" + base;
case 1: return base;
case 2: return "MAX_" + base;
case 3: return "MIN_" + base;
default: return base;
}
}
static cJSON* GetObj(cJSON* parent, const char* name)
{
if (parent == NULL || name == NULL) {
return NULL;
}
return cJSON_GetObjectItem(parent, name);
}
static float GetJsonFloat(cJSON* root,
const char* group,
const char* phase,
const std::string& key)
{
cJSON* value = GetObj(root, "Value");
cJSON* g = GetObj(value, group);
cJSON* p = GetObj(g, phase);
cJSON* item = GetObj(p, key.c_str());
if (item == NULL) {
return 0.0f;
}
if (item && item->type == cJSON_Number) {
return static_cast<float>(item->valuedouble);
}
if (item && item->type == cJSON_String && item->valuestring != NULL) {
return static_cast<float>(atof(item->valuestring));
}
return 0.0f;
}
static float GetValue(cJSON* root,
int statType,
const char* group,
const char* phase,
const std::string& base)
{
return GetJsonFloat(root, group, phase, MakeStatKey(statType, base));
}
static void FillDeviation(float val, float& up, float& low)
{
if (val >= 0.0f) {
up = val;
low = 0.0f;
} else {
up = 0.0f;
low = -val;
}
}
static long long GetJsonTimeMs(cJSON* root)
{
cJSON* value = GetObj(root, "Value");
cJSON* item = GetObj(value, "TIME");
if (item == NULL) {
return 0;
}
if (item && item->type == cJSON_Number) {
return static_cast<long long>(item->valuedouble);
}
if (item && item->type == cJSON_String && item->valuestring != NULL) {
return atoll(item->valuestring);
}
return 0;
}
static tagTime MakeTagTimeFromMs(long long ms)
{
time_t sec = static_cast<time_t>(ms / 1000);
struct tm tmv;
#ifdef _WIN32
localtime_s(&tmv, &sec);
#else
localtime_r(&sec, &tmv);
#endif
return tagTime(tmv);
}
// connectionType: 0=星型, 1=角型
std::array<tagPqData_Float, 4> BuildPqFloatDataFromJson(cJSON* jsondata,
int connectionType)
{
std::array<tagPqData_Float, 4> all_data;
const char* PH3[3];
const char* PH4[4];
if (connectionType == 1) {
// 角型
PH3[0] = "AB";
PH3[1] = "BC";
PH3[2] = "CA";
PH4[0] = "AB";
PH4[1] = "BC";
PH4[2] = "CA";
PH4[3] = "T";
}
else {
// 星型
PH3[0] = "A";
PH3[1] = "B";
PH3[2] = "C";
PH4[0] = "A";
PH4[1] = "B";
PH4[2] = "C";
PH4[3] = "T";
}
long long timeMs = GetJsonTimeMs(jsondata);
for (int stat = 0; stat < 4; ++stat) {
tagPqData_Float& dst = all_data[stat];
dst.name = 0;
dst.Data_Type = 0;
if (timeMs > 0) {
dst.time = MakeTagTimeFromMs(timeMs);
}
// 1. Rms[0..8]
// 0-2: 相电压有效值 VRMS
// 3-5: 相电流有效值 IRMS
// 6-8: 线电压有效值 VRMS_LVR
// 注意:根据 ZJ.xml/jsondata只有线电压有效值使用 LVR
if (connectionType == 1) {
// 角型
// 相电压空置
dst.Rms[0] = EMPTY_FLOAT_VALUE;
dst.Rms[1] = EMPTY_FLOAT_VALUE;
dst.Rms[2] = EMPTY_FLOAT_VALUE;
// 电流
for (int i = 0; i < 3; ++i) {
dst.Rms[i + 3] =
GetValue(jsondata, stat, "I", PH3[i], "IRMS");
}
// 线电压
for (int i = 0; i < 3; ++i) {
dst.Rms[i + 6] =
GetValue(jsondata, stat, "V", PH3[i], "VRMS_LVR");
}
}
else {
// 星型
// 相电压
for (int i = 0; i < 3; ++i) {
dst.Rms[i] =
GetValue(jsondata, stat, "V", PH3[i], "VRMS");
}
// 电流
for (int i = 0; i < 3; ++i) {
dst.Rms[i + 3] =
GetValue(jsondata, stat, "I", PH3[i], "IRMS");
}
// 线电压
for (int i = 0; i < 3; ++i) {
dst.Rms[i + 6] =
GetValue(jsondata, stat, "V", PH3[i], "VRMS_LVR");
}
}
// 2. 电压偏差
// 星型:转换函数取 UU/UL[0..2] 作为相电压偏差,线电压偏差空置
// 角型:转换函数前 3 个位置空置,后面取 UU/UL[0..2] 作为线电压偏差
// 因为 jsondata 里没有 DELTA_V_LVR所以统一填 DELTA_V 到 [0..2]
for (int i = 0; i < 3; ++i) {
float dev = GetValue(jsondata, stat, "V", PH3[i], "DELTA_V");
FillDeviation(dev, dst.UU_Deviation[i], dst.UL_Deviation[i]);
}
// 3. 频率偏差 + 频率
dst.F_Deviation[0] = GetValue(jsondata, stat, "V", "T", "DELTA_FREQ");
dst.F_Deviation[1] = GetValue(jsondata, stat, "V", "T", "FREQ");
// 4. UI_Seq
dst.UI_Seq[0][0] = GetValue(jsondata, stat, "V", "T", "VZSEQ");
dst.UI_Seq[0][1] = GetValue(jsondata, stat, "V", "T", "VNSEQ");
dst.UI_Seq[0][2] = GetValue(jsondata, stat, "V", "T", "VPSEQ");
dst.UI_Seq[0][3] = GetValue(jsondata, stat, "V", "T", "V_UNBAN");
dst.UI_Seq[1][0] = GetValue(jsondata, stat, "I", "T", "IZSEQ");
dst.UI_Seq[1][1] = GetValue(jsondata, stat, "I", "T", "INSEQ");
dst.UI_Seq[1][2] = GetValue(jsondata, stat, "I", "T", "IPSEQ");
dst.UI_Seq[1][3] = GetValue(jsondata, stat, "I", "T", "I_UNBAN");
// 5. 基波有效值 FuHarm[*][0]
// 星型FuHarm[0..2][0] 被当作 A/B/C 相电压 V1
// 角型FuHarm[0..2][0] 被当作 AB/BC/CA 线电压 V1
// jsondata 中仍然使用 V1 字段,不额外使用 LVR
for (int i = 0; i < 3; ++i) {
dst.FuHarm[i][0] = GetValue(jsondata, stat, "V", PH3[i], "V1");
dst.FuHarm[i + 3][0] = GetValue(jsondata, stat, "I", PH3[i], "I1");
}
// 6. 基波相角 FuHarmPhase[*][0]
for (int i = 0; i < 3; ++i) {
dst.FuHarmPhase[i][0] =
GetValue(jsondata, stat, "V", PH3[i], "VFUND_ANGLE");
dst.FuHarmPhase[i + 3][0] =
GetValue(jsondata, stat, "I", PH3[i], "IFUND_ANGLE");
}
// 7. 2-50 次谐波有效值
// 星型转换:先取 FuHarm[0..2][1..49] 作为相电压,再取 FuHarm[3..5] 作为电流,线电压空置
// 角型转换:先空置相电压,再取 FuHarm[3..5] 作为电流,再取 FuHarm[0..2] 作为线电压
// 所以这里统一填 FuHarm[0..2]=V2..V50FuHarm[3..5]=I2..I50
for (int h = 1; h < HARMNUM; ++h) {
int no = h + 1;
for (int i = 0; i < 3; ++i) {
dst.FuHarm[i][h] =
GetValue(jsondata, stat, "V", PH3[i], "V" + std::to_string(no));
dst.FuHarm[i + 3][h] =
GetValue(jsondata, stat, "I", PH3[i], "I" + std::to_string(no));
}
}
// 8. 2-50 次谐波相角
for (int h = 1; h < HARMNUM; ++h) {
int no = h + 1;
for (int i = 0; i < 3; ++i) {
dst.FuHarmPhase[i][h] =
GetValue(jsondata, stat, "V", PH3[i], "VA" + std::to_string(no));
dst.FuHarmPhase[i + 3][h] =
GetValue(jsondata, stat, "I", PH3[i], "IA" + std::to_string(no));
}
}
// 9. 间谐波有效值 InHarm[*][0..49]
// 星型InHarm[0..2] 相电压有效InHarm[3..5] 电流有效,线电压空置
// 角型:先空置相电压,再取 InHarm[3..5] 电流,再取 InHarm[0..2] 线电压
// jsondata 中统一使用 SV_x / SI_x
for (int h = 0; h < HARMNUM; ++h) {
for (int i = 0; i < 3; ++i) {
dst.InHarm[i][h].Val =
GetValue(jsondata, stat, "V", PH3[i], "SV_" + std::to_string(h));
dst.InHarm[i + 3][h].Val =
GetValue(jsondata, stat, "I", PH3[i], "SI_" + std::to_string(h));
}
}
// 10. 总功率 Total_Power[4][3]
// phase: A/B/C/T
// index: 0=P, 1=Q, 2=S
for (int i = 0; i < 4; ++i) {
dst.Total_Power[i][0] = GetValue(jsondata, stat, "PQ", PH4[i], "P");
dst.Total_Power[i][1] = GetValue(jsondata, stat, "PQ", PH4[i], "Q");
dst.Total_Power[i][2] = GetValue(jsondata, stat, "PQ", PH4[i], "S");
}
// 11. 谐波功率 Harm_Power[4][50]
// h=0 对应 P1/Q1/S1h=1..49 对应 P2..P50/Q2..Q50/S2..S50
for (int i = 0; i < 4; ++i) {
for (int h = 0; h < HARMNUM; ++h) {
int no = h + 1;
dst.Harm_Power[i][h].P =
GetValue(jsondata, stat, "PQ", PH4[i], "P" + std::to_string(no));
dst.Harm_Power[i][h].Q =
GetValue(jsondata, stat, "PQ", PH4[i], "Q" + std::to_string(no));
dst.Harm_Power[i][h].S =
GetValue(jsondata, stat, "PQ", PH4[i], "S" + std::to_string(no));
}
}
// 12. 谐波含有率 Harm_Contain[6][50]
// 0-2: 电压谐波含有率
// 3-5: 电流谐波含有率
// 转换函数会根据星型/角型决定这些数据输出到相电压区还是线电压区
for (int h = 1; h < HARMNUM; ++h) {
for (int i = 0; i < 3; ++i) {
dst.Harm_Contain[i][h] =
GetValue(jsondata, stat, "V", PH3[i], "SV_" + std::to_string(h));
dst.Harm_Contain[i + 3][h] =
GetValue(jsondata, stat, "I", PH3[i], "SI_" + std::to_string(h));
}
}
// 13. 谐波总畸变率 Harm_Aberrance[6]
// 0-2: 电压 THD
// 3-5: 电流 THD
for (int i = 0; i < 3; ++i) {
dst.Harm_Aberrance[i] =
GetValue(jsondata, stat, "V", PH3[i], "VTHD");
dst.Harm_Aberrance[i + 3] =
GetValue(jsondata, stat, "I", PH3[i], "ITHD");
}
// 14. 功率因数
for (int i = 0; i < 4; ++i) {
dst.Cos_PF[i] = GetValue(jsondata, stat, "PQ", PH4[i], "PF");
dst.Cos_DF[i] = GetValue(jsondata, stat, "PQ", PH4[i], "DF");
}
// 15. 波动、短闪、长闪
// 目前 jsondata 中没有 LVR 字段,统一填 A/B/C 三相字段
// 星型转换函数输出三相数据,线电压位置空置
// 角型转换函数先空置三相位置,再把这三个值输出到线电压位置
for (int i = 0; i < 3; ++i) {
dst.U_Fluctuation[i] = GetValue(jsondata, stat, "V", PH3[i], "FLUC");
dst.U_Flicker[i] = GetValue(jsondata, stat, "V", PH3[i], "PST");
dst.UL_Flicker[i] = GetValue(jsondata, stat, "V", PH3[i], "PLT");
}
}
return all_data;
}
////////////////////////////////////////////////////实时部分
static float GetRtJsonFloat(cJSON* root,
const char* group,
const char* phase,
const std::string& key)
{
cJSON* value = GetObj(root, "Value");
cJSON* g = GetObj(value, group);
cJSON* p = GetObj(g, phase);
cJSON* item = GetObj(p, key.c_str());
if (item == NULL) {
return 0.0f;
}
if (item && item->type == cJSON_Number) {
return static_cast<float>(item->valuedouble);
}
if (item && item->type == cJSON_String && item->valuestring != NULL) {
return static_cast<float>(atof(item->valuestring));
}
return 0.0f;
}
static float GetRtValue(cJSON* root,
const char* group,
const char* phase,
const std::string& key)
{
return GetRtJsonFloat(root, group, phase, key);
}
static long long GetRtJsonTimeMs(cJSON* root)
{
cJSON* value = GetObj(root, "Value");
cJSON* item = GetObj(value, "TIME");
if (item == NULL) {
return 0;
}
if (item && item->type == cJSON_Number) {
return static_cast<long long>(item->valuedouble);
}
if (item && item->type == cJSON_String && item->valuestring != NULL) {
return atoll(item->valuestring);
}
return 0;
}
static tagTime MakeRtTagTimeFromMs(long long ms)
{
time_t sec = static_cast<time_t>(ms / 1000);
struct tm tmv;
#ifdef _WIN32
localtime_s(&tmv, &sec);
#else
localtime_r(&sec, &tmv);
#endif
return tagTime(tmv);
}
static void FillRtDeviation(float val, float& up, float& low)
{
if (val >= 0.0f) {
up = val;
low = 0.0f;
} else {
up = 0.0f;
low = -val;
}
}
// wiringType: 0=星型, 1=角型
RealtagPqDate_float BuildRealPqDataFromJson(cJSON* jsondata, int wiringType)
{
RealtagPqDate_float realdata;
const bool isDelta = (wiringType == 1);
const char* PH3[3];
const char* PH4[4];
if (isDelta) {
PH3[0] = "AB";
PH3[1] = "BC";
PH3[2] = "CA";
PH4[0] = "AB";
PH4[1] = "BC";
PH4[2] = "CA";
PH4[3] = "T";
} else {
PH3[0] = "A";
PH3[1] = "B";
PH3[2] = "C";
PH4[0] = "A";
PH4[1] = "B";
PH4[2] = "C";
PH4[3] = "T";
}
long long timeMs = GetRtJsonTimeMs(jsondata);
if (timeMs > 0) {
realdata.time = MakeRtTagTimeFromMs(timeMs);
}
// 1. Rms[0..8]
// 实时结构注释:相电压、线电压、电流有效值
if (isDelta) {
// 角型:相电压空置,线电压从 AB/BC/CA 的 VRMS 取,电流也按 AB/BC/CA 取
for (int i = 0; i < 3; ++i) {
realdata.Rms[i] = EMPTY_FLOAT_VALUE;
realdata.Rms[i + 3] = GetRtValue(jsondata, "V", PH3[i], "VRMS");
realdata.Rms[i + 6] = GetRtValue(jsondata, "I", PH3[i], "IRMS");
}
} else {
// 星型:相电压 A/B/C VRMS线电压 A/B/C VRMS_LVR电流 A/B/C IRMS
for (int i = 0; i < 3; ++i) {
realdata.Rms[i] = GetRtValue(jsondata, "V", PH3[i], "VRMS");
realdata.Rms[i + 3] = GetRtValue(jsondata, "V", PH3[i], "VRMS_LVR");
realdata.Rms[i + 6] = GetRtValue(jsondata, "I", PH3[i], "IRMS");
}
}
// 2. 电压偏差
// 星型序列化取 9-11 为相电压偏差;角型序列化 12-14 为线电压偏差。
// 两种最终都从 UU/UL[0..2] 取,所以填 [0..2] 即可。
for (int i = 0; i < 3; ++i) {
float dev = GetRtValue(jsondata, "V", PH3[i], "DELTA_V");
FillRtDeviation(dev, realdata.UU_Deviation[i], realdata.UL_Deviation[i]);
}
// 3. THD[0..5]
// 星型THD[0..2] 相电压THD[3..5] 电流
// 角型THD[0..2] 填线电压Delta_RtHarmV 也取 0..2
for (int i = 0; i < 3; ++i) {
realdata.THD[i] = GetRtValue(jsondata, "V", PH3[i], "VTHD");
realdata.THD[i + 3] = GetRtValue(jsondata, "I", PH3[i], "ITHD");
}
// 4. FREQ[0..1]
realdata.FREQ[0] = GetRtValue(jsondata, "V", "T", "FREQ");
realdata.FREQ[1] = GetRtValue(jsondata, "V", "T", "DELTA_FREQ");
// 5. UI_Seq[2][5]
// 序列化时输出 [0][0,1,2,4] 和 [1][0,1,2,4],所以 [3] 可放零序不平衡备用,[4] 放负序不平衡
realdata.UI_Seq[0][0] = GetRtValue(jsondata, "V", "T", "VZSEQ");
realdata.UI_Seq[0][1] = GetRtValue(jsondata, "V", "T", "VNSEQ");
realdata.UI_Seq[0][2] = GetRtValue(jsondata, "V", "T", "VPSEQ");
realdata.UI_Seq[0][3] = GetRtValue(jsondata, "V", "T", "VZSEQ_UNBAN");
realdata.UI_Seq[0][4] = GetRtValue(jsondata, "V", "T", "V_UNBAN");
realdata.UI_Seq[1][0] = GetRtValue(jsondata, "I", "T", "IZSEQ");
realdata.UI_Seq[1][1] = GetRtValue(jsondata, "I", "T", "INSEQ");
realdata.UI_Seq[1][2] = GetRtValue(jsondata, "I", "T", "IPSEQ");
realdata.UI_Seq[1][3] = GetRtValue(jsondata, "I", "T", "IZSEQ_UNBAN");
realdata.UI_Seq[1][4] = GetRtValue(jsondata, "I", "T", "I_UNBAN");
// 6. TOTAL_POWER[4][3], COS_PF, COS_DF
for (int i = 0; i < 4; ++i) {
realdata.TOTAL_POWER[i][0] = GetRtValue(jsondata, "PQ", PH4[i], "P");
realdata.TOTAL_POWER[i][1] = GetRtValue(jsondata, "PQ", PH4[i], "Q");
realdata.TOTAL_POWER[i][2] = GetRtValue(jsondata, "PQ", PH4[i], "S");
realdata.COS_PF[i] = GetRtValue(jsondata, "PQ", PH4[i], "PF");
realdata.COS_DF[i] = GetRtValue(jsondata, "PQ", PH4[i], "DF");
}
// 7. 基波有效值和基波相角
// HARMV/HARMI/HARMVP/HARMIP 的 [0] 被序列化函数当作基波 V1/I1 和基波角度。
for (int i = 0; i < 3; ++i) {
realdata.HARMV[i][0] = GetRtValue(jsondata, "V", PH3[i], "V1");
realdata.HARMI[i][0] = GetRtValue(jsondata, "I", PH3[i], "I1");
realdata.HARMVP[i][0] = GetRtValue(jsondata, "V", PH3[i], "VFUND_ANGLE");
// 实时 JSON 中电流基波相角字段是 I_ANGLE
realdata.HARMIP[i][0] = GetRtValue(jsondata, "I", PH3[i], "I_ANGLE");
}
// 8. 2~50 次谐波、电压/电流相角
for (int h = 1; h < HARMNUM; ++h) {
int no = h + 1;
for (int i = 0; i < 3; ++i) {
realdata.HARMV[i][h] =
GetRtValue(jsondata, "V", PH3[i], "V" + std::to_string(no));
realdata.HARMI[i][h] =
GetRtValue(jsondata, "I", PH3[i], "I" + std::to_string(no));
realdata.HARMVP[i][h] =
GetRtValue(jsondata, "V", PH3[i], "VA" + std::to_string(no));
// 如果以后 JSON 有 IA2~IA50这里会自动取当前样例没有则为 0
realdata.HARMIP[i][h] =
GetRtValue(jsondata, "I", PH3[i], "IA" + std::to_string(no));
}
}
// 9. 间谐波电压 INHARMV[3][50]
for (int h = 0; h < HARMNUM; ++h) {
for (int i = 0; i < 3; ++i) {
realdata.INHARMV[i][h] =
GetRtValue(jsondata, "V", PH3[i], "SV_" + std::to_string(h));
}
}
return realdata;
}
///////////////////////////////////////////////////////通用方式根据不同的模板到pqd查找名称建立映射然后到json中取值填入再序列化输出
struct PqdMeta {
int idx;
std::string name;
std::string phase;
int harmStart;
int harmEnd;
PqdMeta() : idx(-1), harmStart(0), harmEnd(0) {}
};
static int GetInt(cJSON* obj, const char* name, int defVal = 0)
{
cJSON* item = GetObj(obj, name);
if (!item) return defVal;
if (item->type == cJSON_Number) return item->valueint;
if (item->type == cJSON_String && item->valuestring) return atoi(item->valuestring);
return defVal;
}
static std::string GetStr(cJSON* obj, const char* name)
{
cJSON* item = GetObj(obj, name);
if (item && item->type == cJSON_String && item->valuestring) {
return item->valuestring;
}
return "";
}
static float GetJsonFloat(cJSON* root,
const std::string& group,
const std::string& phase,
const std::string& key)
{
cJSON* value = GetObj(root, "Value");
cJSON* g = GetObj(value, group.c_str());
cJSON* p = GetObj(g, phase.c_str());
cJSON* item = GetObj(p, key.c_str());
if (!item) return 0.0f;
if (item->type == cJSON_Number) {
return static_cast<float>(item->valuedouble);
}
if (item->type == cJSON_String && item->valuestring) {
return static_cast<float>(atof(item->valuestring));
}
return 0.0f;
}
static std::string Base64Encode(const unsigned char* bytes_to_encode, size_t in_len)
{
static const char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) +
((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) +
((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (i = 0; i < 4; i++) {
ret += base64_chars[char_array_4[i]];
}
i = 0;
}
}
if (i) {
for (j = i; j < 3; j++) {
char_array_3[j] = '\0';
}
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) +
((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) +
((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; j < i + 1; j++) {
ret += base64_chars[char_array_4[j]];
}
while (i++ < 3) {
ret += '=';
}
}
return ret;
}
static std::string FloatBufferToBase64(const std::vector<float>& buffer)
{
const unsigned char* data =
reinterpret_cast<const unsigned char*>(buffer.data());
return Base64Encode(data, buffer.size() * sizeof(float));
}
static std::map<int, PqdMeta> BuildPqdMetaMap(cJSON* tplRoot)
{
std::map<int, PqdMeta> result;
cJSON* pqdArr = GetObj(tplRoot, "Pqd");
if (!pqdArr || !pqdArr->type == cJSON_Array){
return result;
}
int size = cJSON_GetArraySize(pqdArr);
for (int i = 0; i < size; ++i) {
cJSON* item = cJSON_GetArrayItem(pqdArr, i);
if (!item) continue;
PqdMeta meta;
meta.idx = GetInt(item, "Idx", -1);
meta.name = GetStr(item, "Name");
meta.phase = GetStr(item, "Phase");
meta.harmStart = GetInt(item, "HarmStart", 0);
meta.harmEnd = GetInt(item, "HarmEnd", 0);
if (meta.idx >= 0) {
result[meta.idx] = meta;
}
}
return result;
}
static cJSON* FindDataSet(cJSON* tplRoot, const std::string& dataSetName)
{
cJSON* cldevArr = GetObj(tplRoot, "Cldev");
if (!cldevArr || !cldevArr->type == cJSON_Array){
return NULL;
}
int cldevSize = cJSON_GetArraySize(cldevArr);
for (int i = 0; i < cldevSize; ++i) {
cJSON* cldev = cJSON_GetArrayItem(cldevArr, i);
cJSON* dsArr = GetObj(cldev, "DataSet");
if (!dsArr || !dsArr->type == cJSON_Array){
continue;
}
int dsSize = cJSON_GetArraySize(dsArr);
for (int j = 0; j < dsSize; ++j) {
cJSON* ds = cJSON_GetArrayItem(dsArr, j);
std::string name = GetStr(ds, "Name");
if (name == dataSetName) {
return ds;
}
}
}
return NULL;
}
static std::vector<int> GetDataArrayIdxList(cJSON* dataSet)
{
std::vector<int> result;
cJSON* arr = GetObj(dataSet, "DataArray");
if (!arr || !arr->type == cJSON_Array) {
return result;
}
int size = cJSON_GetArraySize(arr);
for (int i = 0; i < size; ++i) {
cJSON* item = cJSON_GetArrayItem(arr, i);
int idx = GetInt(item, "Idx", -1);
if (idx >= 0) {
result.push_back(idx);
}
}
return result;
}
static std::string StatPrefix(int statType)
{
// 0=95, 1=平均, 2=最大, 3=最小
switch (statType) {
case 0: return "G_";
case 1: return "";
case 2: return "MAX_";
case 3: return "MIN_";
default: return "";
}
}
static std::string MakeJsonKey(const std::string& baseKey,
bool isStat,
int statType)
{
if (!isStat) {
return baseKey;
}
return StatPrefix(statType) + baseKey;
}
static float GetByKey(cJSON* jsondata,
const std::string& group,
const std::string& phase,
const std::string& baseKey,
bool isStat,
int statType)
{
return GetJsonFloat(jsondata, group, phase,
MakeJsonKey(baseKey, isStat, statType));
}
static void AppendHarmValues(std::vector<float>& out,
cJSON* jsondata,
const PqdMeta& meta,
const std::string& group,
const std::string& prefix,
bool isStat,
int statType)
{
int start = meta.harmStart;
int end = meta.harmEnd;
if (start <= 0 && end <= 0) {
out.push_back(0.0f);
return;
}
for (int h = start; h <= end; ++h) {
out.push_back(GetByKey(jsondata, group, meta.phase,
prefix + std::to_string(h),
isStat, statType));
}
}
static void AppendInHarmValues(std::vector<float>& out,
cJSON* jsondata,
const PqdMeta& meta,
const std::string& group,
const std::string& prefix,
bool isStat,
int statType)
{
int count = meta.harmEnd - meta.harmStart + 1;
if (count <= 0) {
count = 50;
}
for (int i = 0; i < count; ++i) {
out.push_back(GetByKey(jsondata, group, meta.phase,
prefix + std::to_string(i),
isStat, statType));
}
}
static float GetSingleValueByMeta(cJSON* jsondata,
const PqdMeta& meta,
bool isStat,
int statType)
{
const std::string& n = meta.name;
const std::string& ph = meta.phase;
if (n == "Pq_Freq") return GetByKey(jsondata, "V", "T", "FREQ", isStat, statType);
if (n == "Pq_FreqDev") return GetByKey(jsondata, "V", "T", "DELTA_FREQ", isStat, statType);
if (n == "Pq_RmsU") return GetByKey(jsondata, "V", ph, "VRMS", isStat, statType);
if (n == "Pq_RmsLU") return GetByKey(jsondata, "V", ph, "VRMS_LVR", isStat, statType);
if (n == "Pq_RmsI") return GetByKey(jsondata, "I", ph, "IRMS", isStat, statType);
if (n == "Pq_RmsFundU") return GetByKey(jsondata, "V", ph, "V1", isStat, statType);
if (n == "Pq_RmsFundLU") return GetByKey(jsondata, "V", ph, "V1", isStat, statType);
if (n == "Pq_RmsFundI") return GetByKey(jsondata, "I", ph, "I1", isStat, statType);
if (n == "Pq_UDev") return GetByKey(jsondata, "V", ph, "DELTA_V", isStat, statType);
if (n == "Pq_LUDev") return GetByKey(jsondata, "V", ph, "DELTA_V", isStat, statType);
if (n == "Pq_FundUAng") return GetByKey(jsondata, "V", ph, "VFUND_ANGLE", isStat, statType);
if (n == "Pq_FundLUAng") return GetByKey(jsondata, "V", ph, "VFUND_ANGLE", isStat, statType);
if (n == "Pq_FundIAng") return GetByKey(jsondata, "I", ph, "I_ANGLE", isStat, statType);
if (n == "Pq_ThdU") return GetByKey(jsondata, "V", ph, "VTHD", isStat, statType);
if (n == "Pq_ThdLU") return GetByKey(jsondata, "V", ph, "VTHD", isStat, statType);
if (n == "Pq_ThdI") return GetByKey(jsondata, "I", ph, "ITHD", isStat, statType);
if (n == "Pq_SeqZeroU") return GetByKey(jsondata, "V", "T", "VZSEQ", isStat, statType);
if (n == "Pq_SeqNegU") return GetByKey(jsondata, "V", "T", "VNSEQ", isStat, statType);
if (n == "Pq_SeqPosU") return GetByKey(jsondata, "V", "T", "VPSEQ", isStat, statType);
if (n == "Pq_UnbalNegU") return GetByKey(jsondata, "V", "T", "V_UNBAN", isStat, statType);
if (n == "Pq_UnbalZeroU") return GetByKey(jsondata, "V", "T", "VZSEQ_UNBAN", isStat, statType);
if (n == "Pq_SeqZeroI") return GetByKey(jsondata, "I", "T", "IZSEQ", isStat, statType);
if (n == "Pq_SeqNegI") return GetByKey(jsondata, "I", "T", "INSEQ", isStat, statType);
if (n == "Pq_SeqPosI") return GetByKey(jsondata, "I", "T", "IPSEQ", isStat, statType);
if (n == "Pq_UnbalNegI") return GetByKey(jsondata, "I", "T", "I_UNBAN", isStat, statType);
if (n == "Pq_UnbalZeroI") return GetByKey(jsondata, "I", "T", "IZSEQ_UNBAN", isStat, statType);
if (n == "Pq_P") return GetByKey(jsondata, "PQ", ph, "P", isStat, statType);
if (n == "Pq_Q") return GetByKey(jsondata, "PQ", ph, "Q", isStat, statType);
if (n == "Pq_S") return GetByKey(jsondata, "PQ", ph, "S", isStat, statType);
if (n == "Pq_TotP") return GetByKey(jsondata, "PQ", "T", "P", isStat, statType);
if (n == "Pq_TotQ") return GetByKey(jsondata, "PQ", "T", "Q", isStat, statType);
if (n == "Pq_TotS") return GetByKey(jsondata, "PQ", "T", "S", isStat, statType);
if (n == "Pq_PF") return GetByKey(jsondata, "PQ", ph, "PF", isStat, statType);
if (n == "Pq_DF") return GetByKey(jsondata, "PQ", ph, "DF", isStat, statType);
if (n == "Pq_TotPF") return GetByKey(jsondata, "PQ", "T", "PF", isStat, statType);
if (n == "Pq_TotDF") return GetByKey(jsondata, "PQ", "T", "DF", isStat, statType);
if (n == "Pq_Fluct") return GetByKey(jsondata, "V", ph, "FLUC", isStat, statType);
if (n == "Pq_LFluct") return GetByKey(jsondata, "V", ph, "FLUC", isStat, statType);
if (n == "Pq_Pst") return GetByKey(jsondata, "V", ph, "PST", isStat, statType);
if (n == "Pq_LPst") return GetByKey(jsondata, "V", ph, "PST", isStat, statType);
if (n == "Pq_Plt") return GetByKey(jsondata, "V", ph, "PLT", isStat, statType);
if (n == "Pq_LPlt") return GetByKey(jsondata, "V", ph, "PLT", isStat, statType);
return 0.0f;
}
static void AppendValueByMeta(std::vector<float>& out,
cJSON* jsondata,
const PqdMeta& meta,
bool isStat,
int statType)
{
const std::string& n = meta.name;
if (n == "Pq_HarmU" || n == "Pq_HarmLU") {
AppendHarmValues(out, jsondata, meta, "V", "V", isStat, statType);
return;
}
if (n == "Pq_HarmI") {
AppendHarmValues(out, jsondata, meta, "I", "I", isStat, statType);
return;
}
if (n == "Pq_HarmUAng" || n == "Pq_HarmLUAng") {
AppendHarmValues(out, jsondata, meta, "V", "VA", isStat, statType);
return;
}
if (n == "Pq_HarmIAng") {
AppendHarmValues(out, jsondata, meta, "I", "IA", isStat, statType);
return;
}
if (n == "Pq_InHarmUR" || n == "Pq_InHarmLU") {
AppendInHarmValues(out, jsondata, meta, "V", "SV_", isStat, statType);
return;
}
if (n == "Pq_InHarmIAmp") {
AppendInHarmValues(out, jsondata, meta, "I", "SI_", isStat, statType);
return;
}
if (n == "Pq_HarmP" || n == "Pq_FundP" || n == "Pq_TotHarmP") {
AppendHarmValues(out, jsondata, meta, "PQ", "P", isStat, statType);
return;
}
if (n == "Pq_HarmQ" || n == "Pq_FundQ" || n == "Pq_TotHarmQ") {
AppendHarmValues(out, jsondata, meta, "PQ", "Q", isStat, statType);
return;
}
if (n == "Pq_HarmS" || n == "Pq_FundS" || n == "Pq_TotHarmS") {
AppendHarmValues(out, jsondata, meta, "PQ", "S", isStat, statType);
return;
}
out.push_back(GetSingleValueByMeta(jsondata, meta, isStat, statType));
}
// dataSetName 示例:
// "Ds$Pqd$Rt$01"
// "Ds$Pqd$Rt$HarmV$01"
// "Ds$Pqd$Stat$01"
std::string BuildBase64ByTemplate(cJSON* templateRoot,
cJSON* jsondata,
const std::string& dataSetName,
bool isStat,
int statType)
{
std::map<int, PqdMeta> metaMap = BuildPqdMetaMap(templateRoot);
cJSON* dataSet = FindDataSet(templateRoot, dataSetName);
if (!dataSet) {
return "";
}
std::vector<int> idxList = GetDataArrayIdxList(dataSet);
std::vector<float> floatBuffer;
floatBuffer.reserve(2048);
for (size_t i = 0; i < idxList.size(); ++i) {
int idx = idxList[i];
std::map<int, PqdMeta>::const_iterator it = metaMap.find(idx);
if (it == metaMap.end()) {
floatBuffer.push_back(0.0f);
continue;
}
AppendValueByMeta(floatBuffer, jsondata, it->second, isStat, statType);
}
return FloatBufferToBase64(floatBuffer);
}
/////////////////////////////////////////////////////////////////////////////////////发送函数
///////////////////////////////////////////////////////////////////////////////////////上送数据的json格式
// 单条 DataArray 数据
struct DataArrayItem {
int DataAttr;
time_t DataTimeSec;
time_t DataTimeUSec;
int DataTag;
std::string Data;
};
// Msg 对象
struct MsgObj {
int Cldid;
int DataType;
int DataAttr;
int DsNameIdx;
std::vector<DataArrayItem> DataArray;
};
// 整体
struct FullObj {
std::string mac;
int Mid;
int Did;
int Pri;
int Type;
MsgObj Msg;
};
// DataArrayItem -> cJSON
static cJSON* DataArrayItemToJson(const DataArrayItem& d)
{
cJSON* j = cJSON_CreateObject();
cJSON_AddNumberToObject(j, "dataAttr", d.DataAttr);
cJSON_AddNumberToObject(j, "dataTimeSec", (double)d.DataTimeSec);
cJSON_AddNumberToObject(j, "dataTimeUSec", (double)d.DataTimeUSec);
cJSON_AddNumberToObject(j, "dataTag", d.DataTag);
cJSON_AddStringToObject(j, "data", d.Data.c_str());
return j;
}
// MsgObj -> cJSON
static cJSON* MsgObjToJson(const MsgObj& m)
{
cJSON* j = cJSON_CreateObject();
cJSON_AddNumberToObject(j, "clDid", m.Cldid);
cJSON_AddNumberToObject(j, "dataType", m.DataType);
cJSON_AddNumberToObject(j, "dataAttr", m.DataAttr);
cJSON_AddNumberToObject(j, "dsNameIdx", m.DsNameIdx);
// dataArray
cJSON* arr = cJSON_CreateArray();
for (size_t i = 0; i < m.DataArray.size(); ++i) {
cJSON_AddItemToArray(
arr,
DataArrayItemToJson(m.DataArray[i]));
}
cJSON_AddItemToObject(j, "dataArray", arr);
return j;
}
// FullObj -> cJSON
static cJSON* FullObjToJson(const FullObj& f)
{
cJSON* j = cJSON_CreateObject();
cJSON_AddStringToObject(j, "id", f.mac.c_str());
cJSON_AddNumberToObject(j, "mid", f.Mid);
cJSON_AddNumberToObject(j, "did", f.Did);
cJSON_AddNumberToObject(j, "pri", f.Pri);
cJSON_AddNumberToObject(j, "type", f.Type);
cJSON_AddItemToObject(j, "msg",
MsgObjToJson(f.Msg));
return j;
}
std::string generate_json(
const std::string mac,
int Mid,
int Did,
int Pri,
int Type,
int Cldid,
int DataType,
int DataAttr,
int DsNameIdx,
const std::vector<DataArrayItem>& dataArray
)
{
FullObj fobj;
fobj.mac = mac;
fobj.Mid = Mid;
fobj.Did = Did;
fobj.Pri = Pri;
fobj.Type = Type;
fobj.Msg.Cldid = Cldid;
fobj.Msg.DataType = DataType;
fobj.Msg.DataAttr = DataAttr;
fobj.Msg.DsNameIdx = DsNameIdx;
fobj.Msg.DataArray = dataArray;
// 转 cJSON
cJSON* root = FullObjToJson(fobj);
// 输出 json 字符串
char* jsonStr = cJSON_PrintUnformatted(root);
std::string result;
if (jsonStr != NULL) {
result = jsonStr;
free(jsonStr);
}
cJSON_Delete(root);
return result;
}
//时间转换函数
time_t ConvertToTimestamp(const tagTime& time) {
struct tm t = {};
t.tm_year = time.DeviceYear - 1900; // tm_year 从 1900 开始计
t.tm_mon = time.DeviceMonth - 1; // tm_mon 从 01月开始
t.tm_mday = time.DeviceDay;
t.tm_hour = time.DeviceHour;
t.tm_min = time.DeviceMinute;
t.tm_sec = time.DeviceSecond;
// 返回时间戳(本地时间)
return mktime(&t);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////实时数据封装发送
void enqueue_realtime_pq(const RealtagPqDate_float& realdata,
int nPTType,
unsigned char cid,
const std::string& mac,
const std::string& mpid,
int idx)
{
// 先根据 devIdxMap 的配置决定编码分支:
// 2: 基础数据 3: 谐波电压含有率 4: 谐波电流有效值 5: 间谐波电压含有率
std::string base64;
// 这里尝试用 mac 作为 key 获取 idx如果项目里 devIdxMap 的 key 不是 mac
// 你可以把这里改成对应的设备 iddevid。未命中则再尝试用规范化后的 mac。
switch (idx) {
case 2: // 基础数据(根据接线方式选择转换方法 数据全集解析)
base64 = realdata.ConvertToBase64(nPTType);
break;
case 3: // 谐波电压含有率
base64 = realdata.ConvertToBase64_RtHarmV(nPTType);
break;
case 4: // 谐波电流有效值(幅值)
base64 = realdata.ConvertToBase64_RtHarmI();
break;
case 5: // 间谐波电压含有率
base64 = realdata.ConvertToBase64_RtInHarmV();
break;
default:
// 未知 idx回退到基础数据
base64 = realdata.ConvertToBase64(nPTType);
break;
} else {
// 未配置 idx回退到基础数据
base64 = realdata.ConvertToBase64(nPTType);
}
//std::cout << base64 << std::endl;
//lnk实时数据使用接口发送20250711
time_t data_time = ConvertToTimestamp(realdata.time);
std::vector<DataArrayItem> arr;
arr.push_back({1, //数据属性 -1-无, 0-“Rt”,1-“Max”,2-“Min”,3-“Avg”,4-“Cp95”
data_time, //数据转换出来的时间数据时标相对1970年的秒无效填入“-1”
0, //数据时标,微秒钟,无效填入“-1”
0, //数据标识1-标识数据异常
base64});
std::string js = generate_json(
mac, //设备唯一标识规范化后的MAC地址如“001122334455”不带分隔符字母大写
-1, //需应答的报文订阅者收到后需以此ID应答无需应答填入“-1”
1, //设备唯一标识Ldid填入0代表Ndid,后续根据商议决定填id还是数字
1, //报文处理的优先级1 I类紧急请求/响应 2 II类紧急请求/响应 3 普通请求/响应 4 广播报文
0x1302, //设备数据主动上送的数据类型
cid, //逻辑子设备ID0-逻辑设备本身,无填-1
0x04, //数据类型固定为电能质量数据
1, //数据属性无“0”、实时“1”、统计“2”等
idx, //数据集序号(以数据集方式上送),无填-1
arr //数据数组
);
//std::cout << js << std::en
Ckafka_data_t data;
data.monitor_id = 1; //上送的实时数据没有测点序号统一填1
data.strTopic = TOPIC_RTDATA; //实时topic
data.strText = js;
data.mp_id = mpid; //监测点id
kafka_data_list_mutex.lock(); //加锁
kafka_data_list.append(data); //添加 kafka发送链表
kafka_data_list_mutex.unlock(); //解锁
}
////////////////////////////////////////////////////////////////////////////////统计数据打包发送
// 封装:组装统计数据并入队发送
void enqueue_stat_pq(const std::string& max_base64Str,
const std::string& min_base64Str,
const std::string& avg_base64Str,
const std::string& cp95_base64Str,
time_t data_time,
const std::string& mac,
short cid,
const std::string& mpid)
{
std::vector<DataArrayItem> arr;
arr.push_back({1, //数据属性 -1-无, 0-“Rt”,1-“Max”,2-“Min”,3-“Avg”4-“Cp95”
data_time, //数据转换出来的时间数据时标相对1970年的秒无效填入“-1”
0, //数据时标,微秒钟,无效填入“-1”
0, //数据标识1-标识数据异常
max_base64Str});
arr.push_back({2, data_time, 0, 0, min_base64Str});
arr.push_back({3, data_time, 0, 0, avg_base64Str});
arr.push_back({4, data_time, 0, 0, cp95_base64Str});
std::string js = generate_json(
normalize_mac(mac),
-1, //需应答的报文订阅者收到后需以此ID应答无需应答填入“-1”
1, //设备唯一标识Ldid填入0代表Ndid,后续根据商议决定填id还是数字
1, //报文处理的优先级1 I类紧急请求/响应 2 II类紧急请求/响应 3 普通请求/响应 4 广播报文
0x1302, //设备数据主动上送的数据类型
cid, //逻辑子设备ID0-逻辑设备本身,无填-1avg_data.name
0x04, //数据类型固定为电能质量
2, //数据属性无“0”、实时“1”、统计“2”等
1, //数据集序号(以数据集方式上送),无填-1
arr //数据数组
);
//std::cout << js << std::endl;
Ckafka_data_t data;
data.monitor_no = cid; //监测点序号avg_data.name
data.strTopic = TOPIC_STAT;//统计topic
data.strText = js;
data.mp_id = mpid; //监测点id
kafka_data_list_mutex.lock(); //加锁
kafka_data_list.append(data); //添加 kafka发送链表
kafka_data_list_mutex.unlock(); //解锁
std::cout << "Successfully assembled tagPqData for line: "
<< cid << std::endl;
}
////////////////////////////////////////////////////////////////////////////////////
//以下仅估计开发时间,不包括代码编译调试和测试
/*后续接入云平台云平台需要根据61850协议进行台账下发需要对比云和微服务的台账差异看是否能统一各取所需 一天
实时数据请求需要添加参数处理idx区分数据集 半天
补招逻辑有差异61850是全补招云是事件和稳态分开补招下发按照61850的来 徐扬添加
暂态上送逻辑基本一致,对比接口上送数据即可 半天
云需要添加映射文件接口给61850使用 徐扬添加
台账变更逻辑一样,对比台账看是否能统一 一天
请求响应、装置连接状态、前置心跳、文件上传、文件下载、文件传输、读取文件目录、文件删除、装置重启指令的处理逻辑基本一致需要对比交互json下发的要根据61850需求来部分肯定需要修改 五天
日志上传方案基本一致,代码基本一致,但是上送日志内容可能不一样,可以不修改
mq主题云前置有cloudtopic用于装置控制61850是filetopic按照61850的来 徐扬添加针对61850的装置控制逻辑
进程控制要按照61850的来徐扬添加
上传下载接口基本一致
要添加61850到云的指标映射关系 二天
要添加动态云模板的存储(文件/内存) 和解析代码 五天+
要添加61850数据到云数据的转换、复用代码但是代码是写死的一种模板可以先完成后续改动态复用代码编写数据上送和编译测试 5天+
当前基础的转换框架已搭建需要基于61850当前的运行控制框架添加模板控制数据转换控制数据上送控制等预计5天+
*/