From d81bcd9983d71897081a7e21ba44921a2195e7e5 Mon Sep 17 00:00:00 2001 From: lixiaoyuan Date: Thu, 4 Sep 2025 19:31:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0MQTT=E5=8A=9F=E8=83=BD,=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9HTTP=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/Release/assets/config/app.json | 11 +- doc/光储充站控系统-HTTP管理接口.docx | Bin 420804 -> 421922 bytes src/CMakeLists.txt | 2 +- src/app/AppData.cpp | 47 +- src/app/AppData.h | 5 + src/app/Application.cpp | 63 +- src/app/Config.cpp | 17 +- src/app/Config.h | 6 + src/app/Constant.cpp | 6 + src/app/{Constants.h => Constant.h} | 0 src/app/DataStruct.cpp | 62 ++ src/app/DataStruct.h | 981 +++++++++++++++++++++++++++ src/app/Device.h | 4 + src/app/Station.cpp | 27 +- src/app/Station.h | 95 +++ src/app/errcode.cpp | 1 + src/app/errcode.h | 1 + src/common/Fields.cpp | 1 - src/common/JsonN.h | 19 +- src/common/Snowflake.h | 52 +- src/common/Utils.cpp | 130 +++- src/common/Utils.h | 10 +- src/database/Dao.cpp | 12 +- src/database/Dao.h | 2 + src/database/DataModelDef.h | 16 +- src/main.cpp | 200 +++--- src/protocol/HttpEntity.cpp | 457 ++++++++++--- src/protocol/HttpEntity.h | 6 + src/protocol/MqttEntity.cpp | 171 +++-- src/protocol/MqttEntity.h | 322 +-------- 30 files changed, 2029 insertions(+), 697 deletions(-) create mode 100644 src/app/Constant.cpp rename src/app/{Constants.h => Constant.h} (100%) create mode 100644 src/app/DataStruct.cpp create mode 100644 src/app/DataStruct.h diff --git a/bin/Release/assets/config/app.json b/bin/Release/assets/config/app.json index 20ac721..9bb99f7 100644 --- a/bin/Release/assets/config/app.json +++ b/bin/Release/assets/config/app.json @@ -1,11 +1,6 @@ { - "database": { - "host": "localhost", - "port": 3306, - "user": "root", - "passwd": "123456", - "dbname": "ees" - }, + "database": {"host": "localhost", "port": 3306, "user": "root", "passwd": "123456", "dbname": "ees"}, "token":"", - "http": { "port": 19800} + "http": {"port": 19800}, + "mqtt": {"host":"tcp://localhost:1883","username":"","password":""} } \ No newline at end of file diff --git a/doc/光储充站控系统-HTTP管理接口.docx b/doc/光储充站控系统-HTTP管理接口.docx index 890561e711ce5f3d07e62797162144521d08f4bf..964fbfe29b4760e186ca25556564eabe820251ba 100644 GIT binary patch delta 74627 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%0DwB%B#f}gK+RY8&;WFjvh)>CFlF(S{b)0 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
5W?5h!nIPHB>Vy93XlDWq( zgq?pNM0WpE5=`Y*8VKFhEClk#$0s<5sX~<8t_^rq-fsaWAJ_iD)3Y-UQQ}orY8sV3 z2w9QD5E`#o15vzp8(;-!#PaAQi074yAc=o{DeMrlw;-*Y!Ohot zc~L&v(Kzl^gH&$e+lblC-!$6B_R0%!hTHKV^rY`Y3il{M=&4(W4DEf~P-I7r)>5wV zi=CGyBm_lQ!EjJ)ml)`2azbL@kvWjgJxUvsQ6ydhfLtTRq^(Rb(UUxdWF$jkJQ&1F&C6q$vHO^utDz+tDbe;>fDHTQYrR)^DH)VQ?GDtRtS6YBnl}vWd zj}Z8Wymvr&Bnu?6^BgXO*HYW;lv;mXkj&1w5#l}1<;bQoUTF@pm06!dWV_A}Ns3pU zLt$SqKBVzl0+7L@Y#>T?y3Mh%?~)#(f6ymM2o))iE_JAZ_v1Uu4C!`uaSSp(rihV z?<)X_isg;xD-oHJCeOWtC_5i*$%*ex0$J=El_HLK?hOxy@Sxx22ZTFfDV=Vd_J;C&#UYvZz0edHBuAWD8gh_W_TKX{#ktsyAZ28bdD6}n1! zpVSo}ux6#YNzU-+Rh1x>*A{<)G^0JI@qJSuN^IRWW0k%J;XAiLoHFO6Vof1dsek~X z=T{w4`OYmN$_#GMqe`poQY`%Df--W=RbJx^GL*HXxv90l2VOe~LeJ(F5(=+02Wh+} z3*L`Wvk~n==t|SFmmtc%4=cyWC?l7Y6F+RrL5b>uC|-YPgYMTW7gw(gdq54z91IuK;DFHHyuhMmK72tCc`kf_Kth$4v{))SZO9;Onf z=&>j(MvqnTw|m@5uAqM>ujqrGlv-We^H4O>uwK!l!$!rc9=0i7+i>*krbC93FBx#_ zd8>yvN}POntLTH_osxYSUW-Ny*vec+M@+ol1*B{JmgpCV4k^602&D0<7Lcvz>yeET zVH#Sk2gp>`g^}rrlo!J5{p~bLT@A?QRn~S|&99%Ut8$&`DQ|}?C4R7sSE6hS;-(O5 zm0a7hL5XNCtL%Ru2T|&K;Swapc3Do@wPdksE0L__>GwVkY0B!n{8Fk-R-k8QAL5mG z;)+R$k3xqj^Jk^4#79@wypNTYGp|5~60_STDN)01y5Y~+Qahp%#kSl|t3%n&Dv|B& z=$WO5WW~1JPT5;7-+JQJO(0vTtJ&TvHu?5dSUG78f88tkN!jLU&Vv$<4R*$1oGKi8t6~fmN`M3*TOF$N{0&zAazQYnE z^EeWSvVXxz3W}~wAeF}tKnAZ9a?Uhv34ttLg$RGLd4AhDSNN`3kj+nVaZqB4gg75~ zEj36~@0NJQjZ`%9{TR*#%QHhDiLYi3V$EYhPPRbtF?GrFxH8CAq{`*S?H!Q8eOi#M zXe1E6Tg7=}mDvc3kFT^K^eZJotl{1Vh;kC4^Um{nWm)BT1rbQ$^+-avjRc~caRZ{w zIUj!}KW71gXp}X}N5{QGA>1zkiHg4VkSgVUk~T|xiadJ>(s?G*`Ako5KO`wDx*P|| z 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�NPa7m9f{*-KP}sqlRV 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 CAz}pp delta 73546 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�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 iI7bvni{ktV?gI*BL~#ZFZ_RinitFromDB(); + + auto& optionMqtt = Config::option.mqtt; + if (!optionMqtt.host.empty()) + { + for (auto& item : mapStation) + { + auto& station = item.second; + // "tcp://localhost:1883" + station->mqttCli->init(optionMqtt.host, station->code, optionMqtt.username, optionMqtt.password); + } + } } std::shared_ptr AppData::getStation(int stationId) @@ -245,6 +256,17 @@ std::shared_ptr AppData::getStationByName(std::string name) return nullptr; } +std::shared_ptr AppData::getStationByCode(std::string code) +{ + for (auto iter = mapStation.begin(); iter!=mapStation.end(); ++iter) + { + if (iter->second->code == code) + { + return iter->second; + } + } + return nullptr; +} std::shared_ptr AppData::getDevice(int stationId, int deviceId) { @@ -423,4 +445,27 @@ std::string AppData::getElectPreiodVal(int month, int hour) } } return ""; +} + +void AppData::storeRuntimeDB() +{ + auto t = Utils::date(); + + std::string valStr; + for (auto iter=mapDataDay.begin(); iter!=mapDataDay.end(); ++iter) + { + auto& v = iter->second; + if (v != 0.0) + { + if (!valStr.empty()) valStr += ","; + valStr += ("[" + std::to_string(iter->first) + "," + Utils::toStr(v, 2) + "]"); + } + } + valStr = "[" + valStr + "]"; + Fields fields; + fields.set("dt", Utils::dateStr(t)); + fields.set("device_id", 1); + fields.set("datatype", 1); + fields.set("value", valStr); + DAO::insertRuntimeData(NULL, fields); } \ No newline at end of file diff --git a/src/app/AppData.h b/src/app/AppData.h index cbfdf8f..ece7d57 100644 --- a/src/app/AppData.h +++ b/src/app/AppData.h @@ -70,6 +70,7 @@ public: int getStationCount(); std::shared_ptr getStationByName(std::string name); + std::shared_ptr getStationByCode(std::string code); std::shared_ptr getDevice(int stationId, int deviceId); @@ -101,6 +102,8 @@ public: std::string getElectPreiodVal(int month, int hour); + void storeRuntimeDB(); + public: /////////////////////////////////////////////////////////////////////////////////////////////// @@ -156,4 +159,6 @@ public: // 电力峰谷分段 (12个月,每个月按小时分成24个时段) std::vector> vecElectPeriods; + std::map mapDataDay; + }; diff --git a/src/app/Application.cpp b/src/app/Application.cpp index b233fd5..87358b2 100644 --- a/src/app/Application.cpp +++ b/src/app/Application.cpp @@ -39,9 +39,6 @@ void Application::init() // 创建HTTP服务线程 std::thread([=]() { while (!isQuit) { - MqttClient mqttCli; - mqttCli.init("tcp://localhost:1883", "AAAAAAAAA", "", "", {"topic/test"}); // 不阻塞 - HttpEntity http; http.listen("0.0.0.0", Config::option.http.port); // 阻塞 } @@ -63,37 +60,41 @@ void Application::runThreadDevice() void Application::runThreadMain() { std::string addr = "tcp://localhost:1883"; - mqttCli = std::make_shared(); - mqttCli->init(addr, "ESS", "", "", {}); + //mqttCli = std::make_shared(); + //mqttCli->init(addr, "ESS", "", "", {}); while (!isQuit) { - // 连接场站 - static TimeTick ttStation; - if (ttStation.elapse(10000)) - { - if (!mqttCli->isConnected) - { - } - else - { - for (auto& item: appdata.mapStation) - { - auto station = item.second; - if (station && !station->isConnected) - { - std::vector vecTopics = {"topic/test" + std::to_string(station->id)}; - mqttCli->subscribe(vecTopics, [=](int id) - { - station->isConnected = (id == 0); - }); - } - break; - } - } - - } - + //// 连接场站 + //static TimeTick ttStation; + //if (ttStation.elapse(10000)) + //{ + // if (!mqttCli->isConnected) + // { + // } + // else + // { + // for (auto& item: appdata.mapStation) + // { + // auto station = item.second; + // if (station && !station->isConnected) + // { + // std::string stationCode = station->code; + // std::vector vecTopics = { + // "up/json" + stationCode + "/EMS_YX", + // "up/json" + stationCode + "/EMS_YC", + // "up/json" + stationCode + "/PCU_YX", + // "up/json" + stationCode + "/PCU_YC", + // }; + // mqttCli->subscribe(vecTopics, [=](int id) + // { + // station->isConnected = (id == 0); + // }); + // } + // break; + // } + // } + //} std::this_thread::sleep_for(std::chrono::milliseconds(10)); } diff --git a/src/app/Config.cpp b/src/app/Config.cpp index 6dfd9a6..da13feb 100644 --- a/src/app/Config.cpp +++ b/src/app/Config.cpp @@ -32,7 +32,7 @@ bool Config::init(std::string filename) } else { - spdlog::info("[config] parse database failed: not found. host={}", option.database.host); + spdlog::info("[config] parse database failed: not found."); } if (jsonroot.contains("http")) @@ -43,6 +43,21 @@ bool Config::init(std::string filename) option.http.useToken = !token.empty(); NJson::read(json, "port", option.http.port); } + else + { + spdlog::info("[config] parse http failed: not found."); + } + if (jsonroot.contains("mqtt")) + { + NJsonNode json = jsonroot.at("mqtt"); + NJson::read(json, "host", option.mqtt.host); + NJson::read(json, "username", option.mqtt.username); + NJson::read(json, "password", option.mqtt.password); + } + else + { + spdlog::info("[config] parse mqtt failed: not found."); + } return true; } \ No newline at end of file diff --git a/src/app/Config.h b/src/app/Config.h index 558cf35..6da7a81 100644 --- a/src/app/Config.h +++ b/src/app/Config.h @@ -20,6 +20,12 @@ struct AppOption int port {0}; } http; + struct { + std::string host; + std::string username; + std::string password; + } mqtt; + }; class Config diff --git a/src/app/Constant.cpp b/src/app/Constant.cpp new file mode 100644 index 0000000..3b134c3 --- /dev/null +++ b/src/app/Constant.cpp @@ -0,0 +1,6 @@ +#include "Constant.h" + +namespace CONST +{ + const std::string VAR; +} diff --git a/src/app/Constants.h b/src/app/Constant.h similarity index 100% rename from src/app/Constants.h rename to src/app/Constant.h diff --git a/src/app/DataStruct.cpp b/src/app/DataStruct.cpp new file mode 100644 index 0000000..e9f0c79 --- /dev/null +++ b/src/app/DataStruct.cpp @@ -0,0 +1,62 @@ +#include "DataStruct.h" + +void EMSYX::fromJson(const std::string& str) +{ + NJsonNode jsonroot; + auto ret = NJson::parse(str, jsonroot); + if (!ret) { return; } + NJson::read(jsonroot, "mcu", mcu); + NJson::read(jsonroot, "pcs", pcs); + NJson::read(jsonroot, "electMeterMainPoint", electMeterMainPoint); + NJson::read(jsonroot, "electMeter", electMeter); + NJson::read(jsonroot, "fireSystem", fireSystem); + NJson::read(jsonroot, "ups", ups); + NJson::read(jsonroot, "temHumMainPoint", temHumMainPoint); + NJson::read(jsonroot, "temHum", temHum); + NJson::read(jsonroot, "aircMainPoint", aircMainPoint); + NJson::read(jsonroot, "airc", airc); + NJson::read(jsonroot, "controlDryContact", controlDryContact); + NJson::read(jsonroot, "statusDryContact", statusDryContact); + NJson::read(jsonroot, "bcuMain", bcuMain); + NJson::read(jsonroot, "pcuMain", pcuMain); + NJson::read(jsonroot, "electMeterMain", electMeterMain); + NJson::read(jsonroot, "fireSystemMain", fireSystemMain); + NJson::read(jsonroot, "upsMain", upsMain); + NJson::read(jsonroot, "temHumMain", temHumMain); + NJson::read(jsonroot, "aircMain", aircMain); + NJson::read(jsonroot, "emu", emu); + NJson::read(jsonroot, "chillerMain", chillerMain); + NJson::read(jsonroot, "chillerMainPoint", chillerMainPoint); + NJson::read(jsonroot, "chiller", chiller); +} + +std::string EMSYX::toJson() +{ + NJsonNode jsonroot; + jsonroot["bms"] = bms; + jsonroot["bcu"] = bcu; + jsonroot["mcu"] = mcu; + jsonroot["pcs"] = pcs; + jsonroot["electMeterMainPoint"] = electMeterMainPoint; + jsonroot["electMeter"] = electMeter; + jsonroot["fireSystem"] = fireSystem; + jsonroot["ups"] = ups; + jsonroot["temHumMainPoint"] = temHumMainPoint; + jsonroot["temHum"] = temHum; + jsonroot["aircMainPoint"] = aircMainPoint; + jsonroot["airc"] = airc; + jsonroot["controlDryContact"] = controlDryContact; + jsonroot["statusDryContact"] = statusDryContact; + jsonroot["bcuMain"] = bcuMain; + jsonroot["pcuMain"] = pcuMain; + jsonroot["electMeterMain"] = electMeterMain; + jsonroot["fireSystemMain"] = fireSystemMain; + jsonroot["upsMain"] = upsMain; + jsonroot["temHumMain"] = temHumMain; + jsonroot["aircMain"] = aircMain; + jsonroot["emu"] = emu; + jsonroot["chillerMain"] = chillerMain; + jsonroot["chillerMainPoint"] = chillerMainPoint; + jsonroot["chiller"] = chiller; + return jsonroot.dump(); +} \ No newline at end of file diff --git a/src/app/DataStruct.h b/src/app/DataStruct.h new file mode 100644 index 0000000..1da35a6 --- /dev/null +++ b/src/app/DataStruct.h @@ -0,0 +1,981 @@ +#pragma + +#include +#include "common/JsonN.h" + +struct REGInfo +{ + std::string name; + int byte; + std::string remark; +}; + +// EMS遥信 +struct EMSYX +{ + uint16_t bms; //BMS(电池堆)通信状态 R uint16 "0:正常 1:故障" bit位从低到高分别对应1~16 + uint64_t bcu; //BCU(电池簇)通信状态 R uint64 "0:正常 1:故障" bit位从低到高分别对应1~64 + uint16_t mcu; //PCU(主控)通信状态 R uint16 "0:正常 1:故障" bit位从低到高分别对应1~16 + uint64_t pcs; //PCS(模块)通信状态 R uint64 "0:正常 1:故障" bit位从低到高分别对应1~64 + uint16_t electMeterMainPoint; //电表总接点通信状态 R uint16 "0:正常 1:故障" bit位从低到高分别对应1~16 + uint32_t electMeter; //电表通信状态 R uint32 "0:正常 1:故障" bit位从低到高分别对应1~32 + uint16_t fireSystem; //消防通信状态 R uint16 "0:正常 1:故障" bit位从低到高分别对应1~16 + uint16_t ups; //UPS通信状态 R uint16 "0:正常 1:故障" bit位从低到高分别对应1~16 + uint16_t temHumMainPoint; //温湿度总接点通信状态 R uint16 "0:正常 1:故障" bit位从低到高分别对应1~16 + uint32_t temHum; //温湿度通信状态 R uint32 "0:正常 1:故障" bit位从低到高分别对应1~32 + uint16_t aircMainPoint; //空调总接点通信状态 R uint16 "0:正常 1:故障" bit位从低到高分别对应1~16 + uint32_t airc; //空调通信状态 R uint32 "0:正常 1:故障" bit位从低到高分别对应1~32 + uint16_t controlDryContact; //控制干接点(配电系统)状态 R uint16 "0:开路 1:闭合" bit位从低到高分别对应1~16 + uint16_t statusDryContact; //状态干接点(配电系统)状态 R uint16 "0:开路 1:闭合" bit位从低到高分别对应1~16 + uint16_t bcuMain; //BCU总通信状态 R uint16 0:正常 1:告警 2:故障 + uint16_t pcuMain; //PCU总通信状态 R uint16 0:正常 1:告警 2:故障 + uint16_t electMeterMain; //电表总通信状态 R uint16 0:正常 1:告警 2:故障 + uint16_t fireSystemMain; //消防总通信状态 R uint16 0:正常 1:告警 2:故障 + uint16_t upsMain; //UPS总通信状态 R uint16 0:正常 1:告警 2:故障 + uint16_t temHumMain; //温湿度总通信状态 R uint16 0:正常 1:告警 2:故障 + uint16_t aircMain; //空调总通信状态 R uint16 0:正常 1:告警 2:故障 + uint16_t emu; //EMU通信状态 R uint16 0:正常 1:告警 2:故障 + uint16_t chillerMain; //冷机总通信状态 R uint16 0:正常 1:告警 2:故障 + uint16_t chillerMainPoint; //冷机总接点通信状态 R uint16 "0:正常 1:故障" bit位从低到高分别对应1~16 + uint16_t chiller; //冷机通信状态 R uint16 "0:正常 1:故障" bit位从低到高分别对应1~16 + //预留 + //预留 + uint16_t energyStatus; //判断总表的三相总有功 < -5.0fkW 充电 >5.0fkW放电 否则停机 R uint16 0:充电 1 : 放电 2 : 停机 储能状态 + + void fromJson(const std::string& str); + void toJson(); +}; + +// EMS遥测 +struct EMSYC +{ + //BMS(电池堆)个数 R uint16 1 0x1001 + //BCU通道个数 R uint16 1~2 0x1002 + //BCU(电池簇)个数 R uint16 1~40 0x1003 + //PCU(主控)个数 R uint16 1~4 0x1004 + //PCU通道个数 R uint16 1~4 0x1005 + //PCS(模块)个数 R uint16 1~40 0x1006 + //电表通道个数 R uint16 1 0x1007 + //电表个数 R uint16 1~10 0x1008 + //消防通道个数 R uint16 1~10 0x1009 + //消防个数 R uint16 1~10 0x100A + //UPS通道个数 R uint16 1~10 0x100B + //UPS个数 R uint16 1~10 0x100C + //温湿度通道个数 R uint16 1 0x100D + //温湿度个数 R uint16 1~10 0x100E + //空调通道个数 R uint16 1 0x100F + //空调个数 R uint16 1~10 0x1010 + //控制干接点(配电系统)个数 R uint16 16 0x1011 + //状态干接点(配电系统)个数 R uint16 16 0x1012 + //预留 0x1013~0x1071 + //预留 + //预留 + //预留 + //预留 + //预留 + //预留 + //预留 + //预留 + //系统最大可充电功率 R uint32 1KW Pcu数据 0x1072 储能侧 + //系统最大可放电功率 R uint32 1KW 0x1074 + //储能母线电压 R uint32 0.1V 堆数据 0x1076 + //储能母线电流 R int32 0.1A 0x1078 + //储能系统SOC R uint16 0.1 0x107A + //储能系统SOH R uint16 0.1 0x107B + //电流变比 R uint16 电表总表数据 0x107C + //电压变比 R uint16 0x107D + //A相电压 R uint32 1V 0x107E + //B相电压 R uint32 1V 0x1080 + //C相电压 R uint32 1V 0x1082 + //A相电流 R int32 1A 0x1084 + //B相电流 R int32 1A 0x1086 + //C相电流 R int32 1A 0x1088 + //AB相电压 R uint32 1V 0x108A + //BC相电压 R uint32 1V 0x108C + //CA相电压 R uint32 1V 0x108E + //A相有功功率 R int32 1kW 0x1090 + //B相有功功率 R int32 1kW 0x1092 + //C相有功功率 R int32 1kW 0x1094 + //三相总有功功率 R int32 1kW 0x1096 + //当前控制功率 R int32 0.1kW 堆数据 0x1098 + //负荷率 R uint32 1% 台区控制策略数据 0不拿 大于0 默认拿第一个 0x109A + //三相不平衡度  R uint32 1% 0x109C + //功率因素比率  R uint32 1% 0x109E + //进线开关柜功率 R int32 1kW 并网口电表 0x10A0 + //用户关口表功率 R int32 1kW 并网口电表 0x10A2 + //预留 0x10A4~0x1103 + //预留 + //正向总有功总需量 R int32 1kW 0x1104 收益 总表 + //尖段电价 R uint32 1RMB 0x1106 + //峰段电价 R uint32 1RMB 0x1108 + //平段电价 R uint32 1RMB 0x110A + //谷段电价 R uint32 1RMB 0x110C + //日充电电量 R uint32 1kWh 0x110E + //日放电电量 R uint32 1kWh 0x1110 + //日充电费用 R uint32 1RMB 0x1112 + //日放电费用 R uint32 1RMB 0x1114 + //日收益 R int32 1RMB 0x1116 + //日正向尖有功电能 R uint32 1kWh 0x1118 + //日正向峰有功电能 R uint32 1kWh 0x111A + //日正向平有功电能 R uint32 1kWh 0x111C + //日正向谷有功电能 R uint32 1kWh 0x111E + //日正向总有功电能 R uint32 1kWh 0x1120 + //日反向尖有功电能 R uint32 1kWh 0x1122 + //日反向峰有功电能 R uint32 1kWh 0x1124 + //日反向平有功电能 R uint32 1kWh 0x1126 + //日反向谷有功电能 R uint32 1kWh 0x1128 + //日反向总有功电能 R uint32 1kWh 0x112A + //总充电电量 R uint32 1kWh 0x112C + //总放电电量 R uint32 1kWh 0x112E + //总充电费用 R uint32 1RMB 0x1130 + //总放电费用 R uint32 1RMB 0x1132 + //总收益 R int32 1RMB 0x1134 + //总正向尖有功电能 R uint32 1kWh 0x1136 + //总正向峰有功电能 R uint32 1kWh 0x1138 + //总正向平有功电能 R uint32 1kWh 0x113A + //总正向谷有功电能 R uint32 1kWh 0x113C + //总正向总有功电能 R uint32 1kWh 0x113E + //总反向尖有功电能 R uint32 1kWh 0x1140 + //总反向峰有功电能 R uint32 1kWh 0x1142 + //总反向平有功电能 R uint32 1kWh 0x1144 + //总反向谷有功电能 R uint32 1kWh 0x1146 + //总反向总有功电能 R uint32 1kWh 0x1148 + //预留 0x114A~0x11AD + //预留 + //预留 + //交流A相电压 R int16 1V 0x11AE 电网侧 并网口电表 + //交流B相电压 R int16 1V 0x11AF + //交流C相电压 R int16 1V 0x11B0 + //交流A相频率 R int16 1Hz 0x11B1 Pcs + //交流B相频率 R int16 1Hz 0x11B2 + //交流C相频率 R int16 1Hz 0x11B3 + //总直流功率 R int32 1kW 0x11B4 堆 + //总直流电压 R uint32 0.1V 0x11B6 + //总直流电流 R int32 0.1A 0x11B8 + //预留 0x11B9~0x121A + //预留 + //预留 + //储能系统温度 R int16 0.1℃ 堆里面单体温度最高 0x121B 充放电运行状态 + //储能充放电时段hh R uint16 时 0x01 0x121C 普通控制功率取 默认给0 + //储能充放电时段mm R uint16 分 0x01 0x121D + //储能充放电时段ss R uint16 秒 0x01 0x121E + //储能系统各时段功率 R int16 1kW 0x01 0x121F +}; + +// EMS遥调 +struct EMSYT +{ + //EMS工作模式 RW uint16 - "0:无效 1:本地控制模式, EMS不接受平台调控 2 : 平台控制模式, EMS接收平台调控指令" 0x0001 + //有功功率 RW int16 kW "并网恒功率模式下交流侧功率值: <0 : 充电功率 0 : 静置 >0 : 放电功率" 0x0002 + //PCS开关机 RW uint16 - "0: 关机 1 : 开机" 软件开关机 0x0003 + //主控对象 RW uint16 "储能遥控对象:0:无效 1:华云 2:轻舟" 0x0004 + //A相有功功率 RW int16 1kW 0x0005 + //B相有功功率 RW int16 1kW 0x0006 + //C相有功功率 RW int16 1kW 0x0007 + //三相总有功功率 RW int16 1kW 0x0008 + //A相无功功率 RW int16 1kVar 0x0009 + //B相无功功率 RW int16 1kVar 0x000A + //C相无功功率 RW int16 1kVar 0x000B + //三相总无功功率 RW int16 1kVar 0x000C + //A相电流 RW int16 0.01A 在使用的时候除以100转float 0x000D + //B相电流 RW int16 0.01A 0x000E + //C相电流 RW int16 0.01A 0x000F + //A相电压 RW uint16 1V 0x0010 + //B相电压 RW uint16 1V 0x0011 + //C相电压 RW uint16 1V 0x0012 + //A相交流功率因数 RW int16 0.01 在使用的时候除以100转float 0x0013 + //B相交流功率因数 RW int16 0.01 0x0014 + //C相交流功率因数 RW int16 0.01 0x0015 + //A相视在功率 RW int16 1kVA 0x0016 + //B相视在功率 RW int16 1kVA 0x0017 + //C相视在功率 RW int16 1kVA 0x0018 + //正向总有功总需量 RW int16 1kW 0x0019 + //数据有效性 RW int16 0无效 1有效 0x001A + //EMS工作模式 RW uint16 - "0:无效 1:本地控制模式, EMS不接受平台调控 2 : 平台控制模式, EMS接收平台调控指令" 2413项目需求 0x001B + //有功功率 RW uint16 kW "并网恒功率模式下交流侧功率值:功率绝对值 没有正负" 2413项目需求 0x001C + //充放电类型 RW uint16 - "0: 关机 1 : 充电 2 : 放电 3 : 待机" "2413项目需求充放电类型" 0x001D + //最大SOC RW uint16 2413项目需求 0x001E + //最小SOC RW uint16 2413项目需求 0x001F + //自动并离网 RW uint16 "0:手动 1:自动" "2332项目需求(若为自动,则通过主控进行并离网切换)" 0x0020 + //并网离网 RW uint16 "0:并网 1:离网" "2332项目需求(该下发参数的前提为手动模式下)" 0x0021 +}; + + +// PCU遥信 +struct PCUYX +{ + //所属通道号 R uint16 1~4 0x1001 + //故障状态 R uint16 1故障,0正常 0x1002 + //告警状态 R uint16 1告警,0正常 0x1003 + //设备在线 R uint16 1在线,0无效 0x1004 + //本地远程 R uint16 1本地,0远程 0x1005 + //启停状态 R uint16 1开机,0关机 0x1006 + //电网状态 R uint16 1离网,0并网 0x1007 + //模块状态 R uint16 1开机,0待机 0x1008 + //EPO急停 R uint16 1故障,0正常 0x1009 + //防雷器异常 R uint16 1告警,0正常 0x100A + //负载电压反序 R uint16 1故障,0正常 0x100B + //市电电压反序 R uint16 1故障,0正常 0x100C + //输出相反序 R uint16 1故障,0正常 0x100D + //过载告警 R uint16 1告警,0正常 0x100E + //过载超时 R uint16 1故障,0正常 0x100F + //交流过流保护 R uint16 1故障,0正常 0x1010 + //逆变电压异常 R uint16 1故障,0正常 0x1011 + //内部串口异常 R uint16 1故障,0正常 0x1012 + //485通信故障 R uint16 1故障,0正常 0x1013 + //CAN通信故障 R uint16 1故障,0正常 0x1014 + //E2PROM故障 R uint16 1故障,0正常 0x1015 + //电网过压 R uint16 1故障,0正常 0x1016 + //电网欠压 R uint16 1故障,0正常 0x1017 + //电网过频 R uint16 1故障,0正常 0x1018 + //电网欠频 R uint16 1故障,0正常 0x1019 + //电网快检综合异常 R uint16 1故障,0正常 0x101A + //电网幅值快检异常 R uint16 1故障,0正常 0x101B + //电网拖尾异常 R uint16 1故障,0正常 0x101C + //消防输入信号NO R uint16 1闭合,0断开 0x101D + //急停按钮信号NC R uint16 1急停,0正常 0x101E + //避雷器NC R uint16 1故障,0正常 0x101F + //避雷器断路器NC R uint16 1故障,0正常 0x1020 + //PCS总断路器NC R uint16 1闭合,0断开 0x1021 + //电操状态NO R uint16 1闭合,0断开 0x1022 + //远程关机NO R uint16 1开机,0无效 0x1023 + //远程开机NO R uint16 1开机,0无效 0x1024 + //BA故障信号NO R uint16 1故障,0正常 0x1025 + //PCS_01状态 R uint16 1在线,0掉线 0x1026 + //PCS_02状态 R uint16 1在线,0掉线 0x1027 + //PCS_03状态 R uint16 1在线,0掉线 0x1028 + //PCS_04状态 R uint16 1在线,0掉线 0x1029 + //PCS_05状态 R uint16 1在线,0掉线 0x102A + //PCS_06状态 R uint16 1在线,0掉线 0x102B + //PCS_07状态 R uint16 1在线,0掉线 0x102C + //PCS_09状态 R uint16 1在线,0掉线 0x102D + //PCS_10状态 R uint16 1在线,0掉线 0x102E + //PCS_01下发设置 R uint16 1故障,0正常 0x102F + //PCS_02下发设置 R uint16 1故障,0正常 0x1030 + //PCS_03下发设置 R uint16 1故障,0正常 0x1031 + //PCS_04下发设置 R uint16 1故障,0正常 0x1032 + //PCS_05下发设置 R uint16 1故障,0正常 0x1033 + //PCS_06下发设置 R uint16 1故障,0正常 0x1034 + //PCS_07下发设置 R uint16 1故障,0正常 0x1035 + //PCS_08下发设置 R uint16 1故障,0正常 0x1036 + //PCS_09下发设置 R uint16 1故障,0正常 0x1037 + //PCS_10下发设置 R uint16 1:故障,0正常 0x1038 + //内部DSP通信故障 R uint16 1 : 故障,0正常 0x1039 + //BMS CAN通信故障 R uint16 1 : 故障,0正常 0x103A + //下发设置失败 R uint16 1 : 故障,0正常 0x103B +}; + +// PCU 遥测 +struct PCUYC +{ + //所属通道号 R uint16 1~4 0x0001 + //充电功率最大许可 R uint32 1KW 0x0002 + //放电功率最大许可 R uint32 1KW 0x0004 + //交流日总充电量 R uint32 1KWh 0x0006 + //交流日总放电量 R uint32 1KWh 0x0008 + //交流总充电量 R uint32 1KWh 0x000A + //交流总放电量 R uint32 1KWh 0x000C + //有功功率期望值 R int16 1KW 0x000E + //无功功率期望值 R int16 1kVar 0x000F + //PCS侧线电压VAB R int16 1v 0x0010 + //PCS侧线电压VBC R int16 1v 0x0011 + //PCS侧线电压VCA R int16 1v 0x0012 + //PCS侧线A相电压 R int16 1v 0x0013 + //PCS侧线B相电压 R int16 1v 0x0014 + //PCS侧线C相电压 R int16 1v 0x0015 + //PCS侧A相频率 R int16 1Hz 0x0016 + //PCS侧B相频率 R int16 1Hz 0x0017 + //PCS侧C相频率 R int16 1Hz 0x0018 + //PCS侧功率因数A R int16 1 0x0019 + //PCS侧功率因数B R int16 1 0x001A + //PCS侧功率因数C R int16 1 0x001B + //PCS侧相电流A R int16 1A 0x001C + //PCS侧相电流B R int16 1A 0x001D + //PCS侧相电流C R int16 1A 0x001E + //PCS侧有功功率A R int16 1kW 0x001F + //PCS侧有功功率B R int16 1kW 0x0020 + //PCS侧有功功率C R int16 1kW 0x0021 + //PCS侧无功功率A R int16 1kVar 0x0022 + //PCS侧无功功率B R int16 1kVar 0x0023 + //PCS侧无功功率C R int16 1kVar 0x0024 + //PCS侧视在功率A R int16 1kVar 0x0025 + //PCS侧视在功率B R int16 1kVar 0x0026 + //PCS侧视在功率C R int16 1kVar 0x0027 + //PCS侧三相总有功功率 R int16 1kW 0x0028 + //PCS侧三相总无功功率 R int16 1kVar 0x0029 + //PCS侧三相总视在功率 R int16 1kVA 0x002A + //PCS侧三相总功率因数 R int16 1 0x002B + //PCU模块温度 R int16 1℃ 0x002C + //外部温度NTC1 R int16 1℃ 0x002D + //外部温度NTC2 R int16 1℃ 0x002E + //外部温度NTC3 R int16 1℃ 0x002F + //台区负载侧A相电流 R int16 1A 0x0030 + //台区负载侧B相电流 R int16 1A 0x0031 + //台区负载侧C相电流 R int16 1A 0x0032 + //台区负载侧A相有功功率 R int16 1kW 0x0033 + //台区负载侧B相有功功率 R int16 1kW 0x0034 + //台区负载侧C相有功功率 R int16 1kW 0x0035 + //台区负载侧总有功功率 R int16 1kW 0x0036 + //台区负载侧A相无功功率 R int16 1kVar 0x0037 + //台区负载侧B相无功功率 R int16 1kVar 0x0038 + //台区负载侧C相无功功率 R int16 1kVar 0x0039 + //台区负载侧总无功功率 R int16 1kVar 0x003A + //台区负载侧A相视在功率 R int16 1kVA 0x003B + //台区负载侧B相视在功率 R int16 1kVA 0x003C + //台区负载侧C相视在功率 R int16 1kVA 0x003D + //台区负载侧总视在功率 R int16 1kVA 0x003E + //台区负载侧A相功率因数 R int16 1 0x003F + //台区负载侧B相功率因数 R int16 1 0x0040 + //台区负载侧C相功率因数 R int16 1 0x0041 + //台区负载侧总功率因数 R int16 1 0x0042 + //负载侧线电压AB R int16 1V 0x0043 + //负载侧线电压BC R int16 1V 0x0044 + //负载侧线电压CA R int16 1V 0x0045 + //负载侧相电压AN R int16 1V 0x0046 + //负载侧相电压BN R int16 1V 0x0047 + //负载侧相电压CN R int16 1V 0x0048 + //负载侧A功率因素 R int16 1 0x0049 + //负载侧B功率因素 R int16 1 0x004A + //负载侧C功率因素 R int16 1 0x004B + //负载侧A视在功率 R int16 1kVA 0x004C + //负载侧B视在功率 R int16 1kVA 0x004D + //负载侧C视在功率 R int16 1kVA 0x004E +}; + +// PCS 遥信 +struct PCSYX +{ + //所属主控号 R uint16 1~4 0x1001 + //所属PCS号 R uint16 1~40 0x1002 + //故障状态 R uint16 1故障,0正常 0x1003 + //告警状态 R uint16 1告警,0正常 0x1004 + //设备在线 R uint16 1在线,0无效 0x1005 + //禁止充电 R uint16 1禁止,0无效 0x1006 + //禁止放电 R uint16 1禁止,0无效 0x1007 + //运行状态 R uint16 1开机,0关机 0x1008 + //充放状态 R uint16 0:待机, 1:充电, 2:放电, 3:搁置 0x1009 + //电网状态 R uint16 1离网,0并网 0x100A + //逆变供电 R uint16 0禁止,1使能 0x100B + //缓启动完成 R uint16 0禁止,1使能 0x100C + //主机标志 R uint16 0禁止,1使能 0x100D + //并离网状态 R uint16 0:并网, 1:离网 0x100E + //同步请求标志 R uint16 0:无效;1:动作 0x100F + //绝缘故障 R uint16 1:故障,0正常 0x1010 + //漏电保护 R uint16 1 : 故障,0正常 0x1011 + //直流过压 R uint16 1 : 故障,0正常 0x1012 + //市电幅值异常 R uint16 1 : 故障,0正常 0x1013 + //市电相序异常 R uint16 1 : 故障,0正常 0x1014 + //温度开关异常 R uint16 1 : 故障,0正常 0x1015 + //市电频率异常 R uint16 1 : 故障,0正常 0x1016 + //IGBT过温 R uint16 1 : 故障,0正常 0x1017 + //交流接地故障 R uint16 1 : 故障,0正常 0x1018 + //逆变过流异常 R uint16 1 : 故障,0正常 0x1019 + //直流缓起故障 R uint16 1 : 故障,0正常 0x101A + //直流主继电器故障 R uint16 1 : 故障,0正常 0x101B + //风机异常 R uint16 1 : 故障,0正常 0x101C + //主接触器异常 R uint16 1 : 故障,0正常 0x101D + //均浮充切换超时 R uint16 1 : 故障,0正常 0x101E + //硬件故障 R uint16 1 : 故障,0正常 0x101F + //机内过温 R uint16 1 : 故障,0正常 0x1020 + //软启动故障 R uint16 1 : 故障,0正常 0x1021 + //触摸屏通讯故障 R uint16 1 : 故障,0正常 0x1022 + //防雷器故障 R uint16 1 : 故障,0正常 0x1023 + //急停故障 R uint16 1 : 故障,0正常 0x1024 + //BMS系统故障 R uint16 1 : 故障,0正常 0x1025 + //BMS通讯故障 R uint16 1 : 故障,0正常 0x1026 + //BMS干接点通讯故障 R uint16 1 : 故障,0正常 0x1027 + //远程通讯故障 R uint16 1 : 故障,0正常 0x1028 + //门禁告警 R uint16 1 : 故障,0正常 0x1029 + //锁相异常 R uint16 1 : 故障,0正常 0x102A + //IGBT过温告警 R uint16 1 : 故障,0正常 0x102B + //硬件过流保护 R uint16 1 : 故障,0正常 0x102C + //驱动故障 R uint16 1 : 故障,0正常 0x102D + //ID冲突 R uint16 1 : 故障,0正常 0x102E + //电池过压 R uint16 1 : 故障,0正常 0x102F + //电池欠压 R uint16 1 : 故障,0正常 0x1030 + //直流过流保护 R uint16 1 : 故障,0正常 0x1031 + //输出电压异常 R uint16 1 : 故障,0正常 0x1032 + //离网输出电压不符合 R uint16 1 : 故障,0正常 0x1033 + //输出过载保护 R uint16 1 : 故障,0正常 0x1034 + //输出短路保护 R uint16 1 : 故障,0正常 0x1035 + //并机通信异常 R uint16 1 : 故障,0正常 0x1036 + //电池保险异常 R uint16 1 : 故障,0正常 0x1037 + //电池重载低压 R uint16 1 : 故障,0正常 0x1038 + //电池低压告警 R uint16 1 : 故障,0正常 0x1039 + //一拖二压差过大 R uint16 1 : 故障,0正常 0x103A + //电池反接故障 R uint16 1 : 故障,0正常 0x103B + //电池电压异常 R uint16 1 : 故障,0正常 0x103C + //过载告警 R uint16 1 : 故障,0正常 0x103D + //外部接触器异常 R uint16 1 : 故障,0正常 0x103E + //IGBT温度传感器异常 R uint16 1 : 故障,0正常 0x103F + //整机温度传感器异常 R uint16 1 : 故障,0正常 0x1040 + //市电CT异常 R uint16 1 : 故障,0正常 0x1041 + //逆变电流三相不平衡 R uint16 1 : 故障,0正常 0x1042 + //逆变电流直流分量异常 R uint16 1 : 故障,0正常 0x1043 + //母线不平衡 R uint16 1 : 故障,0正常 0x1044 + //逆变电压直流分量异常 R uint16 1 : 故障,0正常 0x1045 + //主接触器控制异常 R uint16 1 : 故障,0正常 0x1046 + //逆变电压控制异常 R uint16 1 : 故障,0正常 0x1047 + //直流霍尔异常 R uint16 1 : 故障,0正常 0x1048 + //电池单体过压 R uint16 1 : 故障,0正常 0x1049 + //电池单体欠压 R uint16 1 : 故障,0正常 0x104A + //电网过压 R uint16 1 : 故障,0正常 0x104B + //电网欠压 R uint16 1 : 故障,0正常 0x104C + //电网过频 R uint16 1 : 故障,0正常 0x104D + //电网欠频 R uint16 1 : 故障,0正常 0x104E + //市电不平衡 R uint16 1 : 故障,0正常 0x104F + //参数设置不匹配 R uint16 1 : 故障,0正常 0x1050 + //SPI通信异常 R uint16 1 : 故障,0正常 0x1051 + //SCI通信异常 R uint16 1 : 故障,0正常 0x1052 + //IIC通信异常 R uint16 1 : 故障,0正常 0x1053 + //Xintf通信异常 R uint16 1 : 故障,0正常 0x1054 + //零偏校准异常 R uint16 1 : 故障,0正常 0x1055 + //烟雾告警 R uint16 1 : 故障,0正常 0x1056 + //无电池组故障 R uint16 1异常,0正常 0x1057 + //环温降频 R uint16 1异常,0正常 0x1058 + //交流过载 R uint16 1异常,0正常 0x1059 + //采样异常 R uint16 1异常,0正常 0x105A + //24V辅源故障 R uint16 1异常,0正常 0x105B + //直流欠压异常 R uint16 1异常,0正常 0x105C + //散热器过温 R uint16 1异常,0正常 0x105D + //CAN配置故障 R uint16 1异常,0正常 0x105E + //3.3V辅源故障 R uint16 1异常,0正常 0x105F + //环境过温 R uint16 1异常,0正常 0x1060 + //A相IGBT逆变过流 R uint16 1异常,0正常 0x1061 + //B相IGBT逆变过流 R uint16 1异常,0正常 0x1062 + //C相IGBT逆变过流 R uint16 1异常,0正常 0x1063 +}; + +// PCS 遥测 +struct PCSYC +{ + //所属主控号 R uint16 1~4 0x0001 + //所属PCS号 R uint16 1~40 0x0002 + //总充电量 R uint32 1kWh 0x0003 + //总放电量 R uint32 1kWh 0x0005 + //散热器温度 R int16 1℃ 0x0007 + //内部温度 R int16 1℃ 0x0008 + //最大允许充电功率 R int16 0.1kW 0x0009 + //最大允许放电功率 R int16 0.1kW 0x000A + //有功功率期望 R int16 1kWh 0x000B + //无功功率期望 R int16 1kVar 0x000C + //AB线电压 R int16 1V 0x000D + //BC线电压 R int16 1V 0x000E + //CA线电压 R int16 1V 0x000F + //A相电压 R int16 1V 0x0010 + //B相电压 R int16 1V 0x0011 + //C相电压 R int16 1V 0x0012 + //A相频率 R int16 1Hz 0x0013 + //B相频率 R int16 1Hz 0x0014 + //C相频率 R int16 1Hz 0x0015 + //A相功率因数 R int16 1 0x0016 + //B相功率因数 R int16 1 0x0017 + //C相功率因数 R int16 1 0x0018 + //A相电流 R int16 1A 0x0019 + //B相电流 R int16 1A 0x001A + //C相电流 R int16 1A 0x001B + //A相有功功率 R int16 1kW 0x001C + //B相有功功率 R int16 1kW 0x001D + //C相有功功率 R int16 1kW 0x001E + //A相无功功率 R int16 1kVar 0x001F + //B相无功功率 R int16 1kVar 0x0020 + //C相无功功率 R int16 1kVar 0x0021 + //A相视在功率 R int16 1kVA 0x0022 + //B相视在功率 R int16 1kVA 0x0023 + //C相视在功率 R int16 1kVA 0x0024 + //三相总有功功率 R int16 1kW 0x0025 + //三相总无功功率 R int16 1kVar 0x0026 + //三相总视在功率 R int16 1kVA 0x0027 + //三相总功率因数 R int16 1 0x0028 + //直流功率 R int16 1kW 0x0029 + //直流电压 R int16 1V 0x002A + //直流电流 R int16 1A 0x002B + //充电功率 R int16 1kW 0x002C + //放电功率 R int16 1kW 0x002D + //PF值 R int16 1 0x002E + //UV线/U相电网计量线电压 R int16 1V 0x002F + //VW线/V相电网计量线电压 R int16 1V 0x0030 + //WU线/W相电网计量线电压 R int16 1V 0x0031 + //U相电网计量电流 R int16 1A 0x0032 + //V相电网计量电流 R int16 1A 0x0033 + //W相电网计量电流 R int16 1A 0x0034 + //正母线电压 R int16 1V 0x0035 + //可用功率 R int16 1kVA 0x0036 + //负母线电压 R int16 1V 0x0037 + //A相IGBT温度 R int16 1℃ 0x0038 + //B相IGBT温度 R int16 1℃ 0x0039 + //C相IGBT温度 R int16 1℃ 0x003A + //逆变侧AB线电压 R int16 1V 0x003B + //逆变侧BC线电压 R int16 1V 0x003C + //逆变侧CA线电压 R int16 1V 0x003D + //逆变侧A相电压 R int16 1V 0x003E + //逆变侧B相电压 R int16 1V 0x003F + //逆变侧C相电压 R int16 1V 0x0040 + //逆变侧A相电流 R int16 1A 0x0041 + //逆变侧B相电流 R int16 1A 0x0042 + //逆变侧C相电流 R int16 1A 0x0043 + //逆变侧A相电流直流分量 R int16 1A 0x0044 + //逆变侧B相电流直流分量 R int16 1A 0x0045 + //逆变侧C相电流直流分量 R int16 1A 0x0046 + //离网频率 R int16 1Hz 0x0047 + //A相负载量 R int16 1 0x0048 + //B相负载量 R int16 1 0x0049 + //C相负载量 R int16 1 0x004A + //总负载量 R int16 1 0x004B + //逆变侧AB线电压直流分量 R int16 1A 0x004C + //逆变侧BC线电压直流分量 R int16 1A 0x004D + //逆变侧CA线电压直流分量 R int16 1A 0x004E + //在线数量 R int16 0x004F + //逆变数量 R int16 0x0050 +}; + +// BMS 遥测 +struct BMSYC +{ + //SOC R uint16 0.1 0x0001 + //SOH R uint16 0.1 0x0002 + //电压 R uint32 0.1V 0x0003 + //电流 R int32 0.1A 0x0005 + //可充电量 R uint32 1kWh 0x0007 + //可放电量 R uint32 1kWh 0x0009 + //单次可充电量 R uint32 1kWh 0x000B + //单次可放电量 R uint32 1kWh 0x000D + //堆功率 R int32 1kW 0x000F + //充电量累加 R uint32 1kWh 0x0011 + //放电量累加 R uint32 1kWh 0x0013 + //簇最大SOC R uint16 0.1 0x0015 + //簇最小SOC R uint16 0.1 0x0016 + //簇最大SOC号 R uint16 0x0017 + //簇最小SOC号 R uint16 0x0018 + //簇SOC差值 R uint16 0.1 0x0019 + //簇最大电压 R uint16 0.1V 0x001A + //簇最小电压 R uint16 0.1V 0x001B + //簇最大电压号 R uint16 0x001C + //簇最小电压号 R uint16 0x001D + //簇电压差值 R uint16 0.1V 0x001E + //单体最大电压簇号 R uint16 0x001F + //单体最大电压节号 R uint16 0x0020 + //单体最大电压 R uint16 mV 0x0021 + //单体最小电压簇号 R uint16 0x0022 + //单体最小电压节号 R uint16 0x0023 + //单体最小电压 R uint16 mV 0x0024 + //单体平均电压 R uint16 mV 0x0025 + //单体电压差 R uint16 mV 0x0026 + //单体最大温度簇号 R uint16 0x0027 + //单体最大温度节号 R uint16 0x0028 + //单体最大温度 R int16 0.1℃ 0x0029 + //单体最小温度簇号 R uint16 0x002A + //单体最小温度节号 R uint16 0x002B + //单体最小温度 R int16 0.1℃ 0x002C + //单体平均温度 R int16 0.1℃ 0x002D + //单体温度差 R int16 0.1℃ 0x002E + //最大内阻簇号 R uint16 0x002F + //最大内阻节号 R uint16 0x0030 + //最大内阻 R uint16 mΩ 0x0031 + //最小内阻簇号 R uint16 0x0032 + //最小内阻节号 R uint16 0x0033 + //最小内阻 R uint16 mΩ 0x0034 + //单体平均内阻 R uint16 mΩ 0x0035 + //单体内阻差 R uint16 mΩ 0x0036 + //单体最大SOH簇号 R uint16 0x0037 + //单体最大SOH节号 R uint16 0x0038 + //单体最大SOH R uint16 0.10% 0x0039 + //单体最小SOH簇号 R uint16 0x003A + //单体最小SOH节号 R uint16 0x003B + //单体最小SOH R uint16 0.10% 0x003C + //单体最大SOc簇号 R uint16 0x003D + //单体最大SOc节号 R uint16 0x003E + //单体最大Soc R uint16 0.10% 0x0040 + //单体最小SOc簇号 R uint16 0x0041 + //单体最小SOc节号 R uint16 0x0042 + //单体最小SOc R uint16 0.10% 0x0043 + //系统剩余最大可充电功率 R uint32 1KW 0x0043 + //系统剩余最大可放电功率 R uint32 1KW 0x0045 + //可充电状态 R uint16 1:可充电;0:不可充电 0x0047 + //可放电状态 R uint16 1:可放电;0:不可放电 0x0048 + //运行状态 R uint16 运行状态 0-正常 1-告警 2-保护 0x0049 + //充放电状态 R uint16 0-待机 1-充电 2-放电 0x004A + +}; + + +// BCU遥信 +struct BCUYX +{ + //所属通道号 R uint16 1~4 0xA001 + //所属BCU号 R uint16 1~40 0xA002 + //蓄电池充放电状态 R uint16 "0x11开路 + //0x22待机 + //0x33充电 + //0x44放电" 0xA003 + //电池组运行状态 R uint16 "0x11跳机 + //0x22待机 + //0x33放空 + //0x44充满 + //0x55预警 + //0x66正常" 0xA004 + //簇DO1状态 R uint16 1:断开 2:闭合 0xA005 + //簇DO2状态 R uint16 1:断开 2:闭合 0xA006 + //继电器总正 R uint16 0:断开 1:闭合 2:粘连 0xA007 + //继电器总负 R uint16 0 : 断开 1:闭合 2:粘连 0xA008 + //继电器预充 R uint16 0 : 断开 1:闭合 2:粘连 0xA009 + //继电器bmu供电 R uint16 0 : 断开 1:闭合 2:粘连 0xA00A + //整簇总电压过高告警 R uint16 0:正常 1:告警 0xA00B + //整簇总电压过低告警 R uint16 0:正常 1:告警 0xA00C + //整簇中单体电压过高告警 R uint16 0:正常 1:告警 0xA00D + //整簇中单体电压过低告警 R uint16 0:正常 1:告警 0xA00E + //整簇中单体电压偏差过大告警 R uint16 0:正常 1:告警 0xA00F + //整簇中单体温度偏差过大告警 R uint16 0:正常 1:告警 0xA010 + //整簇中单体温度过高告警 R uint16 0:正常 1:告警 0xA011 + //整簇中单体温度过低告警 R uint16 0:正常 1:告警 0xA012 + //整簇总充电电流过高告警 R uint16 0:正常 1:告警 0xA013 + //整簇总放电电流过高告警 R uint16 0:正常 1:告警 0xA014 + //整簇总SOC过高告警 R uint16 0:正常 1:告警 0xA015 + //整簇总SOC过低告警 R uint16 0:正常 1:告警 0xA016 + //高压盒主正接触器粘连告警 R uint16 0:正常 1:告警 0xA017 + //高压盒主正接触器不能吸合告警 R uint16 0:正常 1:告警 0xA018 + //高压盒主负接触器粘连告警 R uint16 0:正常 1:告警 0xA019 + //高压盒主负接触器不能吸合告警 R uint16 0:正常 1:告警 0xA01A + //高压盒预充接触器粘连告警 R uint16 0:正常 1:告警 0xA01B + //高压盒预充接触器不能吸合告警 R uint16 0:正常 1:告警 0xA01C + //预充失败告警 R uint16 0:正常 1:告警 0xA01D + //BCU电压检测模块出现问题告警 R uint16 0:正常 1:告警 0xA01E + //BCU温度检测模块出现问题告警 R uint16 0:正常 1:告警 0xA01F + //BCU电流检测模块出现问题告警 R uint16 0:正常 1:告警 0xA020 + //BCU绝缘检测模块出现问题告警 R uint16 0:正常 1:告警 0xA021 + //高压盒内总压检测模块出现问题告警 R uint16 0:正常 1:告警 0xA022 + //高压盒外总压检测模块出现问题告警 R uint16 0:正常 1:告警 0xA023 + //PCS-CAN通信故障告警 R uint16 0:正常 1:告警 0xA024 + //高压盒供电电压过高告警 R uint16 0:正常 1:告警 0xA025 + //绝缘正极故障告警 R uint16 0:正常 1:告警 0xA026 + //绝缘负极故障告警 R uint16 0:正常 1:告警 0xA027 + //绝缘中间侧故障告警 R uint16 0:正常 1:告警 0xA028 + //绝缘故障告警 R uint16 0:正常 1:告警 0xA029 + //BMU中电压采样线开路告警 R uint16 0:正常 1:告警 0xA02A + //BMU中NTC采样线短开路告警 R uint16 0:正常 1:告警 0xA02B + //BMU中采样芯片故障告警 R uint16 0:正常 1:告警 0xA02C + //BMU中电池温度升高过快告警 R uint16 0:正常 1:告警 0xA02D + //BMU中电池内部短路告警 R uint16 0:正常 1:告警 0xA02E + //BMU充电均衡模块出现故障告警 R uint16 0:正常 1:告警 0xA02F + //BMU放电均衡模块出现故障告警 R uint16 0:正常 1:告警 0xA030 + //BMU通信故障告警 R uint16 0:正常 1:告警 0xA031 + //单体内阻过大告警 R uint16 0:正常 1:告警 0xA032 + //单体内阻过小告警 R uint16 0:正常 1:告警 0xA033 + //单体内阻阻差过大告警 R uint16 0:正常 1:告警 0xA034 + //簇内阻过大告警 R uint16 0:正常 1:告警 0xA035 + //簇内阻过小告警 R uint16 0:正常 1:告警 0xA036 + //SOC初始化无效告警 R uint16 0:正常 1:告警 0xA037 + //充电时soc降低故障告警 R uint16 0:正常 1:告警 0xA038 + //放电时soc升高告警 R uint16 0:正常 1:告警 0xA039 + //静止时SOC跳变告警 R uint16 0:正常 1:告警 0xA03A + //整簇总电压过高保护 R uint16 0:正常 1:保护 0xA03B + //整簇总电压过低保护 R uint16 0:正常 1:保护 0xA03C + //整簇中单体电压过高保护 R uint16 0:正常 1:保护 0xA03D + //整簇中单体电压过低保护 R uint16 0:正常 1:保护 0xA03E + //整簇中单体电压偏差过大保护 R uint16 0:正常 1:保护 0xA03F + //整簇中单体温度偏差过大保护 R uint16 0:正常 1:保护 0xA040 + //整簇中单体温度过高保护 R uint16 0:正常 1:保护 0xA041 + //整簇中单体温度过低保护 R uint16 0:正常 1:保护 0xA042 + //整簇总充电电流过高保护 R uint16 0:正常 1:保护 0xA043 + //整簇总放电电流过高保护 R uint16 0:正常 1:保护 0xA044 + //整簇总SOC过高保护 R uint16 0:正常 1:保护 0xA045 + //整簇总SOC过低保护 R uint16 0:正常 1:保护 0xA046 + //高压盒主正接触器粘连保护 R uint16 0:正常 1:保护 0xA047 + //高压盒主正接触器不能吸合保护 R uint16 0:正常 1:保护 0xA048 + //高压盒主负接触器粘连保护 R uint16 0:正常 1:保护 0xA049 + //高压盒主负接触器不能吸合保护 R uint16 0:正常 1:保护 0xA04A + //高压盒预充接触器粘连保护 R uint16 0:正常 1:保护 0xA04B + //高压盒预充接触器不能吸合保护 R uint16 0:正常 1:保护 0xA04C + //预充失败保护 R uint16 0:正常 1:保护 0xA04D + //BCU电压检测模块出现问题保护 R uint16 0:正常 1:保护 0xA04E + //BCU温度检测模块出现问题保护 R uint16 0:正常 1:保护 0xA04F + //BCU电流检测模块出现问题保护 R uint16 0:正常 1:保护 0xA050 + //BCU绝缘检测模块出现问题保护 R uint16 0:正常 1:保护 0xA051 + //高压盒内总压检测模块出现问题保护 R uint16 0:正常 1:保护 0xA052 + //高压盒外总压检测模块出现问题保护 R uint16 0:正常 1:保护 0xA053 + //PCS-CAN通信故障保护 R uint16 0:正常 1:保护 0xA054 + //高压盒供电电压过高保护 R uint16 0:正常 1:保护 0xA055 + //绝缘正极故障保护 R uint16 0:正常 1:保护 0xA056 + //绝缘负极故障保护 R uint16 0:正常 1:保护 0xA057 + //绝缘中间侧故障保护 R uint16 0:正常 1:保护 0xA058 + //绝缘故障保护 R uint16 0:正常 1:保护 0xA059 + //BMU中电压采样线开路保护 R uint16 0:正常 1:保护 0xA05A + //BMU中NTC采样线短开路保护 R uint16 0:正常 1:保护 0xA05B + //BMU中采样芯片故障保护 R uint16 0:正常 1:保护 0xA05C + //BMU中电池温度升高过快保护 R uint16 0:正常 1:保护 0xA05D + //BMU中电池内部短路保护 R uint16 0:正常 1:保护 0xA05E + //BMU充电均衡模块出现故障保护 R uint16 0:正常 1:保护 0xA05F + //BMU放电均衡模块出现故障保护 R uint16 0:正常 1:保护 0xA060 + //BMU通信故障保护 R uint16 0:正常 1:保护 0xA061 + //单体内阻过大保护 R uint16 0:正常 1:保护 0xA062 + //单体内阻过小保护 R uint16 0:正常 1:保护 0xA063 + //单体内阻阻差过大保护 R uint16 0:正常 1:保护 0xA064 + //簇内阻过大保护 R uint16 0:正常 1:保护 0xA065 + //簇内阻过小保护 R uint16 0:正常 1:保护 0xA066 + //SOC初始化无效保护 R uint16 0:正常 1:保护 0xA067 + //充电时soc降低故障保护 R uint16 0:正常 1:保护 0xA068 + //放电时soc升高保护 R uint16 0:正常 1:保护 0xA069 + //静止时SOC跳变保护 R uint16 0:正常 1:保护 0xA06A +}; + +// BCU遥测 +struct BCUYC +{ + //所属通道号 R uint16 1~4 0x0001 + //所属BCU号 R uint16 1~40 0x0002 + //簇电压 R uint32 0.1V 0x0003 + //簇电流 R int32 0.1A 0x0005 + //簇温度 R int32 0.1℃ 0x0007 + //簇电阻 R uint32 1mΩ 0x0009 + //簇SOC R uint16 0.1 0x000B + //簇SOH R uint16 0.1 0x000C + //簇正绝缘电阻 R uint32 1kΩ 0x000D + //簇负绝缘电阻 R uint32 1kΩ 0x000F + //簇允许最大充电电流 R int32 0.1A 0x0011 + //簇允许最大放电电流 R int32 0.1A 0x0013 + //簇允许最大充电功率 R uint32 1kW 0x0015 + //簇允许最大放电功率 R uint32 1kW 0x0017 + //簇可充容量 R uint32 0.1Ah 0x0019 + //簇可放容量 R uint32 0.1Ah 0x001B + //簇单次累计充容量 R uint32 0.1Ah 0x001D + //簇单次累计放容量 R uint32 0.1Ah 0x001F + //簇总累计充容量 R uint32 0.1Ah 0x0021 + //簇总累计放容量 R uint32 0.1Ah 0x0023 + //簇可充电量 R uint32 1kWh 0x0025 + //簇可放电量 R uint32 1kWh 0x0027 + //簇单次充电量 R uint32 1kWh 0x0029 + //簇单次放电量 R uint32 1kWh 0x002B + //簇累计充电量 R uint32 1kWh 0x002D + //簇累计放电量 R uint32 1kWh 0x002F + //pack累计簇总压 R uint32 0.1V 0x0031 + //簇与pack压差 R uint32 0.1V 0x0033 + //簇与PCS压差 R uint32 0.1V 0x0035 + //簇中BMU个数 R uint16 0x0037 + //簇中BMU中单体个数 R uint16 0x0038 + //簇中BMU中温度个数 R uint16 0x0039 + //簇中单体个数 R uint16 0x003A + //簇中温度个数 R uint16 0x003B + //单体最高SOC节号 R uint16 0x003C + //单体最高SOC R uint16 0.1 0x003D + //单体最低SOC节号 R uint16 0x003E + //单体最低SOC R uint16 0.1 0x003F + //单体最高SOH节号 R uint16 0x0040 + //单体最高SOH R uint16 0.1 0x0041 + //单体最低SOH节号 R uint16 0x0042 + //单体最低SOH R uint16 0.1 0x0043 + //单体最高电压节号 R uint16 0x0044 + //单体最高电压 R uint16 mV 0x0045 + //单体最低电压节号 R uint16 0x0046 + //单体最低电压 R uint16 mV 0x0047 + //单体电压差 R uint16 mV 0x0048 + //单体平均电压 R uint16 mV 0x0049 + //单体最高温度节号 R uint16 0x004A + //单体最高温度 R int16 0.1℃ 0x004B + //单体最低温度节号 R uint16 0x004C + //单体最低温度 R int16 0.1℃ 0x004D + //单体温度差 R int16 0.1℃ 0x004E + //单体平均温度 R int16 0.1℃ 0x004F + //单体最高内阻节号 R uint16 0x0050 + //单体最高内阻 R uint16 mΩ 0x0051 + //单体最低内阻节号 R uint16 0x0052 + //单体最低内阻 R uint16 mΩ 0x0053 + //单体内阻差 R uint16 mΩ 0x0054 + //单体平均内阻 R uint16 mΩ 0x0055 + //单体SOC R uint16[1000] 0.1 0x0056~0x043D + //单体SOH R uint16[1000] 0.1 0x043E~0x0825 + //单体电压 R uint16[1000] mV 0x0826~0x0C0D + //单体温度 R int16[1000] 0.01℃ 0x0C0E~0x0FF5 + //单体内阻 R uint16[1000] mΩ 0x0FF6~0x13DD +}; + +// 空调遥信 +struct AirCYX +{ + //所属通道号 R uint16 1 0x1001 + //所属空调号 R uint16 1~10 0x1002 + //开关 R uint16 0:关机,1:开机 0x1003 + //启动制冷指令 R uint16 0:关闭, 1:启动 0x1004 + //启动送风指令 R uint16 0:关闭, 1:启动 0x1005 + //启动待机指令 R uint16 0:关闭, 1:启动 0x1006 + //启动加热指令 R uint16 0:关闭, 1:启动 0x1007 + //传感器故障 R uint16 0:正常,1:告警 0x1008 + //高低电压告警 R uint16 0:正常,1:告警 0x1009 + //高低温告警 R uint16 0:正常,1:告警 0x100A + //高低压告警 R uint16 0:正常,1:告警 0x100B + //压缩机告警 R uint16 0:正常,1:告警 0x100C + +}; + +// 空调遥测 +struct AirCYC +{ + //所属通道号 R uint16 1 0x0001 + //所属空调号 R uint16 1~10 0x0002 + //制冷点 R int16 0.1℃ 0x0003 + //制冷偏差 R int16 0.1℃ 0x0004 + //高温告警值 R int16 0.1℃ 0x0005 + //低温告警值 R int16 0.1℃ 0x0006 + //制热点 R int16 0.1℃ 0x0007 + //制热偏差 R int16 0.1℃ 0x0008 + //当前温度 R int16 0.1℃ 0x0009 + //当前湿度 R int16 0.1℃ 0x000A + //除湿开启温度 R int16 0.1℃ 0x000B + //除湿停止温度 R int16 0.1℃ 0x000C + //除湿开启湿度 R int16 0.1℃ 0x000D + //除湿停止湿度 R int16 0.1℃ 0x000E +}; + +// 电表遥测 +struct EMeterYC +{ + //所属通道号 R uint16 1 0x0001 + //电表地址 R uint16[6] 0x0002~0x0007 + //电表类型 R uint16 "0:储能站总表 + //1:逆变前侧电表 + //2:逆变后侧电表 + //3:配电柜电表 + //4:并网口电表" 0x0008 + //电流变比 R uint16 0x0009 + //电压变比 R uint16 0x000A + //A相电压 R uint32 1V 0x000B + //B相电压 R uint32 1V 0x000D + //C相电压 R uint32 1V 0x000F + //A相电流 R int32 1A 0x0011 + //B相电流 R int32 1A 0x0013 + //C相电流 R int32 1A 0x0015 + //AB相电压 R uint32 1V 0x0017 + //BC相电压 R uint32 1V 0x0019 + //CA相电压 R uint32 1V 0x001B + //A相有功 R int32 1kW 0x001D + //B相有功 R int32 1kW 0x001F + //C相有功 R int32 1kW 0x0021 + //三相总有功 R int32 1kW 0x0023 + //正向总有功总需量 R int32 1kW 0x0025 + //尖段电价 R uint32 1RMB 0x0027 + //峰段电价 R uint32 1RMB 0x0029 + //平段电价 R uint32 1RMB 0x002B + //谷段电价 R uint32 1RMB 0x002D + //日充电电量 R uint32 1kWh 0x002F + //日放电电量 R uint32 1kWh 0x0031 + //日充电费用 R uint32 1RMB 0x0033 + //日放电费用 R uint32 1RMB 0x0035 + //日收益 R int32 1RMB 0x0037 + //日正向尖有功电能 R uint32 1kWh 0x0039 + //日正向峰有功电能 R uint32 1kWh 0x003B + //日正向平有功电能 R uint32 1kWh 0x003D + //日正向谷有功电能 R uint32 1kWh 0x003F + //日正向总有功电能 R uint32 1kWh 0x0041 + //日反向尖有功电能 R uint32 1kWh 0x0043 + //日反向峰有功电能 R uint32 1kWh 0x0045 + //日反向平有功电能 R uint32 1kWh 0x0047 + //日反向谷有功电能 R uint32 1kWh 0x0049 + //日反向总有功电能 R uint32 1kWh 0x004B + //总充电电量 R uint32 1kWh 0x004D + //总放电电量 R uint32 1kWh 0x004F + //总充电费用 R uint32 1RMB 0x0051 + //总放电费用 R uint32 1RMB 0x0053 + //总收益 R int32 1RMB 0x0055 + //总正向尖有功电能 R uint32 1kWh 0x0057 + //总正向峰有功电能 R uint32 1kWh 0x0059 + //总正向平有功电能 R uint32 1kWh 0x005B + //总正向谷有功电能 R uint32 1kWh 0x005D + //总正向总有功电能 R uint32 1kWh 0x005F + //总反向尖有功电能 R uint32 1kWh 0x0061 + //总反向峰有功电能 R uint32 1kWh 0x0063 + //总反向平有功电能 R uint32 1kWh 0x0065 + //总反向谷有功电能 R uint32 1kWh 0x0067 + //总反向总有功电能 R uint32 1kWh 0x0069 +}; + +// 温湿度遥测 +struct TemHumYC +{ + //所属通道号 R uint16 1 0x0001 + //所属温湿度号 R uint16 1~10 0x0002 + //温度 R int16 0.1℃ 0x0003 + //湿度 R int16 0.1℃ 0x0004 +}; + +struct Fire20YX +{ + // 测点太多(1000多个) +}; + +struct Fire30YX +{ + // 测点太多(1000多个) +}; + +struct Fire40YX +{ + //所属通道号 R uint16 1~10 0x0001 + //主控数量 R uint16 1 0x0002 + //主控ID R uint16 1 0x0003 + //主控状态 R uint16 0:正常 1:预警 2:火警 0x0004 + //主控硬件版本 R uint16[2] 主控硬件版本 0x0005~0x0006 + //主控软件版本 R uint16[2] 主控软件版本 0x0007~0x0008 + //主电状态 R uint16 0:使用市电 1:使用备电 0x0009 + //备电电流 R uint32 0.1A 0x000A + //备电电压 R uint32 0.1V 0x000C + //可用容量 R uint32 0.01Ah 0x000E + //可充放容量 R uint32 0.01Ah 0x0010 + //警铃是否使用 R uint16 0x0012 + //警铃状态 R uint16 0:无效 1:掉线 2:正常 3:启动 0x0013 + //瓶头阀是否使用 R uint16 0x0014 + //瓶头阀状态 R uint16 0:无效 1:掉线 2:正常 3:启动 0x0015 + //手报是否使用 R uint16 0x0016 + //手报状态 R uint16 0:无效 1:掉线 2:正常 3:启动 0x0017 + //簇控制器数量 R uint16 0x0018 + //复合探测器总数量 R uint16 0x0019 + //烟雾探测器总数量 R uint16 0x001A + //压力探测器总数量 R uint16 0x001B + //吸气式探测器总数量 R uint16 0x001C + //PACK探测器总数量 R uint16 0x001D + //电池总数量 R uint16 0x001E + +}; + +// 冷机遥信 +struct ChillerYX +{ + //所属通道号 R uint16 1 0x1001 + //所属冷机号 R uint16 1~10 0x1002 + //开关 R uint16 0:关机,1:开机 0x1003 + //采样模式 R uint16 0-出水温度 1-电芯温度 0x1004 + //制冷状态 R uint16 0:关闭, 1:启动 0x1005 + //制热状态 R uint16 0:关闭, 1:启动 0x1006 + //高温告警 R uint16 0:正常,1:告警 0x1007 + //低温告警 R uint16 0:正常,1:告警 0x1008 + //高压告警 R uint16 0:正常,1:告警 0x1009 + //低压告警 R uint16 0:正常,1:告警 0x100A + //进水温度传感器 R uint16 0:正常,1:告警 0x100B + //出水温度传感器 R uint16 0:正常,1:告警 0x100C + //进水压力传感器 R uint16 0:正常,1:告警 0x100D + //出水压力传感器 R uint16 0:正常,1:告警 0x100E +}; + +// 冷机遥测 +struct ChillerYC +{ + //所属通道号 R uint16 1 0x0001 + //所属冷机号 R uint16 1~10 0x0002 + //制冷点 R int16 0.1℃ 0x0003 + //制冷偏差 R int16 0.1℃ 0x0004 + //高温告警值 R int16 0.1℃ 0x0005 + //低温告警值 R int16 0.1℃ 0x0006 + //制热点 R int16 0.1℃ 0x0007 + //制热偏差 R int16 0.1℃ 0x0008 + //电芯温度 R int16 0.1℃ 0x0009 + //环境湿度 R int16 0.1℃ 0x000A + //吸气温度 R int16 0.1℃ 0x000B + //排气温度 R int16 0.1℃ 0x000C + //进水温度/供液温度 R int16 0.1℃ 0x000D + //出水温度/回液温度 R int16 0.1℃ 0x000E + //进水压力/供液压力 R int16 0.1 0x000F + //出水压力/回液压力 R int16 0.1 0x0010 + //高压压力 R int16 0.1 0x0011 + //低压压力 R int16 0.1 0x0012 + //循环水泵转速 R int16 0x0013 + //压缩机频率 R int16 0x0014 + //室外风机转速 R int16 0x0015 +}; \ No newline at end of file diff --git a/src/app/Device.h b/src/app/Device.h index d206f0e..bcaa76e 100644 --- a/src/app/Device.h +++ b/src/app/Device.h @@ -35,6 +35,10 @@ public: //double getAttrDouble(std::string key); //std::string getAttrStr(std::string key); + + int64_t tsDataDate {}; + std::map mapCacheData; + // 启动通讯 int startComm(); diff --git a/src/app/Station.cpp b/src/app/Station.cpp index ad7b79e..fa63d52 100644 --- a/src/app/Station.cpp +++ b/src/app/Station.cpp @@ -4,10 +4,30 @@ #include "common/fields.h" #include "app/Device.h" #include "common/Spdlogger.h" - +#include "common/Utils.h" +#include "protocol/MqttEntity.h" Station::Station() : id(0) { + mqttCli = std::make_shared(); + + // 测试,设置默认值 + for (int i = 1; i<=5; i++) { mapTempHumUnit[i] = TempHumUnit(Utils::random(20, 40), Utils::random(20, 80)); } + + for (int i = 1; i<=5; i++) { mapFire40Unit[i] = 0; } + + for (int i = 1; i<=5; i++) { + auto& unit = mapCoolingUnit[i]; + unit.powerOn = 1; + unit.mode = i%2; + } + + for (int i = 1; i<=5; i++) { + auto& unit = mapAircUnit[i]; + unit.powerOn = 1; + unit.temp = Utils::random(20, 40); + unit.hum = Utils::random(20, 80); + } } void Station::setFields(Fields& fields) @@ -15,7 +35,8 @@ void Station::setFields(Fields& fields) this->id = fields.get(DMStation::STATION_ID); this->name = fields.value(DMStation::NAME); this->energyCapacity = fields.get(DMStation::CAPACITY); - this->workModeId = fields.get(DMStation::WORK_MODE_ID); + this->workModeId = fields.get(DMStation::WORK_MODE); + this->code = fields.value(DMStation::CODE); } void Station::addDevice(int deviceId, std::shared_ptr device) @@ -67,7 +88,7 @@ void Station::setWorkMode(int modeId) { this->workModeId = modeId; std::string sql = SQL(SQL::TYPE::update).table(DMStation::TABLENAME) - .update(DMStation::WORK_MODE_ID, std::to_string(modeId)) + .update(DMStation::WORK_MODE, std::to_string(modeId)) .where(DMStation::STATION_ID + "=" + std::to_string(id)).str(); Errcode err = DAO::exec(NULL, sql); if (err != Errcode::OK) diff --git a/src/app/Station.h b/src/app/Station.h index 77f0d11..569a45d 100644 --- a/src/app/Station.h +++ b/src/app/Station.h @@ -5,6 +5,87 @@ #include "common/Fields.h" class Device; +class MqttClient; + +struct TempHumUnit +{ + int temp {0}; + int hum {0}; + TempHumUnit(int temp, int hum) : temp(temp), hum(hum) {}; + TempHumUnit() {} +}; + +struct Fire40Unit +{ + //主控数量 R uint16 1 0x0002 + //主控ID R uint16 1 0x0003 + //主控状态 R uint16 0:正常 1:预警 2:火警 0x0004 + //主控硬件版本 R uint16[2] 主控硬件版本 0x0005~0x0006 + //主控软件版本 R uint16[2] 主控软件版本 0x0007~0x0008 + //主电状态 R uint16 0:使用市电 1:使用备电 0x0009 + //备电电流 R uint32 0.1A 0x000A + //备电电压 R uint32 0.1V 0x000C + //可用容量 R uint32 0.01Ah 0x000E + //可充放容量 R uint32 0.01Ah 0x0010 + //警铃是否使用 R uint16 0x0012 + //警铃状态 R uint16 0:无效 1:掉线 2:正常 3:启动 0x0013 + //瓶头阀是否使用 R uint16 0x0014 + //瓶头阀状态 R uint16 0:无效 1:掉线 2:正常 3:启动 0x0015 + //手报是否使用 R uint16 0x0016 + //手报状态 R uint16 0:无效 1:掉线 2:正常 3:启动 0x0017 + //簇控制器数量 R uint16 0x0018 + //复合探测器总数量 R uint16 0x0019 + //烟雾探测器总数量 R uint16 0x001A + //压力探测器总数量 R uint16 0x001B + //吸气式探测器总数量 R uint16 0x001C + //PACK探测器总数量 R uint16 0x001D + //电池总数量 R uint16 0x001E + +}; + + +struct CoolingUnit +{ + int powerOn {0}; //开关 R uint16 0:关机,1:开机 0x1003 + int mode {0}; //采样模式 R uint16 0-出水温度 1-电芯温度 0x1004 + int cooling {0}; //制冷状态 R uint16 0:关闭, 1:启动 0x1005 + int heating {0}; //制热状态 R uint16 0:关闭, 1:启动 0x1006 + int highTemp {0}; //高温告警 R uint16 0:正常,1:告警 0x1007 + int lowTemp {0}; //低温告警 R uint16 0:正常,1:告警 0x1008 + int highPressure {0}; //高压告警 R uint16 0:正常,1:告警 0x1009 + int lowPressure {0}; //低压告警 R uint16 0:正常,1:告警 0x100A + //进水温度传感器 R uint16 0:正常,1:告警 0x100B + //出水温度传感器 R uint16 0:正常,1:告警 0x100C + //进水压力传感器 R uint16 0:正常,1:告警 0x100D + //出水压力传感器 R uint16 0:正常,1:告警 0x100E +}; + +struct AircUnit +{ + int powerOn {0}; //开关 R uint16 0:关机,1:开机 + int cooling {0}; //启动制冷指令 R uint16 0:关闭, 1:启动 + int airSupply {0}; //启动送风指令 R uint16 0:关闭, 1:启动 + int standby {0}; //启动待机指令 R uint16 0:关闭, 1:启动 + int heating {0}; //启动加热指令 R uint16 0:关闭, 1:启动 + int sensorAlarm {0}; //传感器故障 R uint16 0:正常,1:告警 + int voltageAlarm {0}; //高低电压告警 R uint16 0:正常,1:告警 + int tempAlarm {0}; //高低温告警 R uint16 0:正常,1:告警 + int pressureAlarm {0}; //高低压告警 R uint16 0:正常,1:告警 + int compressorAlarm {0};//压缩机告警 R uint16 0:正常,1:告警 + + //制冷点 R int16 0.1℃ 0x0003 + //制冷偏差 R int16 0.1℃ 0x0004 + //高温告警值 R int16 0.1℃ 0x0005 + //低温告警值 R int16 0.1℃ 0x0006 + //制热点 R int16 0.1℃ 0x0007 + //制热偏差 R int16 0.1℃ 0x0008 + int temp {0}; //当前温度 R int16 0.1℃ 0x0009 + int hum {0}; //当前湿度 R int16 0.1℃ 0x000A + //除湿开启温度 R int16 0.1℃ 0x000B + //除湿停止温度 R int16 0.1℃ 0x000C + //除湿开启湿度 R int16 0.1℃ 0x000D + //除湿停止湿度 R int16 0.1℃ 0x000E +}; class Station { @@ -28,6 +109,7 @@ public: public: int id {}; std::string name; + std::string code; bool isConnected {false}; int workModeId {}; // 运行模式 @@ -89,4 +171,17 @@ public: std::unordered_map> mapDevice; std::map mapDeviceGroupNum; + + // 温湿度信息 + std::map mapTempHumUnit; + // 消防4.0信息 + std::map mapFire40Unit; + // 冷机信息 + std::map mapCoolingUnit; + // 空调信息 + std::map mapAircUnit; + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// === MQTT client + std::shared_ptr mqttCli {nullptr}; }; \ No newline at end of file diff --git a/src/app/errcode.cpp b/src/app/errcode.cpp index 8878a00..d9c4113 100644 --- a/src/app/errcode.cpp +++ b/src/app/errcode.cpp @@ -7,6 +7,7 @@ static std::unordered_map mapErr = {Errcode::ERR_TOKEN, "TOKEN错误"}, {Errcode::ERR_PARAM, "参数错误"}, {Errcode::ERR_PARAM_NUL, "缺少参数"}, + {Errcode::ERR_DATA_NUL, "数据不存"}, {Errcode::ERR_LOGIN_USER_NOTEXIST, "用户不存在"}, {Errcode::ERR_LOGIN_PASSWD, "密码错误"}, diff --git a/src/app/errcode.h b/src/app/errcode.h index 003e7d4..32e9721 100644 --- a/src/app/errcode.h +++ b/src/app/errcode.h @@ -9,6 +9,7 @@ enum class Errcode ERR_TOKEN, // TOKEN错误 ERR_PARAM, // 参数错误 ERR_PARAM_NUL, // 缺少参数 + ERR_DATA_NUL, // 数据不存在 ERR_USER = 100, ERR_LOGIN_USER_NOTEXIST, // 登入错误,用户不存在 diff --git a/src/common/Fields.cpp b/src/common/Fields.cpp index 5c34f35..b580ddc 100644 --- a/src/common/Fields.cpp +++ b/src/common/Fields.cpp @@ -184,7 +184,6 @@ void Fields::parseJson(std::string jsonstr) { NJsonNode jsonroot; NJson::parse(jsonstr, jsonroot); - for (auto& item : jsonroot.items()) { this->set(item.key(), item.value()); diff --git a/src/common/JsonN.h b/src/common/JsonN.h index cc17059..7b42ca2 100644 --- a/src/common/JsonN.h +++ b/src/common/JsonN.h @@ -54,19 +54,17 @@ public: static bool load(std::string jsonfile, NJsonNode& json) { std::ifstream ifs(jsonfile); - if (ifs.is_open()) - { - ifs >> json; - return true; - } - return false; + if (!ifs.is_open()) { return false; } + try { ifs >> json; } + catch (nlohmann::json::parse_error& e) { return false; } + return true; } static bool parse(std::string jsonstr, NJsonNode& json) { try { - json = NJsonNode::parse(jsonstr); + if (!jsonstr.empty()) { json = NJsonNode::parse(jsonstr); } } catch (nlohmann::json::parse_error& e) { @@ -75,11 +73,6 @@ public: } return true; } - - static bool contains(NJsonNode& json, std::string key) - { - return json.contains("database"); - } template static void read(NJsonNode& json, std::string k, T& v) @@ -90,7 +83,7 @@ public: } catch (const nlohmann::detail::exception& e) { - std::cout << "JSON read error: " << e.what() << std::endl; + std::cout << "JSON read error: k=" << k << ", err=" << e.what() << std::endl; } } diff --git a/src/common/Snowflake.h b/src/common/Snowflake.h index 46b3a8a..7d98e2f 100644 --- a/src/common/Snowflake.h +++ b/src/common/Snowflake.h @@ -41,21 +41,13 @@ public: return inst; } - void setWorkerId(unsigned int workerId) { - this->workerId_ = workerId; - } + void setWorkerId(unsigned int workerId) { this->workerId = workerId; } - void setDatacenterId(unsigned int datacenterId) { - this->datacenterId_ = datacenterId; - } + void setDatacenterId(unsigned int datacenterId) { this->datacenterId = datacenterId; } - uint64_t getId() { - return nextId(); - } + uint64_t getId() { return nextId(); } - std::string getIdStr() { - return std::to_string(nextId()); - } + std::string getIdStr() { return std::to_string(nextId()); } /** * 获得下一个ID (该方法是线程安全的) @@ -72,38 +64,38 @@ public: timestamp = timetick(); // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 - if (timestamp < lastTimestamp_) { + if (timestamp < lastTimestamp) { std::ostringstream s; - s << "clock moved backwards. Refusing to generate id for " << lastTimestamp_ - timestamp << " milliseconds"; + s << "clock moved backwards. Refusing to generate id for " << lastTimestamp - timestamp << " milliseconds"; throw std::exception(std::runtime_error(s.str())); } - if (lastTimestamp_ == timestamp) { + if (lastTimestamp == timestamp) { // 如果是同一时间生成的,则进行毫秒内序列 - sequence_ = (sequence_ + 1) & sequenceMask; - if (0 == sequence_) { + sequence = (sequence + 1) & sequenceMask; + if (0 == sequence) { // 毫秒内序列溢出, 阻塞到下一个毫秒,获得新的时间戳 - timestamp = tilNextMillis(lastTimestamp_); + timestamp = tilNextMillis(lastTimestamp); } } else { - sequence_ = 0; + sequence = 0; } #ifndef SNOWFLAKE_ID_WORKER_NO_LOCK - lastTimestamp_ = timestamp; + lastTimestamp = timestamp; #else lastTimestamp_ = timestamp.load(); #endif // 移位并通过或运算拼到一起组成64位的ID - return ((timestamp - twepoch_) << timestampLeftShift) - | (datacenterId_ << datacenterIdShift) - | (workerId_ << workerIdShift) - | sequence_; + return ((timestamp - twepoch) << timestampLeftShift) + | (datacenterId << datacenterIdShift) + | (workerId << workerIdShift) + | sequence; } protected: - Snowflake() : workerId_(0), datacenterId_(0), sequence_(0), lastTimestamp_(0) {} + Snowflake() : workerId(0), datacenterId(0), sequence(0), lastTimestamp(0) {} /** * 返回以毫秒为单位的当前时间 @@ -139,7 +131,7 @@ private: /** * 开始时间截 2025-01-01 00:00:00.000 */ - const uint64_t twepoch_ = 1735660800000; + const uint64_t twepoch = 1735660800000; /** * 机器id所占的位数 @@ -189,22 +181,22 @@ private: /** * 工作机器id(0~31) */ - unsigned int workerId_; + unsigned int workerId; /** * 数据中心id(0~31) */ - unsigned int datacenterId_; + unsigned int datacenterId; /** * 毫秒内序列(0~4095) */ - AtomicUInt sequence_ {0}; + AtomicUInt sequence {0}; /** * 上次生成ID的时间截 */ - AtomicUInt64 lastTimestamp_ {0}; + AtomicUInt64 lastTimestamp {0}; }; #endif // _JW_CORE_ID_WORKER_H_ \ No newline at end of file diff --git a/src/common/Utils.cpp b/src/common/Utils.cpp index 788d760..0e8fed2 100644 --- a/src/common/Utils.cpp +++ b/src/common/Utils.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include using namespace std; string Utils::toStr(int v) @@ -147,18 +149,18 @@ string Utils::to_hex_text(string s, string d/* = " "*/) void time_point_to_duration() { - auto tp = std::chrono::system_clock::now(); + auto tp = chrono::system_clock::now(); - auto seconds = std::chrono::duration_cast(tp.time_since_epoch()); + auto seconds = chrono::duration_cast(tp.time_since_epoch()); cout << seconds.count() << " s" << endl;//seconds from 1970 - auto ms = std::chrono::duration_cast(tp.time_since_epoch()); + auto ms = chrono::duration_cast(tp.time_since_epoch()); cout << ms.count() << " ms" << endl; - auto us = std::chrono::duration_cast(tp.time_since_epoch()); + auto us = chrono::duration_cast(tp.time_since_epoch()); cout << us.count() << " us" << endl; - auto ns = std::chrono::duration_cast(tp.time_since_epoch()); + auto ns = chrono::duration_cast(tp.time_since_epoch()); cout << ns.count() << " ns" << endl; cout << tp.time_since_epoch().count() << " default is ns" << endl; @@ -167,17 +169,17 @@ void time_point_to_duration() void duration_to_time_point() { std::uint64_t ticker = 1609756793160376465; - auto ns = std::chrono::nanoseconds(ticker); + auto ns = chrono::nanoseconds(ticker); - auto tp1 = std::chrono::time_point(ns); - auto tp2 = tp1 - std::chrono::hours(1);//time point before one hour + auto tp1 = chrono::time_point(ns); + auto tp2 = tp1 - chrono::hours(1);//time point before one hour cout << "tp1=" << tp1.time_since_epoch().count() << endl << "tp2=" << tp2.time_since_epoch().count() << endl; } void format_time_point() { - auto tp = std::chrono::system_clock::now(); - auto time = std::chrono::system_clock::to_time_t(tp); + auto tp = chrono::system_clock::now(); + auto time = chrono::system_clock::to_time_t(tp); std::stringstream ss; ss << std::put_time(std::localtime(&time), "%Y-%m-%d %H:%M:%S"); @@ -191,19 +193,44 @@ void parse_from_string() std::tm tm{}; ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S"); - auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm)); + auto tp = chrono::system_clock::from_time_t(std::mktime(&tm)); cout << tp.time_since_epoch().count() << endl; } -int64_t Utils::time() +int64_t Utils::time(std::string s/* = ""*/) { - return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - //return std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + if (s.empty()) + { + //return chrono::time_point_cast(chrono::system_clock::now()).time_since_epoch().count(); + return chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); + } + + static const std::string T = "1987/01/01 00/00/00"; + // YYYY/mm/dd HH/MM/SS + int len = T.size(); + int size = s.size(); + if (size < len) { s += T.substr(size, len-size); } + s[4] = s[7] = s[13] = s[16] = '/'; + s[10] = ' '; + std::tm t; + std::stringstream ss(s); + ss >> std::get_time(&t, "%Y/%m/%d %H/%M/%S"); + auto tp = chrono::system_clock::from_time_t(std::mktime(&t)); + return chrono::duration_cast(tp.time_since_epoch()).count(); } -string Utils::timeStr(std::string fmt /*= "%Y-%m-%dT%H:%M:%S"*/) +string Utils::timeStr(int64_t ts, std::string fmt /*= "%Y-%m-%dT%H:%M:%S"*/) { - auto t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + time_t t; + if (ts == 0) + { + t = chrono::system_clock::to_time_t(chrono::system_clock::now()); + } + else + { + auto tp = chrono::time_point(chrono::seconds(ts)); + t = chrono::system_clock::to_time_t(tp); + } std::stringstream ss; ss << std::put_time(std::localtime(&t), fmt.c_str()); return ss.str(); @@ -212,7 +239,7 @@ string Utils::timeStr(std::string fmt /*= "%Y-%m-%dT%H:%M:%S"*/) int64_t Utils::date() { // 获取当前时间戳 - std::time_t t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + std::time_t t = chrono::system_clock::to_time_t(chrono::system_clock::now()); // 转换为本地时间结构体,设置时分秒为0 std::tm* tmlocal = localtime(&t); @@ -220,27 +247,27 @@ int64_t Utils::date() tmlocal->tm_min = 0; tmlocal->tm_sec = 0; - auto tp = std::chrono::time_point_cast(std::chrono::system_clock::from_time_t(mktime(tmlocal))); + auto tp = chrono::time_point_cast(chrono::system_clock::from_time_t(mktime(tmlocal))); return tp.time_since_epoch().count(); } -string Utils::dateStr(std::string fmt /*= "%Y-%m-%d %H:%M:%S"*/) +string Utils::dateStr(int64_t ts, std::string fmt /*= "%Y-%m-%d %H:%M:%S"*/) { - return Utils::timeStr(fmt).substr(0, 10); + return Utils::timeStr(ts, fmt).substr(0, 10); } string Utils::timeStrMS(std::string fmt /*= "%Y-%m-%dT%H:%M:%S"*/) { - auto tpNow = std::chrono::system_clock::now(); - auto time = std::chrono::system_clock::to_time_t(tpNow); + auto tpNow = chrono::system_clock::now(); + auto time = chrono::system_clock::to_time_t(tpNow); std::stringstream ss; ss << std::put_time(std::localtime(&time), fmt.c_str()); auto epoch = tpNow.time_since_epoch(); - auto tms = std::chrono::duration_cast(epoch); - auto tseconds = std::chrono::duration_cast(epoch); + auto tms = chrono::duration_cast(epoch); + auto tseconds = chrono::duration_cast(epoch); ss << "." << std::setfill('0') << std::setw(3) << (tms - tseconds).count(); return ss.str(); } @@ -257,9 +284,9 @@ string Utils::timeStrMS(std::string fmt /*= "%Y-%m-%dT%H:%M:%S"*/) // //string Utils::time_to_string(int64_t dt, std::string fmt /*= "%Y-%m-%d %H:%M:%S"*/) //{ -// auto ms = std::chrono::milliseconds(dt); -// auto tp = std::chrono::time_point(ms); -// time_t t = std::chrono::system_clock::to_time_t(tp); +// auto ms = chrono::milliseconds(dt); +// auto tp = chrono::time_point(ms); +// time_t t = chrono::system_clock::to_time_t(tp); // std::stringstream ss; // ss << std::put_time(std::localtime(&t), fmt.c_str()); // return ss.str(); @@ -270,7 +297,7 @@ string Utils::timeStrMS(std::string fmt /*= "%Y-%m-%dT%H:%M:%S"*/) //{ // if (dt.empty()) // { -// return std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); +// return chrono::time_point_cast(chrono::system_clock::now()).time_since_epoch().count(); // } // else // { @@ -303,9 +330,9 @@ string Utils::timeStrMS(std::string fmt /*= "%Y-%m-%dT%H:%M:%S"*/) // std::tm t {}; // ss >> std::get_time(&t, fmt.c_str()); // t.tm_hour += zone; -// auto tp = std::chrono::system_clock::from_time_t(std::mktime(&t)); -// return std::chrono::duration_cast(tp.time_since_epoch()).count(); -// //return std::chrono::duration_cast(tp.time_since_epoch()).count(); +// auto tp = chrono::system_clock::from_time_t(std::mktime(&t)); +// return chrono::duration_cast(tp.time_since_epoch()).count(); +// //return chrono::duration_cast(tp.time_since_epoch()).count(); // } //} // @@ -346,11 +373,11 @@ string Utils::timeStrMS(std::string fmt /*= "%Y-%m-%dT%H:%M:%S"*/) void Utils::sleep_ms(int ms) { // 计算时间间隔: - //auto start = std::chrono::high_resolution_clock::now(); - //auto end = std::chrono::high_resolution_clock::now(); - //std::chrono::duration elapsed = end - start; + //auto start = chrono::high_resolution_clock::now(); + //auto end = chrono::high_resolution_clock::now(); + //chrono::duration elapsed = end - start; //int64_t t = elapsed.count(); - std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + std::this_thread::sleep_for(chrono::milliseconds(ms)); } @@ -445,3 +472,36 @@ void Utils::split(string buf, string c, vector& res) tmp = tmp.substr(pos + c.size()); } } + +std::string Utils::readFile(std::string filename) +{ + std::filesystem::path filePath = std::filesystem::u8path(filename); + //std::locale::global(locale(""));//将全局区域设为操作系统默认区域 + std::ifstream ifs(filePath, std::ios::binary); + //std::locale::global(locale("C"));//还原全局区域设定 + + if (ifs.is_open()) + { + // 将文件指针移动到末尾获取文件大小 + ifs.seekg(0, std::ios::end); + std::streamsize size = ifs.tellg(); + ifs.seekg(0, std::ios::beg); + + std::string buf(size, '\0'); + ifs.read(&buf[0], size); + return buf; + } + else + { + std::cout << "error" << std::endl; + return ""; + } +} + + +std::string Utils::toHexStr(int64_t val) +{ + std::stringstream ss; + ss << "0x" << std::uppercase << setw(4) << setfill('0') << std::hex << val; + return ss.str(); +} \ No newline at end of file diff --git a/src/common/Utils.h b/src/common/Utils.h index d0ba2ae..c90b588 100644 --- a/src/common/Utils.h +++ b/src/common/Utils.h @@ -40,13 +40,13 @@ public: } // 获取当前时间的时间戳(毫秒) - static int64_t time(); + static int64_t time(std::string s=""); // 获取当前时间的格式字符串 - static string timeStr(std::string fmt = "%Y-%m-%d %H:%M:%S"); + static string timeStr(int64_t ts=0, std::string fmt = "%Y-%m-%d %H:%M:%S"); // 获取当前日期的时间戳(毫秒) static int64_t date(); // 获取当前日期的格式字符串 - static string dateStr(std::string fmt = "%Y-%m-%d %H:%M:%S"); + static string dateStr(int64_t ts = 0, std::string fmt = "%Y-%m-%d %H:%M:%S"); static string timeStrMS(std::string fmt = "%Y-%m-%d %H:%M:%S"); //static string timeStr(int64_t ts, std::string fmt = "%Y-%m-%d %H:%M:%S"); @@ -83,6 +83,10 @@ public: static int random(int min, int max); static void split(string buf, string c, vector& res); + + static std::string readFile(std::string filename); + + static std::string toHexStr(int64_t val); }; class TimeTick diff --git a/src/database/Dao.cpp b/src/database/Dao.cpp index c2b1a62..46bf095 100644 --- a/src/database/Dao.cpp +++ b/src/database/Dao.cpp @@ -88,7 +88,7 @@ static Errcode QueryCount(DaoEntity& dao, std::string sqlFrom, int& count) { std::vector result; int ret = dao.exec("SELECT COUNT(*) count " + sqlFrom, result); - if (ret != 0) + if (ret == 0) { count = (result.size() > 0) ? result[0].get("count") : 0; } @@ -110,8 +110,9 @@ static Errcode QueryPagination(std::string sqlFields, std::string sqlCondition, return err; } + if (page.index < 1) page.index = 1; page.total = count; - std::string sql = "SELECT " + sqlFields + " " + sqlCondition + DAO::sqlPageLimit(page.index, page.size); + std::string sql = "SELECT " + sqlFields + " " + sqlCondition + DAO::sqlPageLimit(page.index -1, page.size); int ret = dao.exec(sql, result); return Errcode(ret); } @@ -555,4 +556,11 @@ Errcode DAO::queryPolicyTypeDef(std::shared_ptr dao, vector& { std::string sql = "SELECT * FROM " + DMDefPolicyType::TABLENAME + ";"; return DAO::exec(dao, sql, result); +} + +Errcode DAO::insertRuntimeData(std::shared_ptr dao, Fields& fields) +{ + if (!dao) { dao = DaoEntity::create("history1"); } + int ret = dao->duplicateUpdate(fields, {"value"}); + return Errcode(ret); } \ No newline at end of file diff --git a/src/database/Dao.h b/src/database/Dao.h index c5889e5..e8ecfd0 100644 --- a/src/database/Dao.h +++ b/src/database/Dao.h @@ -117,4 +117,6 @@ public: static Errcode queryWorkModeDef(std::shared_ptr dao, vector& result); static Errcode queryPolicyTypeDef(std::shared_ptr dao, vector& result); + + static Errcode insertRuntimeData(std::shared_ptr dao, Fields& fields); }; \ No newline at end of file diff --git a/src/database/DataModelDef.h b/src/database/DataModelDef.h index f28dbd0..a4b4ab3 100644 --- a/src/database/DataModelDef.h +++ b/src/database/DataModelDef.h @@ -89,8 +89,10 @@ namespace DMStation const string TEL = "tel"; const string CAPACITY = "capacity"; const string STATUS = "status"; - const string WORK_MODE_ID = "work_mode_id"; + const string WORK_MODE = "work_mode"; const string POLICY_ID = "policy_id"; + const string CODE = "code"; + const string ATTR = "attr"; } namespace DMDefDeviceType @@ -178,4 +180,14 @@ namespace DMStatStation const string CHARGE_ELECT = "charge_elect"; const string CHARGE_NUM = "charge_num"; const string CHARGE_NUM_ERR = "charge_num_err"; -} \ No newline at end of file +} + +namespace DMHistory1 +{ + const string TABLENAME = "history1"; + const string DT = "dt"; + const string STATION_ID = "station_id"; + const string DEVICE_ID = "device_id"; + const string DATATYPE = "datatype"; + const string VALUE = "value"; +} diff --git a/src/main.cpp b/src/main.cpp index 15eba99..ffa931a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,108 +20,150 @@ #include "rlsocket.h" #include "common/Spdlogger.h" - - - +#include "database/DaoEntity.h" #include #include #include +#include +#include +#include +#include "DataStruct.h" #define wsa rlwsa -int main(int argc, char** argv) +void rlSocketTest() { + rlwsa(); + rlSocket socket("127.0.0.1", 19801, 1); + int ret = socket.connect(); + std::string s1 = "helloworld"; + socket.write(s1.c_str(), s1.size()); + + std::vector buf(1024, 0); + while (true) { - NJsonNode jsonroot; - NJson::parse(R"({"name": "Alice", "age": 25, "data":[["1","1","1"],["1","1","1"],["1","1","1"]]})", jsonroot); + int len = socket.read(&buf[0], 1, 0); + if (len > 0) + { + std::cout << "===>>> " << std::string(buf.begin(), buf.end()); + } + } +} - std::vector> v1; - NJson::read>>(jsonroot, "data", v1); +void memberJsonTest() +{ + std::string fromJson = "fromJson(const std::string& str) {\nNJsonNode jsonroot;\nauto ret = NJson::parse(str, jsonroot);\nif (!ret) { return; }\n"; + std::string toJson = "toJson() {\nNJsonNode jsonroot;"; - std::vector> vec = { - {"1", "1", "1", "1", "1", "1", "1", "1"}, - {"1", "1", "1", "1", "1", "1", "1", "1"}, - {"1", "1", "1", "1", "1", "1", "1", "1"}, - {"1", "1", "1", "1", "1", "1", "1", "1"} - }; - - NJsonNode jsonroot1; - jsonroot1["price_super_peak"] = 0.53; - jsonroot1["price_peak"] = 0.53; - jsonroot1["price_shoulder"] = 0.53; - jsonroot1["price_off_peak"] = 0.53; - jsonroot1["periods"] = vec; - - std::cout << jsonroot1.dump() << std::endl; + std::ifstream ifs("", std::ios::in); + std::string line; + std::string cpp = "#include \"DataStruct.h\"\n\n"; + if (ifs.is_open()) + { + std::string s1; + std::string s2; + while (std::getline(ifs, line)) + { + int pos = line.find("struct"); + if (pos != std::string::npos) + { + std::string className = line.substr(pos+7); + s1 = "void " + className + "::" + fromJson; + s2 = "}\nstd::string " + className + "::" + toJson; + } + else + { + std::string key; + pos = line.find("uint"); + if (pos != std::string::npos) { key = line.substr(pos+9); } + if (!key.empty()) + { + pos = key.find(";"); + if (pos != std::string::npos) { key = key.substr(0, pos); } + } + if (!key.empty()) + { + s1 += ("NJson::read(jsonroot, \"" + key + "\", " + key + ");\n"); + s2 += ("jsonroot[\"" + key + "\"] = " + key + ";\n"); + } + } + } + ifs.close(); } + + //std::string strTmp = R"()"; + //std::vector vecTmp; + //Utils::split(strTmp, "\n", vecTmp); + + //std::string from = "void fromJson(const std::string& str) {\nNJsonNode jsonroot;\nauto ret = NJson::parse(str, jsonroot);\nif (!ret) { return; }\n"; + //std::string to = "std::string toJson() {\nNJsonNode jsonroot;"; + //for (auto& item: vecTmp) + //{ + // std::string key; + // int pos = item.find_first_of("_"); + // if (pos != std::string::npos) { key = item.substr(pos+3); } + // pos = key.find_first_of(";"); + // if (pos != std::string::npos) { key = key.substr(0, pos); } + + // if (!key.empty()) + // { + // from += ("NJson::read(jsonroot, \"" + key + "\", " + key + ");\n"); + // to += ("jsonroot[\"" + key + "\"] = " + key + ";\n"); + // } + //} + //from += "}"; + //to += "return jsonroot.dump();\n}"; + //std::cout << from << std::endl; + //std::cout << to << std::endl; +} + +int main(int argc, char** argv) +{ // 设置控制台输出为 UTF-8 编码 SetConsoleOutputCP(CP_UTF8); // 设置控制台输入为 UTF-8 编码(如果需要输入中文) SetConsoleCP(CP_UTF8); Spdlogger::init(spdlog::level::debug, ""); - spdlog::info("[main] start ... ===================================================================================================="); - spdlog::info("spd info"); - spdlog::debug("spd debug"); - spdlog::error("spd error"); + spdlog::info("[main] start ... ======================================================================"); - - //rlwsa(); - //rlSocket socket("127.0.0.1", 19801, 1); - //int ret = socket.connect(); - //std::string s1 = "helloworld"; - //socket.write(s1.c_str(), s1.size()); - - //std::vector buf(1024, 0); - //while (true) - //{ - // int len = socket.read(&buf[0], 1, 0); - // if (len > 0) - // { - // std::cout << "===>>> " << std::string(buf.begin(), buf.end()); - // } - //} - - ////std::cout << Snowflake::instance().getId() << std::endl; - //for (int i = 0; i<=10; ++i) { - // //std::cout << Snowflake::instance().getId() << std::endl; - //} - - - //DaoEntity dao(""); - // - //std::string filename = "assets/html/data中文.txt"; - // - //std::filesystem::path filePath = std::filesystem::u8path("assets/html/data中文.txt"); - ////std::locale::global(locale(""));//将全局区域设为操作系统默认区域 - //std::ifstream ifs(filePath, std::ios::binary); - ////std::locale::global(locale("C"));//还原全局区域设定 - - //if (ifs.is_open()) - //{ - // // 将文件指针移动到末尾获取文件大小 - // ifs.seekg(0, std::ios::end); - // std::streamsize size = ifs.tellg(); - // ifs.seekg(0, std::ios::beg); - - // std::string buf(size, '\0'); - // ifs.read(&buf[0], size); - // std::cout << "文件内容: " << buf << std::endl; - //} - //else - //{ - // std::cout << "error" << std::endl; - //} - - //TcpEntity tcpEntity; - //tcpEntity.setHost("127.0.0.1", 9901, true); - //tcpEntity.setReconnect(5000); - //tcpEntity.start(); + std::cout << Snowflake::instance().getId() << std::endl; + for (int i = 0; i<=10; ++i) { + std::cout << Snowflake::instance().getId() << std::endl; + } // 运行后台服务 Application::instance().init(); + { + //REGInfo reg; + //std::string s = "BMS(电池堆)通信状态 R uint16 \"0:正常1:故障\" bit位从低到高分别对应1~16 0x2001"; + //int pos; + //if (std::string::npos != (pos = s.find("\t0x"))) + //{ + // reg.name = s.substr(pos+1); + // s = s.substr(0, pos); + // std::cout << reg.name << std::endl; + //} + //if (std::string::npos != (pos = s.find("\t"))) + //{ + // reg.remark = s.substr(0, pos); + // s = s.substr(pos+1); + // std::cout << reg.remark << std::endl; + //} + //if (std::string::npos != (pos = s.find("uint"))) + //{ + // std::string bytename = s.substr(pos, 6); + // std::cout << bytename << std::endl; + // s = s.substr(pos+6); + //} + //std::cout << s << std::endl; + //std::cout << s << std::endl; + + } + + // 运行QT主窗口 //QApplication qapp(argc, argv); //MainWindow mainWin; diff --git a/src/protocol/HttpEntity.cpp b/src/protocol/HttpEntity.cpp index 151376a..f93f148 100644 --- a/src/protocol/HttpEntity.cpp +++ b/src/protocol/HttpEntity.cpp @@ -6,8 +6,17 @@ #include "app/Application.h" #include "app/AppData.h" #include "app/Config.h" +#include "app/Station.h" -static NJsonNode FieldsToJsonArray(std::vector vecFields) +static void FieldsToJson(Fields& fields, NJsonNode& json) +{ + for (auto& item : fields.map()) + { + json[item.first] = item.second; + } +} + +static NJsonNode FieldsToJsonArray(std::vector& vecFields) { NJsonNode jsonnode = NJsonNode::array(); for (auto& fields : vecFields) @@ -24,13 +33,45 @@ static NJsonNode FieldsToJsonArray(std::vector vecFields) static void GetRequestParam(const httplib::Request& req, const std::vector& vecKeys, Fields& fields) { - for (auto& key : vecKeys) + + if (req.method == "GET") { - if (req.has_param(key)) + for (auto& key : vecKeys) { - fields.set(key, req.get_param_value(key)); + if (req.has_param(key)) + { + fields.set(key, req.get_param_value(key)); + } } } + else if (req.method == "POST") + { + NJsonNode json; + NJson::parse(req.body, json); + for (auto& key : vecKeys) + { + if (json.contains(key)) { + + switch (json[key].type()) + { + case NJsonNode::value_t::string: { fields.set(key, json[key].get()); } break; + case NJsonNode::value_t::boolean: { fields.set(key, json[key].get()); } break; + case NJsonNode::value_t::number_integer: { fields.set(key, json[key].get()); } break; + case NJsonNode::value_t::number_unsigned: { fields.set(key, json[key].get()); } break; + case NJsonNode::value_t::number_float: { fields.set(key, json[key].get()); } break; + case NJsonNode::value_t::null: {} break; + case NJsonNode::value_t::object: {} break; + case NJsonNode::value_t::array: {} break; + case NJsonNode::value_t::binary: {} break; + case NJsonNode::value_t::discarded: {} break; + default: + break; + } + + } + } + } + } class HttpHelper @@ -77,86 +118,106 @@ struct HandlerOptions } }; -static std::map g_mapHttpHandler = +static std::map g_mapHttpHandlerGet = { {"/login", HandlerOptions(&HttpEntity::login, {DMUser::ACCOUNT, DMUser::PASSWD})}, - {"/queryUserList", HandlerOptions(&HttpEntity::queryUserList, {"token"})}, - {"/insertUser", HandlerOptions(&HttpEntity::insertUser, {"token", DMUser::ACCOUNT})}, - {"/updateUser", HandlerOptions(&HttpEntity::updateUser, {"token", DMUser::USER_ID})}, - {"/deleteUser", HandlerOptions(&HttpEntity::deleteUser, {"token", DMUser::USER_ID})}, + {"/queryUserList", HandlerOptions(&HttpEntity::queryUserList, {})}, + {"/deleteUser", HandlerOptions(&HttpEntity::deleteUser, { DMUser::USER_ID})}, - {"/queryPermissionList", HandlerOptions(&HttpEntity::queryPermissionList, {"token"})}, - {"/insertPermission", HandlerOptions(&HttpEntity::insertPermission, {"token", DMPermission::NAME})}, - {"/updatePermission", HandlerOptions(&HttpEntity::updatePermission, {"token", DMPermission::PERMISSION_ID})}, - {"/deletePermission", HandlerOptions(&HttpEntity::deletePermission, {"token", DMPermission::PERMISSION_ID})}, + {"/queryPermissionList", HandlerOptions(&HttpEntity::queryPermissionList, {})}, + {"/deletePermission", HandlerOptions(&HttpEntity::deletePermission, { DMPermission::PERMISSION_ID})}, - {"/queryRoleList", HandlerOptions(&HttpEntity::queryRoleList, {"token"})}, - {"/insertRole", HandlerOptions(&HttpEntity::insertRole, {"token", DMRole::NAME})}, - {"/updateRole", HandlerOptions(&HttpEntity::updateRole, {"token", DMRole::ROLE_ID})}, - {"/deleteRole", HandlerOptions(&HttpEntity::deleteRole, {"token", DMRole::ROLE_ID})}, + {"/queryRoleList", HandlerOptions(&HttpEntity::queryRoleList, {})}, + {"/deleteRole", HandlerOptions(&HttpEntity::deleteRole, { DMRole::ROLE_ID})}, - {"/queryStationList", HandlerOptions(&HttpEntity::queryStationList, {"token"})}, - {"/insertStation", HandlerOptions(&HttpEntity::insertStation, {"token", DMStation::NAME})}, - {"/updateStation", HandlerOptions(&HttpEntity::updateStation, {"token", DMStation::STATION_ID})}, - {"/deleteStation", HandlerOptions(&HttpEntity::deleteStation, {"token", DMStation::STATION_ID})}, - - {"/queryDeviceList", HandlerOptions(&HttpEntity::queryDeviceList, {"token"})}, - {"/insertDevice", HandlerOptions(&HttpEntity::insertDevice, {"token", DMDevice::NAME})}, - {"/updateDevice", HandlerOptions(&HttpEntity::updateDevice, {"token", DMDevice::DEVICE_ID})}, - {"/deleteDevice", HandlerOptions(&HttpEntity::deleteDevice, {"token", DMDevice::DEVICE_ID})}, - {"/queryDevicTypeDef", HandlerOptions(&HttpEntity::queryDevicTypeDef, {"token"})}, - - {"/queryPolicyList", HandlerOptions(&HttpEntity::queryPolicyList, {"token"})}, - {"/insertPolicy", HandlerOptions(&HttpEntity::insertPolicy, {"token", DMPolicy::NAME})}, - {"/updatePolicy", HandlerOptions(&HttpEntity::updatePolicy, {"token", DMPolicy::POLICY_ID})}, - {"/deletePolicy", HandlerOptions(&HttpEntity::deletePolicy, {"token", DMPolicy::POLICY_ID})}, + {"/queryStationList", HandlerOptions(&HttpEntity::queryStationList, {})}, + {"/deleteStation", HandlerOptions(&HttpEntity::deleteStation, { DMStation::STATION_ID})}, - {"/querySystemLogList", HandlerOptions(&HttpEntity::querySystemLogList, {"token"})}, + {"/queryStationInfo", HandlerOptions(&HttpEntity::queryStationInfo, { DMStation::STATION_ID})}, + {"/queryStationRuntime", HandlerOptions(&HttpEntity::queryStationRuntime, { DMStation::STATION_ID})}, - {"/queryAlertLogList", HandlerOptions(&HttpEntity::queryAlertLogList, {"token"})}, + {"/queryDeviceList", HandlerOptions(&HttpEntity::queryDeviceList, {})}, + {"/deleteDevice", HandlerOptions(&HttpEntity::deleteDevice, { DMDevice::DEVICE_ID})}, + {"/queryDevicTypeDef", HandlerOptions(&HttpEntity::queryDevicTypeDef, {})}, + + {"/queryPolicyList", HandlerOptions(&HttpEntity::queryPolicyList, {})}, - {"/queryPredictionDetail", HandlerOptions(&HttpEntity::queryPredictionDetail, {"token"})}, + + {"/deletePolicy", HandlerOptions(&HttpEntity::deletePolicy, { DMPolicy::POLICY_ID})}, + + {"/querySystemLogList", HandlerOptions(&HttpEntity::querySystemLogList, {})}, - {"/queryStatSystem", HandlerOptions(&HttpEntity::queryStatSystem, {"token"})}, - {"/queryStatTotal", HandlerOptions(&HttpEntity::queryStatTotal, {"token"})}, - {"/queryStatDayList", HandlerOptions(&HttpEntity::queryStatDayList, {"token"})}, + {"/queryAlertLogList", HandlerOptions(&HttpEntity::queryAlertLogList, {})}, + + {"/queryPredictionDetail", HandlerOptions(&HttpEntity::queryPredictionDetail, {})}, + + {"/queryStatSystem", HandlerOptions(&HttpEntity::queryStatSystem, {})}, + {"/queryStatTotal", HandlerOptions(&HttpEntity::queryStatTotal, {})}, + {"/queryStatStation", HandlerOptions(&HttpEntity::queryStatStation, {})}, + {"/queryStatDayList", HandlerOptions(&HttpEntity::queryStatDayList, {})}, + + {"/queryEnvironment", HandlerOptions(&HttpEntity::queryEnvironment, { "station_id"})}, //{"/insert", HandlerOptions(&HttpEntity::insert, {})}, //{"/update", HandlerOptions(&HttpEntity::update, {})}, //{"/delete", HandlerOptions(&HttpEntity::delete, {})}, }; +static std::map g_mapHttpHandlerPost +{ + {"/insertUser", HandlerOptions(&HttpEntity::insertUser, { DMUser::ACCOUNT})}, + {"/updateUser", HandlerOptions(&HttpEntity::updateUser, { DMUser::USER_ID})}, + + {"/insertPermission", HandlerOptions(&HttpEntity::insertPermission, { DMPermission::NAME})}, + {"/updatePermission", HandlerOptions(&HttpEntity::updatePermission, { DMPermission::PERMISSION_ID})}, + + {"/insertRole", HandlerOptions(&HttpEntity::insertRole, { DMRole::NAME})}, + {"/updateRole", HandlerOptions(&HttpEntity::updateRole, { DMRole::ROLE_ID})}, + + {"/insertStation", HandlerOptions(&HttpEntity::insertStation, { DMStation::NAME})}, + {"/updateStation", HandlerOptions(&HttpEntity::updateStation, { DMStation::STATION_ID})}, + + {"/insertDevice", HandlerOptions(&HttpEntity::insertDevice, { DMDevice::NAME})}, + {"/updateDevice", HandlerOptions(&HttpEntity::updateDevice, { DMDevice::DEVICE_ID})}, + + {"/insertPolicy", HandlerOptions(&HttpEntity::insertPolicy, { DMPolicy::NAME})}, + {"/updatePolicy", HandlerOptions(&HttpEntity::updatePolicy, { DMPolicy::POLICY_ID})}, +}; + +bool CheckHttpToken(const httplib::Request& req) +{ + // 验证token + std::string token = req.get_param_value("token"); + if (!token.empty()) + { + User user = Application::data().getUser(token); + if (!user.userId.empty()) + { + return true; + } + } + return false; +} + + HttpEntity::HttpEntity() { bool useToken = Config::option.http.useToken; - for (auto& item : g_mapHttpHandler) + for (auto& item : g_mapHttpHandlerGet) { std::string name = item.first; HandlerOptions& handler = item.second; this->httpsvr.Get(name, [=, &handler](const httplib::Request& req, httplib::Response& resp) { spdlog::info("[http] request: {}", name); - NJsonNode json; Errcode errcode = Errcode::OK; - if (name != "/login" && useToken) { - // 验证token - std::string token = req.get_param_value("token"); - if (token.empty()) - { - errcode = Errcode::ERR_TOKEN; - } - else - { - User user = Application::data().getUser(token); - if (user.userId.empty()) - { - errcode = Errcode::ERR_TOKEN; - } - } + bool ret = CheckHttpToken(req); + errcode = ret ? Errcode::OK : Errcode::ERR_TOKEN; } + NJsonNode json; std::string errmsg; if (errcode == Errcode::OK) { @@ -169,7 +230,38 @@ HttpEntity::HttpEntity() errcode = (this->*(handler.func))(req, resp, json); } } + json["errcode"] = errcode; + json["errmsg"] = ErrcodeStr(errcode) + (errmsg.empty() ? "" : (":"+errmsg)); + resp.set_content(json.dump(), "text/plain; charset=utf-8"); + resp.status = 200; + }); + } + for (auto& item : g_mapHttpHandlerPost) + { + std::string name = item.first; + HandlerOptions& handler = item.second; + this->httpsvr.Post(name, [=](const httplib::Request& req, httplib::Response& resp) + { + Errcode errcode = Errcode::OK; + std::string errmsg; + if (name != "/login" && useToken) + { + bool ret = CheckHttpToken(req); + errcode = ret ? Errcode::OK : Errcode::ERR_TOKEN; + } + + if (errcode == Errcode::OK) + { + NJsonNode jsonparam; + bool ret = NJson::parse(req.body, jsonparam); + if (ret) + { + errcode = (this->*(handler.func))(req, resp, jsonparam); + } + } + + NJsonNode json; json["errcode"] = errcode; json["errmsg"] = ErrcodeStr(errcode) + (errmsg.empty() ? "" : (":"+errmsg)); resp.set_content(json.dump(), "text/plain; charset=utf-8"); @@ -383,6 +475,61 @@ Errcode HttpEntity::deleteStation(const httplib::Request& req, httplib::Response return DAO::remove(NULL, DMStation::TABLENAME, primaryKey, req.get_param_value(primaryKey)); }; +Errcode HttpEntity::queryStationInfo(const httplib::Request& req, httplib::Response& resp, NJsonNode& json) +{ + // 查询场站的基础配置信息 + std::string stationId = req.get_param_value("station_id"); + if (stationId.empty()) + { + return Errcode::ERR_PARAM; + } + + std::string sql = "SELECT * FROM " + DMStation::TABLENAME + " WHERE station_id=" + stationId + ";"; + std::vector result; + Errcode err = DAO::exec(NULL, sql, result); + if (err != Errcode::OK) + { + return err; + } + if (result.size() == 0) + { + return Errcode::ERR_DATA_NUL; + } + auto& fields = result[0]; + + NJsonNode jsondata; + std::string attr = fields.remove(DMStation::ATTR); + NJson::parse(attr, jsondata); + + FieldsToJson(fields, jsondata); + json["data"] = jsondata; + return Errcode::OK; + + // work_mode: 运行模式: + // capacity: 储能容量: + + // {"batttey_type": "磷酸铁锂", "cooling_type":"风冷", "voltage_rated":"300", "power_rated": "1500"} + // batttey_type: 电池类型: + // cooling_type: 冷却方式: + // voltage_rated: 电池额定电压: + // power_rated: PCS额定功率 +} +Errcode HttpEntity::queryStationData(const httplib::Request& req, httplib::Response& resp, NJsonNode& json) +{ + // 温度, 电压、电流、功率、功率因数、 + NJsonNode jsondata; + jsondata["voltage"] = Utils::toStr(200.32); + jsondata["current"] = Utils::toStr(20.56); + jsondata["power"] = Utils::toStr(200.32); + jsondata["powerFactor"] = Utils::toStr(1); + jsondata["envTemp"] = Utils::toStr(200.32); + jsondata["envhum"] = Utils::toStr(200.32); + jsondata["aircStatus"] = Utils::toStr(1); + jsondata["coolingStatus"] = Utils::toStr(0); + + json["data"] = jsondata; + return Errcode::OK; +} Errcode HttpEntity::queryDeviceList(const httplib::Request& req, httplib::Response& resp, NJsonNode& json) { @@ -513,40 +660,70 @@ Errcode HttpEntity::queryStatSystem(const httplib::Request& req, httplib::Respon { auto& appdata = Application::data(); - json["launch_date"] = "2025-01-01"; //: 系统上线启用日期,格式:yyyy-mm-dd - json["income_total"] = "0.00"; // : 累计收益(元),精度0.01 - json["station_num"] = Utils::toStr(appdata.getStationCount()); // : 能源站数量 - json["storage_device_num "] = Utils::toStr(appdata.getStationCount()); //: 储能设备数量 - json["solar_device_num"] = "0"; // : 光伏设备数量 - json["capacity_total"] = "0.000"; // : 储能总容量(kWh),精度0.001 - json["elect_gen"] = "0.000"; // : 发电总电量(kWh),精度0.001 - json["elect_grid"] = "0.000"; // : 入网种电量(kWh),精度0.001 - json["storage_elect_in"] = "0.000"; // : 储能充电总电量(kWh),精度0.001 - json["storage_elect_out"] = "0.000"; // : 储能放电总电量(kWh),精度0.001 + NJsonNode jsondata; + jsondata["launch_date"] = "2025-01-01"; //: 系统上线启用日期,格式:yyyy-mm-dd + jsondata["income_total"] = std::to_string(Utils::random(100, 200)); // : 累计收益(元),精度0.01 + jsondata["station_num"] = Utils::toStr(appdata.getStationCount()); // : 能源站数量 + jsondata["storage_device_num "] = Utils::toStr(appdata.getStationCount()); //: 储能设备数量 + jsondata["solar_device_num"] = "0"; // : 光伏设备数量 + jsondata["capacity_total"] = std::to_string(Utils::random(100, 200)); // : 储能总容量(kWh),精度0.001 + jsondata["solar_elect_gen"] = std::to_string(Utils::random(100, 200)); // : 发电总电量(kWh),精度0.001 + jsondata["solar_elect_grid"] = std::to_string(Utils::random(100, 200)); // : 入网种电量(kWh),精度0.001 + jsondata["storage_elect_in"] = std::to_string(Utils::random(100, 200)); // : 储能充电总电量(kWh),精度0.001 + jsondata["storage_elect_out"] = std::to_string(Utils::random(100, 200)); // : 储能放电总电量(kWh),精度0.001 + json["data"] = jsondata; return Errcode::OK; } - Errcode HttpEntity::queryStatTotal(const httplib::Request& req, httplib::Response& resp, NJsonNode& json) { std::string station_id = req.get_param_value("station_id"); std::string category = req.get_param_value("category"); - json["dt"] = "2025-01-01"; //日期 - json["storage_elect_in"] = "123.123"; //储能充电电量(kWh),精度:0.001 - json["storage_elect_out"] = "123.123"; //储能放电电量(kWh),精度:0.001 - json["storage_num_in"] = "1"; //储能设备充电次数 - json["storage_num_out"] = "1"; //储能设备放电次数 - json["storage_num_err"] = "1"; //储能设备故障次数 - json["solar_elect_gen"] = "123.123"; //光伏发电电量(kWh),精度:0.001 - json["solar_elect_grid"] = "123.123"; //光伏入网电量(kWh),精度:0.001 - json["solar_num_err"] = "1"; //光伏设备故障次数 - json["charge_elect"] = "123.123"; //充电设备充电电量(kWh),精度:0.001 - json["charge_num"] = "1"; //充电设备充电次数 - json["charge_num_err"] = "1"; //充电设备故障次数 - json["income_elect"] = ""; //发电收益(元),精度:0.01 - json["income_charge"] = ""; //充电收益(元),精度:0.01 - json["usage"] = ""; + NJsonNode jsondata; + jsondata["station_id"] = "1"; + jsondata["launch_date"] = "2025-01-01"; //场站上线日期 + jsondata["usage_rate"] = "12"; + jsondata["storage_elect_in"] = "123.123"; //储能充电电量(kWh),精度:0.001 + jsondata["storage_elect_out"] = "123.123"; //储能放电电量(kWh),精度:0.001 + jsondata["storage_num_in"] = "1"; //储能设备充电次数 + jsondata["storage_num_out"] = "1"; //储能设备放电次数 + jsondata["storage_num_err"] = "1"; //储能设备故障次数 + jsondata["solar_elect_gen"] = "123.123"; //光伏发电电量(kWh),精度:0.001 + jsondata["solar_elect_grid"] = "123.123"; //光伏入网电量(kWh),精度:0.001 + jsondata["solar_num_err"] = "1"; //光伏设备故障次数 + jsondata["charge_elect"] = "123.123"; //充电设备充电电量(kWh),精度:0.001 + jsondata["charge_num"] = "1"; //充电设备充电次数 + jsondata["charge_num_err"] = "1"; //充电设备故障次数 + jsondata["income_elect"] = "123.123"; //发电收益(元),精度:0.01 + jsondata["income_charge"] = "123.123"; //充电收益(元),精度:0.01 + + json["data"] = jsondata; + return Errcode::OK; +} +Errcode HttpEntity::queryStatStation(const httplib::Request& req, httplib::Response& resp, NJsonNode& json) +{ + std::string station_id = req.get_param_value("station_id"); + std::string category = req.get_param_value("category"); + NJsonNode jsondata; + jsondata["station_id"] = "1"; + jsondata["launch_date"] = "2025-01-01"; //场站上线日期 + jsondata["usage_rate"] = "12"; + jsondata["storage_elect_in"] = "123.123"; //储能充电电量(kWh),精度:0.001 + jsondata["storage_elect_out"] = "123.123"; //储能放电电量(kWh),精度:0.001 + jsondata["storage_num_in"] = "1"; //储能设备充电次数 + jsondata["storage_num_out"] = "1"; //储能设备放电次数 + jsondata["storage_num_err"] = "1"; //储能设备故障次数 + jsondata["solar_elect_gen"] = "123.123"; //光伏发电电量(kWh),精度:0.001 + jsondata["solar_elect_grid"] = "123.123"; //光伏入网电量(kWh),精度:0.001 + jsondata["solar_num_err"] = "1"; //光伏设备故障次数 + jsondata["charge_elect"] = "123.123"; //充电设备充电电量(kWh),精度:0.001 + jsondata["charge_num"] = "1"; //充电设备充电次数 + jsondata["charge_num_err"] = "1"; //充电设备故障次数 + jsondata["income_elect"] = "123.123"; //发电收益(元),精度:0.01 + jsondata["income_charge"] = "123.123"; //充电收益(元),精度:0.01 + + json["data"] = jsondata; return Errcode::OK; } @@ -554,12 +731,124 @@ Errcode HttpEntity::queryStatDayList(const httplib::Request& req, httplib::Respo { std::string station_id = req.get_param_value("station_id"); std::string category = req.get_param_value("category"); - std::string dt_start = req.get_param_value("dt_start"); - std::string dt_end = req.get_param_value("dt_end"); + std::string dt_start = req.get_param_value("start_date"); + std::string dt_end = req.get_param_value("end_date"); - if (!dt_start.empty() && dt_end.empty()) + int64_t t1 = Utils::time(dt_start)/1000; + int64_t t2 = Utils::time(dt_end)/1000; + + int64_t tMax = t1+ 86400 * 30; + NJsonNode jsondata = NJsonNode::array(); + for (int64_t t = t1; t<=t2 && t<=tMax; t += 86400) { + NJsonNode jnode; + jnode["station_id"] = station_id; + if (!category.empty()) jnode["category"] = category; + jnode["dt"] = Utils::dateStr(t*1000); //日期 + jnode["storage_elect_in"] = std::to_string(Utils::random(100, 200)); //储能充电电量(kWh),精度:0.001 + jnode["storage_elect_out"] = std::to_string(Utils::random(100, 200)); //储能放电电量(kWh),精度:0.001 + jnode["storage_num_in"] = std::to_string(Utils::random(1,5)); //储能设备充电次数 + jnode["storage_num_out"] = std::to_string(Utils::random(1, 5)); //储能设备放电次数 + jnode["storage_num_err"] = std::to_string(Utils::random(1, 5)); //储能设备故障次数 + jnode["solar_elect_gen"] = std::to_string(Utils::random(100, 200)); //光伏发电电量(kWh),精度:0.001 + jnode["solar_elect_grid "] = std::to_string(Utils::random(100, 200)); //光伏入网电量(kWh),精度:0.001 + jnode["solar_num_err"] = std::to_string(Utils::random(1, 5)); //光伏设备故障次数 + jnode["charge_elect"] = std::to_string(Utils::random(100, 200)); //充电设备充电电量(kWh),精度:0.001 + jnode["charge_num"] = std::to_string(Utils::random(1, 5)); //充电设备充电次数 + jnode["charge_num_err"] = std::to_string(Utils::random(1, 5)); //充电设备故障次数 + jnode["income_elect"] = std::to_string(Utils::random(100, 200)); //发电收益(元),精度:0.01 + jnode["income_charge"] = std::to_string(Utils::random(100, 200)); //充电收益(元),精度:0.01 + jnode["usage_rate"] = std::to_string(Utils::random(10, 50)); //利用率 + jsondata.push_back(jnode); } + json["data"] = jsondata; + return Errcode::OK; +} +Errcode HttpEntity::queryEnvironment(const httplib::Request& req, httplib::Response& resp, NJsonNode& json) +{ + std::string stationId = req.get_param_value("station_id"); + auto& appdata = Application::data(); + + auto station = appdata.getStation(Utils::toInt(stationId)); + if (!station) + { + spdlog::error("[http] request queryEnvironment failed, get station info error, station_id={}", stationId); + return Errcode::ERR_PARAM; + } + + NJsonNode jsondata; + + { // 温湿度 + auto& mapTempHumUnit = station->mapTempHumUnit; + NJsonNode nodearray = NJsonNode::array(); + for (auto iter = mapTempHumUnit.begin(); iter!=mapTempHumUnit.end(); iter++) + { + auto& unit = iter->second; + NJsonNode node; + node["pos"] = "#" + std::to_string(iter->first); + node["temp"] = unit.temp; + node["hum"] = unit.hum; + nodearray.push_back(node); + } + jsondata["temp_hum"] = nodearray; + } + { //空调 + auto& mapAircUnit = station->mapAircUnit; + AircUnit unitTmp; + AircUnit* unit = (mapAircUnit.size() > 0) ? &(mapAircUnit[0]) : &unitTmp; + NJsonNode nodearray = NJsonNode::array(); + if (unit) + { + NJsonNode node; + nodearray.push_back({{"pos", "开关"}, {"status", unit->powerOn == 0 ? "关机" : "开机"}}); + nodearray.push_back({{"pos", "启动制冷指令"}, {"status", unit->cooling == 0 ? "启动" : "关闭"}}); + nodearray.push_back({{"pos", "启动送风指令"}, {"status", unit->airSupply == 0 ? "关闭" : "启动"}}); + nodearray.push_back({{"pos", "启动待机指令"}, {"status", unit->standby == 0 ? "关闭" : "启动"}}); + nodearray.push_back({{"pos", "启动加热指令"}, {"status", unit->heating == 0 ? "关闭" : "启动"}}); + nodearray.push_back({{"pos", "传感器故障"}, {"status", unit->sensorAlarm == 0 ? "正常" : "告警"}}); + nodearray.push_back({{"pos", "高低电压告警"}, {"status", unit->voltageAlarm == 0 ? "正常" : "告警"}}); + nodearray.push_back({{"pos", "高低温告警"}, {"status", unit->tempAlarm == 0 ? "正常" : "告警"}}); + nodearray.push_back({{"pos", "高低压告警"}, {"status", unit->pressureAlarm == 0 ? "正常" : "告警"}}); + nodearray.push_back({{"pos", "压缩机告警"}, {"status", unit->compressorAlarm == 0 ? "正常" : "告警"}}); + nodearray.push_back({{"pos", "当前温度"}, {"status", std::to_string(unit->temp) + "℃"}}); + nodearray.push_back({{"pos", "当前湿度"}, {"status", std::to_string(unit->hum) + "%"}}); + } + jsondata["airc"] = nodearray; + } + { // 消防 + static std::map mapFireStatusDef = { {0, "正常"}, {1,"预警"}, {2,"火警"} }; + + auto& mapFire40Unit = station->mapFire40Unit; + NJsonNode nodearray = NJsonNode::array(); + for (auto iter = mapFire40Unit.begin(); iter!=mapFire40Unit.end(); ++iter) + { + NJsonNode node; + node["pos"] = "#" + std::to_string(iter->first); + node["status"] = mapFireStatusDef[iter->second]; // 0:正常 1:预警 2:火警 + nodearray.push_back(node); + } + jsondata["fire40"] = nodearray; + } + { // 冷机 + auto& mapCoolingUnit = station->mapCoolingUnit; + CoolingUnit unitTmp; + CoolingUnit* unit = (mapCoolingUnit.size() > 0) ? &(mapCoolingUnit[0]) : &unitTmp; + NJsonNode nodearray = NJsonNode::array(); + if (unit) + { + NJsonNode node; + nodearray.push_back({{"pos", "开关"}, {"status", unit->powerOn == 0 ? "关机" : "开机"}}); + nodearray.push_back({{"pos", "采样模式"}, {"status", unit->mode == 0 ? "出水温度" : "电芯温度"}}); + nodearray.push_back({{"pos", "制冷状态"}, {"status", unit->cooling == 0 ? "关闭" : "启动"}}); + nodearray.push_back({{"pos", "制热状态"}, {"status", unit->heating == 0 ? "关闭" : "启动"}}); + nodearray.push_back({{"pos", "高温告警"}, {"status", unit->highTemp == 0 ? "正常" : "告警"}}); + nodearray.push_back({{"pos", "低温告警"}, {"status", unit->lowTemp == 0 ? "正常" : "告警"}}); + nodearray.push_back({{"pos", "高压告警"}, {"status", unit->highPressure == 0 ? "正常" : "告警"}}); + nodearray.push_back({{"pos", "低压告警"}, {"status", unit->lowPressure == 0 ? "正常" : "告警"}}); + } + jsondata["cooling"] = nodearray; + } + json["data"] = jsondata; return Errcode::OK; } \ No newline at end of file diff --git a/src/protocol/HttpEntity.h b/src/protocol/HttpEntity.h index eb9fa0c..1f8dd44 100644 --- a/src/protocol/HttpEntity.h +++ b/src/protocol/HttpEntity.h @@ -35,6 +35,9 @@ public: Errcode updateStation(const httplib::Request& req, httplib::Response& resp, NJsonNode& json); Errcode deleteStation(const httplib::Request& req, httplib::Response& resp, NJsonNode& json); + Errcode queryStationInfo(const httplib::Request& req, httplib::Response& resp, NJsonNode& json); + Errcode queryStationData(const httplib::Request& req, httplib::Response& resp, NJsonNode& json); + Errcode queryDeviceList(const httplib::Request& req, httplib::Response& resp, NJsonNode& json); Errcode insertDevice(const httplib::Request& req, httplib::Response& resp, NJsonNode& json); Errcode updateDevice(const httplib::Request& req, httplib::Response& resp, NJsonNode& json); @@ -58,5 +61,8 @@ public: Errcode queryStatSystem(const httplib::Request& req, httplib::Response& resp, NJsonNode& json); Errcode queryStatTotal(const httplib::Request& req, httplib::Response& resp, NJsonNode& json); + Errcode queryStatStation(const httplib::Request& req, httplib::Response& resp, NJsonNode& json); Errcode queryStatDayList(const httplib::Request& req, httplib::Response& resp, NJsonNode& json); + + Errcode queryEnvironment(const httplib::Request& req, httplib::Response& resp, NJsonNode& json); }; \ No newline at end of file diff --git a/src/protocol/MqttEntity.cpp b/src/protocol/MqttEntity.cpp index 2af8534..9fefcbe 100644 --- a/src/protocol/MqttEntity.cpp +++ b/src/protocol/MqttEntity.cpp @@ -4,17 +4,31 @@ #define TIMEOUT 10000L -int MqttClient::init(string addr, string client_id, string username, string password, std::vector vecTopic) +int MqttClient::init(string addr, string clientId, string username, string password) { this->addr = addr; - this->vecTopic = vecTopic; + this->clientId = clientId; + this->vecTopic = { + "up/json/" + clientId + "/EMS_YX", + "up/json/" + clientId + "/EMS_YC", + "up/json/" + clientId + "/EMS_YT", + "up/json/" + clientId + "/PCU_YX", + "up/json/" + clientId + "/PCU_YC", + "up/json/" + clientId + "/PCS_YX", + "up/json/" + clientId + "/PCS_YC", + "up/json/" + clientId + "/BCU_YX", + "up/json/" + clientId + "/BCU_YC", + "up/json/" + clientId + "/BMS_YX", + "up/json/" + clientId + "/BMS_YC", + "up/json/" + clientId + "/MEM_YC", + }; MQTTAsync_connectOptions option = MQTTAsync_connectOptions_initializer; MQTTAsync_message pubmsg = MQTTAsync_message_initializer; int rc {0}; // "tcp://localhost:1883" - rc = MQTTAsync_create(&client, addr.c_str(), client_id.c_str(), MQTTCLIENT_PERSISTENCE_NONE, NULL); + rc = MQTTAsync_create(&client, addr.c_str(), clientId.c_str(), MQTTCLIENT_PERSISTENCE_NONE, NULL); if (rc != MQTTASYNC_SUCCESS) { spdlog::error("[mqtt] MQTTAsync_create error: {}", rc); @@ -42,6 +56,7 @@ int MqttClient::init(string addr, string client_id, string username, string pass if (rc != MQTTASYNC_SUCCESS) { spdlog::error("[mqtt] MQTTAsync_setCallbacks error"); + this->destory(); return rc; } @@ -70,55 +85,43 @@ int MqttClient::init(string addr, string client_id, string username, string pass //MQTTAsync_destroy(&client); } +void MqttClient::destory() +{ + if (client) + { + MQTTAsync_destroy(&client); + client = nullptr; + } +} + struct SubscribInfo { std::function callback; }; -void MqttClient::subscribe(std::vector vecTopics, std::function callback) +void MqttClient::subscribe() { - SubscribInfo* info = new SubscribInfo(); - info->callback = callback; + MQTTAsync_onSuccess* funcSuccess = [](void* context, MQTTAsync_successData* response) + { + spdlog::info("[mqtt] subscribe {} success.", (char*)context); + }; + MQTTAsync_onFailure* funcFailure = [](void* context, MQTTAsync_failureData* response) + { + spdlog::error("[mqtt] subscribe {} failed.", (char*)context); + }; MQTTAsync_responseOptions options = MQTTAsync_responseOptions_initializer; - options.context = info; - options.onSuccess = [](void* context, MQTTAsync_successData* response) - { - spdlog::info("[mqtt] subscribe success."); - SubscribInfo* info = (SubscribInfo*)context; - info->callback(0); - delete info; - - }; - options.onFailure = [](void* context, MQTTAsync_failureData* response) - { - spdlog::error("[mqtt] subscribe failed."); - SubscribInfo* info = (SubscribInfo*)context; - info->callback(-1); - delete info; - }; - - - int count = 3; - char* topicsTmp[] = { - "topic/aa", - "topic/bb", - "topic/cc" - }; - std::vector qosTmp(count, 1); // 为每个主题指定 QoS - - if (count > 0) + options.onSuccess = funcSuccess; + options.onFailure = funcFailure; + for (auto& topic: vecTopic) { - int rc = MQTTAsync_subscribeMany(client, count, topicsTmp, qosTmp.data(), &options); + options.context = topic.data(); + int rc = MQTTAsync_subscribe(client, topic.data(), qos, &options); if (rc != MQTTASYNC_SUCCESS) { - spdlog::error("[mqtt] subscribe failed, err={}", rc); + spdlog::error("[mqtt] subscribe [{},{}] failed, err={}", topic, qos, rc); } } - else - { - delete info; - } } int MqttClient::publish(string topic, string text) @@ -151,18 +154,45 @@ int MqttClient::publish(string topic, string text) void MqttClient::onConnectionLost(char* cause) { this->isConnected = false; + this->destory(); spdlog::error("MQTT connection lost, cause={}", cause); } +std::string GetSubStr(std::string c, std::string& str) +{ + std::string v; + int pos = str.find_first_of("/"); + if (pos != string::npos) + { + v = str.substr(0, pos); + str = str.substr(pos); + } + return v; +} + int MqttClient::onMessageArrived(char* topic, int topicLen, MQTTAsync_message* msg) { + std::string topicStr = topic; int len = msg->payloadlen; - char* payload = (char*)msg->payload; + std::string payload = (char*)msg->payload; spdlog::info("MQTT message arrived: topic=[{},{}], payload len={}, payload msg={}", topic, msg->qos, len, payload); + // <数据方向>/<数据格式>/<厂家ID>/<指合>/<设备标识,上行可选> + std::string direction = GetSubStr("/", topicStr); + std::string datatype = GetSubStr("/", topicStr); + std::string stationId = GetSubStr("/", topicStr); + std::string command = GetSubStr("/", topicStr); + std::string deviceCode = GetSubStr("/", topicStr); + + if (command == "EMS_YX") {} + else if (command == "EMS_YC") {} + else if (command == "PCU_YX") { this->parsePCU_YX(payload); } + else if (command == "PCU_YC") {} + // 必须释放消息内存! MQTTAsync_freeMessage(&msg); MQTTAsync_free(topic); + return 1; // 1表示消息已经处理 } @@ -175,8 +205,9 @@ void MqttClient::onDeliveryComplete(MQTTAsync_token token) void MqttClient::onConnectSuccess( MQTTAsync_successData* resp) { + spdlog::info("[mqtt] connect to {} success.", addr); this->isConnected = true; - //spdlog::info("[mqtt] connect success: {}", addr); + this->subscribe(); //MQTTAsync_responseOptions options = MQTTAsync_responseOptions_initializer; //options.context = this; //options.onSuccess = [](void* context, MQTTAsync_successData* response) @@ -203,63 +234,23 @@ void MqttClient::onConnectSuccess( MQTTAsync_successData* resp) } void MqttClient::onConnectFaiure(MQTTAsync_failureData* resp) { + spdlog::error("[mqtt] connect to {} error.", addr); this->isConnected = false; + this->destory(); } -string MQTT::packEquipmentInfo(mqtt::EquipmentInfo& info) + +void MqttClient::parseEMS_YC(std::string& text) +{ + +} +void MqttClient::parsePCU_YX(std::string& text) { - NJsonNode jsonroot; - jsonroot["EquipmentID"] = info.EquipmentID.c_str(); - jsonroot["ManufacturerID"] = info.ManufacturerID.c_str(); - jsonroot["EquipmentModel"] = info.EquipmentModel.c_str(); - jsonroot["ProductionDate"] = info.ProductionDate.c_str(); - jsonroot["OpenForBusinessDate"] = info.OpenForBusinessDate.c_str(); - jsonroot["EquipmentType"] = info.EquipmentType; - return jsonroot.dump(); } -string MQTT::packSwapEquipmentStatusInfo(string node_id) +string MQTT::packEquipmentInfo() { NJsonNode jsonroot; return jsonroot.dump(); } - - -string MQTT::packNotifyStationInfo() -{ - NJsonNode jsonroot; - return jsonroot.dump(); -} - - -string MQTT::packNotifyAlarm() -{ - NJsonNode jsonroot; - return jsonroot.dump(); -} - -string MQTT::packNotifyChargeStatus() -{ - NJsonNode jsonroot; - return jsonroot.dump(); -} - -string MQTT::packNotifySwapStatus() -{ - NJsonNode jsonroot; - return jsonroot.dump(); -} - -string MQTT::packNotifyChargeOrder() -{ - NJsonNode jsonroot; - return jsonroot.dump(); -} - - -string MQTT::packNotifySwapOrder() -{ - NJsonNode jsonroot; - return jsonroot.dump(); -} \ No newline at end of file diff --git a/src/protocol/MqttEntity.h b/src/protocol/MqttEntity.h index 142d37a..f709a06 100644 --- a/src/protocol/MqttEntity.h +++ b/src/protocol/MqttEntity.h @@ -10,10 +10,10 @@ using namespace std; class MqttClient { public: - int init(string addr, string client_id, string username, string password, std::vector vecTopic); - - void subscribe(std::vector topics, std::function callback); + int init(string addr, string clientId, string username, string password); + void destory(); + void subscribe(); int publish(string topic, string text); void onConnectionLost(char* cause); @@ -23,13 +23,15 @@ public: void onConnectSuccess(MQTTAsync_successData* resp); void onConnectFaiure(MQTTAsync_failureData* resp); + void parseEMS_YC(std::string& text); + void parsePCU_YX(std::string& text); public: + std::string clientId; MQTTAsync client = nullptr; std::vector vecTopic; std::string addr; // "tcp://localhost:1883" int qos {1}; - std::string clientId; bool isConnected {false}; bool isSubscribed {false}; }; @@ -52,317 +54,11 @@ public: #define TOPIC_PCS_YC "up/json/预制舱01/PCS_YC" -#define MQTT_TOPIC_NOTIFY_STATION "notification_stationInfo" // 充(换)电站信息变化推送 -#define MQTT_TOPIC_QUERY_STATION "query_stations_info" // 查询充(换)电站信息 -#define MQTT_TOPIC_NOTIFY_ALARM "notification_alarmInfo" // 告警信息推送 -#define MQTT_TOPIC_NOTIFY_CHARGE_STATUS "notification_connectorStatus" //充电设备状态变化推送 -#define MQTT_TOPIC_NOTIFY_SWAP_STATUS "notification_swapStatus" //换电设备状态变化推送 -#define MQTT_TOPIC_QUERY_STATUS "query_station_status" //查询站内设备接口状态 -#define MQTT_TOPIC_NOTIFY_CHARGE_ORDER "notification_orderInfo" //充电电量信息推送 -#define MQTT_TOPIC_QUERY_ORDER "query_order_info" //查询充电电量信息 -#define MQTT_TOPIC_NOTIFY_SWAP_ORDER "notification_swapInfo" //换电记录信息推送 -#define MQTT_TOPIC_QUERY_SWAP_ORDER "query_swap_info" //查询换电电量信息 - -namespace mqtt -{ - // 充(换)电运营商信息 - struct OperatorInfo - { - string OperatorID; // 运营商ID 组织机构代码 是 字符串 9 字符 - string OperatorName; // 运营商名称 机构全称 是 字符串 <= 64 字符 - string OperatorTel1; // 运营商电话1 运营商客服电话 1 是 字符串 <= 32 字符 - string OperatorTel2; // 运营商电话2 运营商客服电话 2 否 字符串 <= 32 字符 - string OperatorRegAddress; // 运营商注册地址 运营商注册地址 否 字符串 <= 64 字符 - string OperatorNote; // 备注 备注信息 否 字符串 <= 255 字符 - }; - - // 充(换)电站信息 - struct StationInfo - { - string StationID; // 充(换)电站 ID 运营商自定义的唯一编码 是 字符串 <= 20 字 符 - string OperatorID; // 运营商 ID 电动汽车充(换)电服务平台的运营商 ID 是 字符串 9 字符 - string EquipmentOwnerID; // 设备所属方 ID 设备所属方组织机构代码,所属方为个人时可不填 否 字符串 9 字符 - - string StationName; // 充(换)电站名称 充(换)电站名称的描述 是 字符串 <= 50 字 - - string CountryCode; // 充(换)电站国家代码 比如 CN 是 字符串 2 字符 - string AreaCode; // 充(换)电站省市辖区编码 填写内容为参照 GB / T2260,以民政部发布最新数据为准 是 字符串 <= 20 字符 - string Address; // 详细地址 是 字符串<= 100字符 - string StationTel; // 站点电话 能够联系场站工作人员进行协助的联系电话 否 字符串<= 30 字符 - string ServiceTel; // 服务电话 平台服务电话,例如 400 电话 是 字符串<= 30字符 - string ServiceType; // *服务类型 1:充电 2:换电 3:充换电 255:其他 是 整型 - string StationType; // 站点类型 1:公共充(换)电站 2:专用充(换)电站 3:居民充电站 255:其他 是 整型 - string StationStatus; // 站点状态 0:未知 1:建设中 5:关闭下线 6:维护中 50:正常使用 是 整型 - string ParkNums; // 车位数量 可停放进行充电的车位总数, 默认:0 未知 是 整型 - float StationLng; // 经度 GCJ - 02 坐标系 是 浮点型 保留小数点后6 位 - float StationLat; // 纬度 GCJ - 02 坐标系 是 浮点型保留小数点后6 位 - string SiteGuide; // 站点引导描述性文字,用于引导车主找到充电车位 否 字符串<= 255字符 - - - int Construction; // 建设场所 - //101:公共服务场所 - //102:公共停车场 - //103:城市交通节点 - //104:加油站 - //105:具备停车条件的充电区域 - //106:高速服务区 - //201:政府机关 - //202:公共机构 - //203:企业事业单位 - //204:公交 - //205:环卫 - //206:物流 - //207:出租车 - //208:港口码头 - //209:重卡换电场所 - //210:矿卡换电场所 - //301:居民(小)区 - //255:其他 - // 是 整型 - string Pictures; // 站点照片 充(换)电设备照片、充(换)电车位照片、停车场入口照片 是 字符串数组 无照片时可传空数组 - string MatchCars; // 服务车型描述 描述该站点可充(换)电服务 的车辆类型:如大巴、物流车、私家乘用车、出租车、重卡型卡车等 否 字符串<= 255字符 - string ParkInfo; // 车位楼层及数量描述车位楼层以及数量信息 否 字符串<= 100字符 - int OpenAllDay; // *全天开放 0:否 1:是 是 整型 - string OpenForBusinessDate; // *投运日期 站点投运日期 yyyy - MM - dd 格式 是 字符串 - string BusineHours; // 营业时间 营业时间描述 否 字符串<=255字符 - - string ElectricityFee; // *电费费率 示例 [{"StartTime":"000000","Price":"1.0000"},{"StartTime":"120000","Price":"1.2000"}] 否 字符串 <= 2000 字符 - string ServiceFee; // *服务费率 示例[{"StartTime":"000000","Price":"1.0000"},{"StartTime":"120000","Price":"1.2000"}] 否 字符串 <= 2000 字符 - - - string ParkOwner; // *停车场产权方 停车场产权人 否 字符串 - string ParkManager; // *停车场管理方 停车场管理人(如:XX 物业) 否 字符串 - int ParkType; // 停车费类型 0:免费 1:不免费 2:限时免费停车 3:充电限时减免 255:参考场地实际收费标准 否 整型 - string ParkFee; // 停车费描述 停车费率描述 否 字符串 <= 255字符 - string Payment; // 支付方式 支付方式: 刷卡、线上、现金其中电子钱包类卡为刷卡,身份鉴权卡、微信 / 支付宝等在线支付、APP 支付为线上否 字符串<= 20 字符 - int SupportOrder; // 是否支持预约 0:不支持预约 1:支持预约。不填默认为 0 否 整型 - string Remark; // 备注 其他备注信息 否 字符串<= 100字符 - string EquipmentInfos; // 充电设备信息列表 该充(换)电站所有充电设备 信息对象数组 是 EquipmentInfos[],参照 4.4 - string SwapEquipmentInfos; // *换电设备信息列表 该充(换)电站所有换电设备。换电站以及充换电站提供此数据,充电站默认空数组。 是 SwapEquipmentInfo[],参照 4.6 换电工位信息 - int BatteryNo; // *备用电池数量换电站内可提供更换最大电池数量。换电站以及充换电站提供此数据,充电站默认填0。 是 整型 - }; - - // 充电设备信息 - struct EquipmentInfo - { - string EquipmentID; - string ManufacturerID; - string EquipmentModel; - string ProductionDate; - string OpenForBusinessDate; - int EquipmentType; - int EquipmentStatus; - vector ConnectorInfos; - float EquipmentLng; - float EquipmentLat; - float Power; - string EquipmentName; - }; - - // 充电设备接口信息 - struct ConnectorInfo - { - string ConnectorID; // 充电设备接口编码 充电设备接口编码,同一运营商内唯一 是 字符串 <= 64 字符 - string ConnectorName; // 充电设备接口名称 否 字符串 <= 30 字符 - int ConnectorType; // 充电设备接口类型 - //1:家用插座(模式 2) - //2:交流接口插座(模式 3,连接方式 B ) - //3:交流接口插头(带枪线,模式 3,连接方式 C) - //4:直流接口枪头(带枪线,模式 4) - //5:无线充电座 - //6:其他 - //7:对换电站电池箱的接口 - //是 整型 - int VoltageUpperLimits; // 额定电压上限 单位:V 是 整型 - int VoltageLowerLimits; // 额定电压下限 单位:V 交流可与额定电压上限相同 是 整型 - int ConstantVoltageUpperLimits; // *恒功率电压上限 单位:V 否 整型 - int ConstantVoltageLowerLimits; // *恒功率电压下限 单位:V 否 整型 - int Current; // 额定电流 单位:A 是 整型 - float Power; // 额定功率 单位:kW 是 浮点型 保留小数点后一位 - string ParkNo; // 车位号 停车场车位编号,或充电架编号 否 字符串 <= 10 字符 - int NationalStandard; // 国家标准 1:2011 2 : 2015 3 : 兼容 2011 和 2015 是 整型 - }; - - // 换电设备信息(SwapEquipmentInfo) - class SwapEquipmentInfo - { - string EquipmentID; // 设备编码 换电设备唯一编码,同一运营商下唯一 是 字符串 <= 64 字符 - string ManufacturerID; // 设备生产商组织机构代码 设备生产商组织机构代码 否 字符串 9 字符 - string EquipmentModel; // 设备型号 由设备生厂商定义的设备型号 否 字符串 <= 20 字符 - string ProductionDate; // 设备生产日期 YYYY - MM - DD 否 字符串 10 字符 - string OpenForBusinessDate; // *投运日期 充电桩投运日期 yyyy - MM - dd 格式 是 字符串 - string OpreateStatus; // 运营状态 0:未知 1:建设中 5:关闭下线 6:维护中 50:正常使用 是 整型 - int EquipmentType; // 换电设备类型 填写内容为参照GB29317 - 2021 4.3节中的描述 1:侧向换电 2:底部换电 3:顶部换电 4:端部换电 5:中置换电 255:其他 是 整型 - string MatchCars; // 服务车型描述 描述该设备可服务的车辆类型以及 型号等 否 字符串<= 1000 字符 - string SupplyBattery; // 提供电池描述 描述该设备提供的电池类型以及型号等 否 字符串<= 100 字符 - }; - - //电池箱信息 - struct BatteryInfo - { - string BatteryNo; // 电池箱编号 运营商自定义唯一编码 是 字符串 <= 32 字 - string BatteryOwnerID; // 电池所属方ID 设备所属方组织机构代码,所属方为个人时可不填 否 字符串 9 字符 - string ManufacturerID; // 设备生产商组织机构代码 设备生产商组织机构代码 否 字符串 9 字符 - string BatteryModel; // 电池型号 由设备生厂商定义的设备型号 否 字符串 <= 20 字符 - string ProductionDate; // 设备生产日期 YYYY - MM - DD 否 字符串 10 字符 - string OpenForBusinessDate; // 投运日期 电池投运日期 yyyy - MM - dd 格式 是 字符串 - int CellNum; // 电池箱所含单体电池个数 电池箱所含单体个数 是 整型 - int SeriesNum; // 单体电池串联总数 串联总数 否 整型 - int ParallelNum; // 单体电池并联总数 并联总数 否 整型 - int BatteryType; // 电池类型 - //1:磷酸铁锂电池 - //2:锰酸锂电池 - //3:钴酸锂电池 - //4:三元材料电池 - //5:聚合物锂离子电池 - //6:钛酸锂电池 - //7:燃料电池 - //255:其它 - //是 整型 - float RatedCapacity; // 电池箱额定容量 单位:Ah,小数点后 1 位 是 浮点型 - float RatedVoltage; // 电池箱额定电压 单位:V,小数点后 1 位 是 浮点型 - }; - - // 充电设备接口状态 - struct ConnectorStatusInfo - { - string ConnectorID; // 充电设备接口编码 充电设备接口编码,同一运营商内唯一 是 字符串<= 64 字符 - string UpdateTime; // 状态更新时间 本次状态变化的时间,格式“yyyy -MM - dd HH : mm:ss” 是 字符串 <= 20 字符 - int Status; // 接口状态 0:离线 1:空闲 2:占用(未充电) 3:占用(充电中) 4:占用(预约锁定) 255:故障 是 整型 - int ParkStatus; // 车位状态 0:未知 10:空闲 50:占用 否 整型 - int LockStatus; // 地锁状态 0:未知 10:已解锁 50:已上锁 否 整型 - int CurrentA; // A 相电流 单位:A,默认:0 含直流(输出) 是 整型 - int CurrentB; // B 相电流 单位:A,默认:0 否 整型 - int CurrentC; // C 相电流 单位:A,默认:0 否 整型 - int VoltageA; // A 相电压 单位:V,默认:0 含直流(输出) 是 整型 - int VoltageB; // B 相电压 单位:V,默认:0 否 整型 - int VoltageC; // C 相电压 单位:V,默认:0 否 整型 - float SOC; // *剩余电量 默认:0 交流充电桩采集不到SOC 值的填 0 是 浮点型 - string Begin_time; // *开始充电时间 格式 为 yyyy-MM-dd HH:mm:ss 是 字符串 - float Current_kwh; // *本次已充电量 单位:kWh 是 浮点型 - float Current_meter; // *当前电表读数 单位:kWh 否 浮点型 - string Vin; // *车架号 否 字符串 <= 20 字符 - //BatteryStatusInfo //*电池状态信息 充电设备有电池情况下需上报 是 BatteryStatusInfo,参照4.9 - }; - - // 电池箱状态 - struct BatteryStatusInfo - { - string BatteryNo; // 电池箱编号 (充电设备有电池情况下需上报) 是 字符串<= 32 字符 - string UpdateTime; // 状态更新时间 本次状态变化的时间,格式“yyyy -MM - dd HH : mm:ss” 是 字符串 <= 20 字符 - float Voltage; // 当前电压 单位:V,小数点后 2 位 (充电设备有电池情况下需上报) 是 浮点型 - float Current; // 当前电流 单位:V,小数点后 2 位 (充电设备有电池情况下需上报) 是 浮点型 - float SOC; // 当前 Soc 当前电池电量百分比,范围:0~100,小数点后 1 位 (充电设备有电池情况下需上报) 是 浮点型 - float SOH; // 当前 Soh 当前电池健康度,范围:0~100,小数点后 1 位(充电设备有电池情况下需上报)是 浮点型 - int BatteryIsFault; // 电池箱是否故障 0:未知 1:是 2:否 (充电设备有电池情况下需上报) 是 整型 - int MaxVoltageBatteryNo; // 最高电压单体电池编号 充电设备有电池情况下需上报 否 整型 - float MaxVoltage; // 最高电压单体电池电压值单位: V,小数点后3位 充电设备有电池情况下需上报 否 浮点型 - int MinVoltageBatteryNo; // 最低电压单体电池编号 充电设备有电池情况下需上报 否 整型 - float MinVoltage; // 最低电压单体电池电压值 单位: V,小数点后 3 位 充电设备有电池情况下需上报 否 浮点型 - float MaxTempBatteryNo; // 最高温度测温点编号 充电设备有电池情况下需上报 否 整型 - int MaxTemp; // 最高温度测温点温度值 单位:℃ 充电设备有电池情况下需上报 否 整型 - int MinTempBatteryNo; // 最低温度测温点编号 充电设备有电池情况下需上报 否 整型 - int MinTemp; // 最低温度测温点温度值 单位:℃ 充电设备有电池情况下需上报 否 整型 - }; - - // 换电设备状态(SwapEquipmentStatusInfo) - struct SwapEquipmentStatusInfo - { - string EquipmentID; // 换电设备编码 换电设备编码,同一运营商内唯一 是 字符串 <= 64 字符 - string UpdateTime; // 状态更新时间 本次状态变化的时间,格式“yyyy - MM - dd HH : mm:ss” 是 字符串 <= 20 字符 - int Status; // 换电设备状态 0:离线 1:空闲 2:工作 255:故障 是 整型 - int SwapMode; // 换电模式 0:手动模式 1:半自动模式 2:全自动模式 3:检修模式 否 整型 - }; - - // 充(换)电站状态(StationStatusInfo) - struct StationStatusInfo - { - string StationID; // 充(换)电站 ID 运营商自定义的唯一编码 是 字符串 <= 20 字符 - string ConnectorStatusInfos; // 充电设备接口状态列表 充(换)电站下所有充电设备接口的状态对象数组 是 ConnectorStatusInfos[], 参照 5.6 - - string SwapEquipmentStatusInfo; // 换电设备状态列 所有充电设备接口的是 SwapEquipmentStatusInfo[]表 状态 ,参照4.10 - }; - - //充电电量信息(OrderInfo) - struct OrderInfo - { - string OperatorID; // 运营商 ID 统一社会信用代码 是 字符串 9 字符 - string ConnectorID; // 充电设备接口编码 充电设备接口编码,同一充(换)电运营平台内唯一 是 字符串 <= 26 字符 - string StartChargeSeq; // 充电业务编号 运营商充电业务编号 是 字符串 <= 32 字符 - int UserChargeType; // 用户发起充电类型 1:充(换)电运营平台注册用户 2 : 监管平台注册用户 3 : 其他 否 整型 - string MobileNumber; // 用户手机号 若用户发起充电类型为APP,用户手机号必填否 字符串 - float Money; // 本次充电消费总金额 单位:元 若通过苏e充APP启动,此字段为必填项。 否 浮点型 - float ElectMoney; // 本次充电电费总金额 单位:元 若通过苏e充APP启动,此字段为必填项。 否 浮点型 - float ServiceMoney; // 本次充电服务费金额 单位:元 若通过苏e充APP启动,此字段为必填项。 否 浮点型 - float Elect; // 本次充电电量 单位 kWh,精度0.001,如果不设置峰谷电价,平电量等于本次充电电量,其他分电量为零。 是 浮点型 - float CuspElect; // *尖阶段电量 单位 kWh,精度0.001 是 浮点型 - float PeakElect; // *峰阶段电量 单位 kWh,精度0.001 是 浮点型 - float FlatElect; // *平阶段电量 单位 kWh,精度0.001, 是 浮点型 - float ValleyElect; // *谷阶段电量 单位 kWh,精度0.001 是 浮点型 - float StartTime; // 本次充电开始时间 格式“yyyy - MM - ddHH : mm:ss” 是 字符串 - float EndTime; // 本次充电结束时间 格式“yyyy - MM - ddHH : mm: ss” 是 字符串 - float PaymentAmount; // 支付金额 支付金额 若通过苏e充APP启动,此字段为必填项。 否 浮点型 保留小数点后 2 位 - float MeterValueStart; // *电表总起值 单位 kWh,精度0.001 是 浮点型 保留小数点后三位 - float MeterValueEnd; // *电表总止值 单位 kWh,精度0.001 是 浮点型 保留小数点后三位 - float Vin; // *本次充电车架号 充电设备有车辆VIN码需上报 否 字符串 <= 64 字符 - float BatteryNo; // *本次充电电池编号 充电设备有电池情况下需上报 否 字符串 <= 64 字符 - float ExchangeChargeSeq;// *换电记录编号 格式“运营商 ID + 唯一编号”,27 字符,如果有对应的换电记录需要填写是 字符串 <= 40 字符 - }; - - // 换电记录信息(SwapInfo) - struct SwapInfo - { - string OperatorID; //运营商 ID 统一社会信用代码 是 字符串 9 字符 - string EquipmentID; // 换电设备编码 换电设备接口编码,同一充(换)电运营平台内唯一 是 字符串 <= 40 字符 - - string ExchangeChargeSeq; // 换电记录编号 格式“运营商 ID + 唯一编号”,27 字 符 是 字符串 - string SwapMode; // 换电模式 0:手动模式 1:半自动模式 2:全自动模式 3:检修模式 否 整型 - string CarNo; // 车牌号 否 字符串 <= 16 字符 - string Vin; // 车辆VIN码 车辆识别码;见GB - T - 27930 - 2015国标PGN512 BMS 和车辆辨识报文(BRM)约定 否 字符串 - string RepDownBatteryNo;// 换下电池箱编号 运营商自定义唯一 编码, 是 字符串 <= 32 字符 - string RepDownBatterySoc;// 换下电池箱SOC 电池电量百分比,范围:0~100, 是 整型 - string RepOnBatteryNo;//换上电池箱编号 运营商自定义唯一编码,是 字符串 <= 32 字符 - int RepOnBatterySoc; //换上电池箱SOC 电池电量百分比,范围:0~100 是 整型 - float TotalPower; //换上电池箱总充入电量 单位:度(kWh) 是 浮点型 保留小数点后两位 - string StartTime; //换电开始时间 格式“yyyy - MM - dd HH : mm:ss” 是 字符串 - string EndTime; //换电结束时间 格式“yyyy - MM - dd HH : mm: ss” 是 字符串 - }; - - // 充(换)电设备告警信息(AlarmInfo) - struct AlarmInfo - { - string EquipmentID; // 设备编码 充电接口唯一编码,对同一运营商,保证唯一 是 字符串 23 字符 - int EquipmentType; //设备类型 1:充电设备 2:换电设备 整型 - string Alert_time; //告警时间 格 式 为 yyyy - MMdd HH : mm:ss 是 字符串 - int Alert_code; //告警代码 告警代码 是 整型 - string Describe; //描述 文字描述,最大长度 256字符。是 字符串 256 字符 - int Status; //状态 告警发生:0;告警 恢复: 1,默认为 0。是 整型 - }; -} - - class MQTT { public: - static string packEquipmentInfo(mqtt::EquipmentInfo& info); + - static string packSwapEquipmentStatusInfo(string node_name); - - // 充(换)电站信息变化推送 - static string packNotifyStationInfo(); - - // 告警信息推送 - static string packNotifyAlarm(); - - // 充电设备状态变化推送, 充电启停或者离线状态改变时推送,充电过程中每分钟一次推送 - static string packNotifyChargeStatus(); - - // 换电设备状态变化推送 - static string packNotifySwapStatus(); - - // 充电电量信息推送(chon), 充电结束后5分钟内推送 - static string packNotifyChargeOrder(); - - // 换电记录信息推送, 换电结束后5分钟内推送 - static string packNotifySwapOrder(); +public: + static string packEquipmentInfo(); }; \ No newline at end of file