diff --git a/bin/Release/assets/config/monitoraddr.json b/bin/Release/assets/config/monitoraddr.json index ba9da6b..06e9005 100644 --- a/bin/Release/assets/config/monitoraddr.json +++ b/bin/Release/assets/config/monitoraddr.json @@ -103,7 +103,7 @@ ["功率限值", "31077", "0.0", " kW"], ["输出电压", "31079", "0.0", " V"], ["输出电流", "31081", "0.0", " A"], - ["输出功率", "31083", "0.0", " kW"], + ["输出功率", "31083", "0.0", " kW"] ] } } \ No newline at end of file diff --git a/src/app/AppData.cpp b/src/app/AppData.cpp index 5e775b1..6e3d922 100644 --- a/src/app/AppData.cpp +++ b/src/app/AppData.cpp @@ -106,7 +106,8 @@ bool AppData::initFromDB() } { // 数据库读取场站信息 str = "", result.clear(); - DAO::queryStationList(dao, result); + std::string sql = "SELECT s.*, p.name policy_name, p.`type` policy_type, p.value FROM station s LEFT JOIN policy p ON s.policy_id=p.policy_id;"; + dao->exec(sql, result); for (auto& fields: result) { auto station = std::make_shared(); @@ -140,7 +141,7 @@ bool AppData::initFromDB() DAO::queryPolicyList(dao, result); for (auto& fields: result) { - auto policy = std::make_shared(); + auto policy = std::make_shared(); policy->policyId = fields.get(DMPolicy::POLICY_ID); policy->type = fields.get(DMPolicy::TYPE); policy->name = fields.value(DMPolicy::NAME); diff --git a/src/app/AppData.h b/src/app/AppData.h index 79d26eb..07a24f2 100644 --- a/src/app/AppData.h +++ b/src/app/AppData.h @@ -11,7 +11,7 @@ class Station; class Device; -class MyPolicy; +class SysPolicy; using VecPairSS = std::vector>; @@ -155,7 +155,7 @@ public: std::unordered_map mapPolicyType; // 策略信息 - std::unordered_map> mapPolicy; + std::unordered_map> mapPolicy; // 电力峰谷分段 (12个月,每个月按小时分成24个时段) std::vector> vecElectPeriods; diff --git a/src/app/Policy.cpp b/src/app/Policy.cpp index e69de29..3b1ee87 100644 --- a/src/app/Policy.cpp +++ b/src/app/Policy.cpp @@ -0,0 +1,146 @@ +#include "Policy.h" +#include "common/Spdlogger.h" +#include "Utils.h" + +void SysPolicy::setFields(Fields& fields) +{ + this->policyId = fields.get("policy_id"); + this->type = fields.get("policy_type"); + this->name = fields.value("policy_name"); + this->value = fields.value("value"); + + this->parseValue(value); +} + + +/// type=1: 峰谷套利 +static void ParseJsonType1(njson& json) +{ +} + +void SysPolicy::getGatewayJsonPrice(njson& json) +{ +} + +// 1:峰谷套利,2:配网增容,3:应急供电,4:并网保电,5:自定时段 +void SysPolicy::parseValue(std::string jsonstr) +{ + njson jsonroot; + if (!JSON::parse(jsonstr, jsonroot)) + { + spdlog::error("[policy] json parse policy value error, policy_id={}, value={}", policyId, jsonstr); + return; + } + + // 读取电价 + if (jsonroot.contains("price")) + { + auto& jsonArrayPrice = jsonroot["price"]; + for (int i = 0; i()); + } + } + } + + if (this->type == 1 || this->type == 5) + { + this->parseJsonPeriods(jsonroot); + } +} + +void SysPolicy::parseJsonPeriods(njson& jsonroot) +{ + if (!jsonroot.contains("period")) + { + spdlog::error("[policy] json parse policy value error, [period] is not exist, value={}", jsonroot.dump()); + return; + } + auto& json = jsonroot["period"]; + if (!json.is_array()) + { + spdlog::error("[policy] json parse policy value error, [period] is not array, value={}", json.dump()); + return; + } + + // 1: 谷,2:平,3:峰,4:尖 + if (this->type == 1) // 峰谷套利 + { + vecPeriods1.clear(); + for (int i = 0; i 0) + { + vecPeriods1.push_back(std::vector>()); + for (auto& itemMonth: json[i]) + { + if (itemMonth.size() >=2) { vecPeriods1[i].push_back({itemMonth[0], itemMonth[1]}); } + } + } + } + } + else if (this->type == 5) + { + //{ + // "period":[ + // {"charge_time":[],"discharge_time":[],"charge_power":"","discharge_power":""}, + // {"charge_time":[],"discharge_time":[],"charge_power":"","discharge_power":""} + // ], + // "price":["0.00","0.00","0.00","0.00"] + //} + vecPeriods1.clear(); + for (int i = 0; i>()); + auto& item = json[i]; + if (item.contains("charge_time") && item["charge_time"].size() >= 2) + { + auto& jsonP = item["charge_time"]; + vecPeriods1[i].push_back({jsonP[0], "谷"}); // 第一/二次充电开始 + vecPeriods1[i].push_back({jsonP[1], "平"}); // 第一/二次充电结束 + } + if (item.contains("discharge_time") && item["discharge_time"].size() >= 2) + { + auto& jsonP = item["discharge_time"]; + vecPeriods1[i].push_back({jsonP[0], "尖"}); // 第一/二次放电开始 + vecPeriods1[i].push_back({jsonP[1], "峰"}); // 第一/二次放电结束 + } + } + } +} + +static void PeriodsTimeStrToInt(std::string str, int& h, int& m) +{ + int pos = str.find(":"); + if (pos != std::string::npos) + { + h = Utils::toInt(str.substr(0, pos)); + m = Utils::toInt(str.substr(pos+1)); + } +} + +void SysPolicy::getGatewayJsonPeriods(njson& json) +{ + if (type == 1 || type == 5) + { + // std::vector>> + for (auto& itemMonth: vecPeriods1) + { + njson jsonArrayMonth = njson::array(); + for (auto& item: itemMonth) + { + int h = 0; int m = 0; + PeriodsTimeStrToInt(item.first, h, m); + int p = 1; + if (item.second == "谷") p = 1; + else if (item.second == "平") p = 2; + else if (item.second == "峰") p = 3; + else if (item.second == "尖") p = 4; + jsonArrayMonth.push_back({h, m, p}); + } + json.push_back(jsonArrayMonth); + } + } +} \ No newline at end of file diff --git a/src/app/Policy.h b/src/app/Policy.h index 172e238..6810cc4 100644 --- a/src/app/Policy.h +++ b/src/app/Policy.h @@ -1,15 +1,32 @@ #pragma once #include +#include #include "common/Fields.h" +#include "common/JsonN.h" -class MyPolicy +class SysPolicy { public: int policyId {0}; + + // 1:峰谷套利,2:配网增容,3:应急供电,4:并网保电,5:自定时段 int type {0}; + std::string name; std::string value; - int isOpen {0}; + int isOpen {1}; Fields fields; + + std::vector vecPrice {0.0f, 0.0f, 0.0f, 0.0f}; + + std::vector>> vecPeriods1; + + void setFields(Fields& fields); + + void parseValue(std::string jsonstr); + void parseJsonPeriods(njson& json); + + void getGatewayJsonPrice(njson& json); + void getGatewayJsonPeriods(njson& json); }; \ No newline at end of file diff --git a/src/app/Station.cpp b/src/app/Station.cpp index b046e6c..a9b9062 100644 --- a/src/app/Station.cpp +++ b/src/app/Station.cpp @@ -40,6 +40,9 @@ void Station::setFields(Fields& fields) this->workModeId = fields.get(DMStation::WORK_MODE); this->code = fields.value(DMStation::CODE); this->status = fields.get(DMStation::STATUS); + this->operationDate = fields.value(DMStation::OPERATION_DATE); + + this->policy.setFields(fields); } void Station::addDevice(int deviceId, std::shared_ptr device) @@ -210,7 +213,19 @@ void Station::setGarewayWorkMode() json["ts"] = Utils::time(); json["no"] = 1; // 设备编号 json["40001"] = this->workModeId; + + if (policy.type == 1) + { + json["40002"] = njson::array(); // 峰谷套利 + policy.getGatewayJsonPeriods(json["40002"]); + } + else if (policy.type == 5) + { + json["40021"] = njson::array(); // 自定时段 + policy.getGatewayJsonPeriods(json["40021"]); + } std::string text = json.dump(); + spdlog::info(text); mqttCli->publish("Gateway_YT", text); -} \ No newline at end of file +} diff --git a/src/app/Station.h b/src/app/Station.h index f618026..335be31 100644 --- a/src/app/Station.h +++ b/src/app/Station.h @@ -3,6 +3,7 @@ #include #include #include "common/Fields.h" +#include "Policy.h" class Device; class MqttClient; @@ -106,7 +107,6 @@ public: void setWorkMode(int modeId); void setPolicy(int policyId); - void writeRuntimeData(std::string dt, int npos); void initMqtt(); @@ -119,6 +119,9 @@ public: std::string code; bool isOpen {false}; int status {0}; + std::string operationDate; + SysPolicy policy; + bool isConnected {false}; int workModeId {}; // 运行模式 diff --git a/src/database/DataModelDef.h b/src/database/DataModelDef.h index e47cfd6..35e07b1 100644 --- a/src/database/DataModelDef.h +++ b/src/database/DataModelDef.h @@ -93,6 +93,7 @@ namespace DMStation const string POLICY_ID = "policy_id"; const string CODE = "code"; const string ATTR = "attr"; + const string OPERATION_DATE = "operation_date"; } namespace DMDefDeviceType diff --git a/src/database/MysqlClient.cpp b/src/database/MysqlClient.cpp index 3fac260..415c203 100644 --- a/src/database/MysqlClient.cpp +++ b/src/database/MysqlClient.cpp @@ -1,7 +1,6 @@ #include "MysqlClient.h" #include "common/Utils.h" -//#include "Spdlogger.h" -#include "Logger.h" +#include "Spdlogger.h" #include using namespace std; @@ -28,7 +27,7 @@ int MysqlClient::conn() { return 0; } - if (GetTimestamp() - g_tickErr <= 5) + if (GetTimestamp() - g_tickErr <= 10) { return 1; } @@ -73,19 +72,19 @@ static int MysqlQuery(MYSQL* mysql, const std::string& sql) int err = 0; if (!mysql) { - XLOGE() << "Mysql exec error, database is not connected."; + spdlog::error("Mysql exec error, database is not connected."); return err; } if (sql.empty()) { - XLOGE() << "Mysql exec error, sql is empty."; + spdlog::error("Mysql exec error, sql is empty."); return err; } err = mysql_query(mysql, sql.c_str()); if (0 != err) { err = mysql_errno(mysql); - XLOGE() << "Mysql exec error: " << err << "," << mysql_error(mysql) << ", sql=" << sql; + spdlog::error("Mysql exec error: {}, {}, sql={}", err, mysql_error(mysql), sql); return err; } return err; diff --git a/src/main.cpp b/src/main.cpp index 3a2398a..1cee7a3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -127,57 +127,14 @@ int main(int argc, char** argv) SetConsoleOutputCP(CP_UTF8); // 设置控制台输入为 UTF-8 编码(如果需要输入中文) SetConsoleCP(CP_UTF8); - - float ratio = 1.1f; - int precision = 0; - if (ratio != 1.0f) - { - precision = 2; - }; - - //qputenv("QTWEBENGINE_REMOTE_DEBUGGING", "9222"); // 即使内置视图,有时也需要开启调试端口 - //QApplication app(argc, argv); - - //QMainWindow mainWindow; - - //// 主窗口和布局 - //QWidget myRoot; - - //QHBoxLayout* layout = new QHBoxLayout(&myRoot); - - //// 创建主 Web 视图 - //QWebEngineView* mainWebView = new QWebEngineView; - //mainWebView->load(QUrl("https://www.example.com")); - - //// 创建用于显示开发者工具的 Web 视图 - //QWebEngineView* devToolsView = new QWebEngineView; - - //// 将主 Web 页面的开发者工具页面设置为 devToolsView 的页面 - //mainWebView->page()->setDevToolsPage(devToolsView->page()); - //// 如果你需要先导航主页面,然后在某个事件(如按钮点击)后显示开发者工具,可以将这行代码放在事件处理函数中。 - - //// 将两个视图添加到布局中 - //layout->addWidget(mainWebView); - //layout->addWidget(devToolsView); - //// 可以适当设置两个视图的大小比例,例如: - //// layout->setStretchFactor(0, 2); // 主视图占2份 - //// layout->setStretchFactor(1, 1); // 开发者工具视图占1份 - //myRoot.show(); - - //mainWindow.resize(1600, 900); - //mainWindow.setCentralWidget(&myRoot); - //mainWindow.show(); - - //// 如果你想在启动时自动打开开发者工具,可以触发一个“打开”事件(但setDevToolsPage本身调用后通常需要一些条件才弹出,有时需要手动在浏览器中inspected后再内置查看) - //// 更常见的做法是连接一个信号,例如页面加载完成后,或者通过一个按钮触发 devToolsView->show()。 - - //return app.exec(); - - - + // 初始化日志 Spdlogger::init(spdlog::level::debug, ""); spdlog::info("[main] start ... ======================================================================"); + njson json; + json = {1, 2, 3, 4}; + spdlog::info(json.dump()); + // 运行后台服务 Application::instance().init(); diff --git a/src/protocol/HttpEntity.cpp b/src/protocol/HttpEntity.cpp index 76df57d..1bd8c6b 100644 --- a/src/protocol/HttpEntity.cpp +++ b/src/protocol/HttpEntity.cpp @@ -341,7 +341,7 @@ Errcode HttpEntity::login(const httplib::Request& req, njson& json, std::string& json["permission"] = nodePermission; } - DAO::insertSystemLogUser(token, "用户登录:" + ErrcodeStr(err), (err==Errcode::OK) ? 0: 1); + DAO::insertSystemLogUser(token, "用户登录:" + ErrcodeStr(err), (err==Errcode::OK) ? 1: 0); return err; } @@ -358,7 +358,7 @@ Errcode HttpEntity::queryUserList(const httplib::Request& req, njson& json, std: { HttpHelper::setPagination(pageinfo, result, json); } - DAO::insertSystemLogUser(token, "查询用户列表:" + ErrcodeStr(err), (err==Errcode::OK) ? 0 : 1); + //DAO::insertSystemLogUser(token, "查询用户列表:" + ErrcodeStr(err), (err==Errcode::OK) ? 1 : 0); return err; } @@ -601,12 +601,38 @@ Errcode HttpEntity::insertStation(const httplib::Request& req, njson& json, std: Errcode HttpEntity::updateStation(const httplib::Request& req, njson& json, std::string& errmsg) { Fields params; - GetRequestParam(req, {"station_id", "name", "address", "lon", "lat", "tel", "capacity", "status", "work_mode"}, params); + GetRequestParam(req, {"station_id", "name", "address", "lon", "lat", "tel", "capacity", "status", "work_mode", "policy_id"}, params); + std::string stationId = params.value("station_id"); params.check("capacity", "", "0.0"); params.check("lon", "", "0.0"); params.check("lat", "", "0.0"); params.check("status", "", "1"); - return DAO::updateStationById(params); + Errcode err = DAO::updateStationById(params); + if (err == Errcode::OK) + { + std::string sql = "SELECT s.*, p.name policy_name, p.`type` policy_type, p.value FROM station s LEFT JOIN policy p ON s.policy_id=p.policy_id" + " WHERE s.station_id='" + stationId + "';"; + std::vector result; + int ret = DaoEntity::execOnce(sql, result); + if (ret != 0) + { + spdlog::error("[http] update station success, set station cache error, station_id={}", stationId); + } + else + { + if (result.size() > 0) + { + auto station = Application::data().getStation(Utils::toInt(stationId)); + if (station) + { + station->setFields(result[0]); + station->setGarewayWorkMode(); + } + } + + } + } + return err; }; Errcode HttpEntity::deleteStation(const httplib::Request& req, njson& json, std::string& errmsg) diff --git a/src/protocol/MqttEntity.cpp b/src/protocol/MqttEntity.cpp index 6b25fa7..a0d8296 100644 --- a/src/protocol/MqttEntity.cpp +++ b/src/protocol/MqttEntity.cpp @@ -194,6 +194,8 @@ void MqttClient::subscribe() int MqttClient::publish(std::string topic, std::string text) { + if (!client) return 0; + MQTTAsync_responseOptions options = MQTTAsync_responseOptions_initializer; options.onSuccess = [](void* context, MQTTAsync_successData* response) {}; options.onFailure = [](void* context, MQTTAsync_failureData* response) {}; diff --git a/src/qt/MainWeb.cpp b/src/qt/MainWeb.cpp index 32a70ba..7a96ca8 100644 --- a/src/qt/MainWeb.cpp +++ b/src/qt/MainWeb.cpp @@ -79,6 +79,12 @@ MainWeb::MainWeb() void MainWeb::initWebview() { + labelWebErr.setParent(this); + labelWebErr.setGeometry(180, 100, 800, 50); + labelWebErr.setText("WEB服务异常!!!"); + labelWebErr.setStyleSheet("font: bold 20px;"); + labelWebErr.hide(); + qputenv("QTWEBENGINE_REMOTE_DEBUGGING", "9222"); // 即使内置视图,有时也需要开启调试端口 //this->setCentralWidget(&webView); webView.setParent(this); @@ -104,11 +110,13 @@ void MainWeb::initWebview() if (ok) { webView.show(); + labelWebErr.hide(); } else { spdlog::error("[web] webview load failed, url={}", Config::option.webSrvUrl); webView.hide(); + labelWebErr.show(); } }); } @@ -186,8 +194,13 @@ bool MainWeb::event(QEvent* e) void MainWeb::keyPressEvent(QKeyEvent* e) { - if (e->key() == Qt::Key_F12) + auto key = e->key(); + if (key == Qt::Key_F12) { this->showDevTools(); } + else if (key == Qt::Key_F5) + { + webView.load(QUrl(Config::option.webSrvUrl.c_str())); + } } \ No newline at end of file diff --git a/src/qt/MainWeb.h b/src/qt/MainWeb.h index ba21914..ce3aeb4 100644 --- a/src/qt/MainWeb.h +++ b/src/qt/MainWeb.h @@ -18,9 +18,13 @@ public: void keyPressEvent(QKeyEvent* event); public: + QLabel labelWebErr; + QWebEngineView webView; QWebEngineView devTools; + + std::shared_ptr splash; QLabel label1; QLabel labelProgress;