#include "../json/cjson.h" #include "../json/mms_json_inter.h" #include "../json/PQSMsg.h" #include #include #include #include #include #include #include #ifndef EMPTY_FLOAT_VALUE #define EMPTY_FLOAT_VALUE 3.14159f #endif extern QMutex kafka_data_list_mutex; extern QList 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(item->valuedouble); } if (item && item->type == cJSON_String && item->valuestring != NULL) { return static_cast(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(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(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 BuildPqFloatDataFromJson(cJSON* jsondata, int connectionType) { std::array 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..V50,FuHarm[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/S1,h=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(item->valuedouble); } if (item && item->type == cJSON_String && item->valuestring != NULL) { return static_cast(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(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(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(item->valuedouble); } if (item->type == cJSON_String && item->valuestring) { return static_cast(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& buffer) { const unsigned char* data = reinterpret_cast(buffer.data()); return Base64Encode(data, buffer.size() * sizeof(float)); } static std::map BuildPqdMetaMap(cJSON* tplRoot) { std::map 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 GetDataArrayIdxList(cJSON* dataSet) { std::vector 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& 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& 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& 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 metaMap = BuildPqdMetaMap(templateRoot); cJSON* dataSet = FindDataSet(templateRoot, dataSetName); if (!dataSet) { return ""; } std::vector idxList = GetDataArrayIdxList(dataSet); std::vector floatBuffer; floatBuffer.reserve(2048); for (size_t i = 0; i < idxList.size(); ++i) { int idx = idxList[i]; std::map::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 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& 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 从 0(1月)开始 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, // 你可以把这里改成对应的设备 id(devid)。未命中则再尝试用规范化后的 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 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, //逻辑子设备ID,0-逻辑设备本身,无填-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 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, //逻辑子设备ID,0-逻辑设备本身,无填-1(原:avg_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天+ */