修改网关参数设置接口

This commit is contained in:
lixiaoyuan
2025-10-11 19:30:15 +08:00
parent b12aca76c4
commit 567966d4f1
12 changed files with 408 additions and 956 deletions

View File

@@ -19,6 +19,7 @@ struct AppOption
} database;
struct {
bool enabled {true};
int useToken {1};
int port {0};
int encryption {1};
@@ -26,6 +27,7 @@ struct AppOption
} http;
struct {
bool enabled {true};
std::string host;
std::string username;
std::string password;

View File

@@ -15,6 +15,9 @@
Station::Station() : stationId(0)
{
mqttCli = std::make_shared<MqttClient>();
predictStorageIn = vector<int>(144, 0);
predictStorageOut = vector<int>(144, 0);
predictCharge = vector<int>(144, 0);
}
void Station::setFields(Fields& fields)
@@ -168,12 +171,9 @@ int64_t Station::getPollingTS()
void Station::setGarewayWorkMode()
{
if (!mqttCli)
{
return;
}
auto policy = Application::data().getPolicyByType(this->workMode);
if (!mqttCli) { return; }
auto policy = Application::data().getPolicyByType(this->workMode);
njson json;
json["ts"] = Utils::time();
json["no"] = 1; // 设备编号
@@ -193,15 +193,25 @@ void Station::setGarewayWorkMode()
}
}
std::string text = json.dump();
spdlog::info("[station] set gateway workmode [Gateway_YT], stationId={}, text={}", stationId, text);
mqttCli->publish("Gateway_YT", text);
}
void Station::setGarewayParams()
{
if (!mqttCli) { return; }
njson json;
json["ts"] = Utils::time();
json["no"] = 1; // 设备编号
json["40038"] = {gatewayParam.socMin, gatewayParam.socMax, gatewayParam.capacity, gatewayParam.powerSafe, gatewayParam.powerDischarge, gatewayParam.powerCharge};
json["40058"] = {gatewayParam.backflow, gatewayParam.overload};
std::string text = json.dump();
spdlog::info(text);
spdlog::info("[station] set gateway params, stationId={}, text={}", stationId, text);
mqttCli->publish("Gateway_YT", text);
}
string Station::getGatewayMode()
{
// 0手动1峰谷套利2增网配容3应急供电4并网保电5自定时段
@@ -336,7 +346,7 @@ void Station::readRuntimeData(int deviceNo, string addr, int val)
{
if (addr == "0x000B") { this->voltage = val; } // A相电压 R uint32 1V 0x000B
if (addr == "0x0011") { this->current = val; } // A相电流 R int32 1A 0x0011
if (addr == "0x0011") { this->power = val; } // 三相总有功 R int32 1kW 0x0023
if (addr == "0x0023") { this->power = val; } // 三相总有功 R int32 1kW 0x0023
}
else if (deviceNo == 2)
{
@@ -461,14 +471,10 @@ void Station::readGatewayMode(int deviceNo, int mode, string p1, string p2, stri
device->online = true;
device->ts = Utils::time();
}
this->gatewayParam.mode = mode;
this->gatewayParam.param1 = p1;
this->gatewayParam.param2 = p2;
this->gatewayParam.param3 = p3;
if (mode != this->workMode)
{
//this->setGarewayWorkMode();
}
if (mode != -1) { this->gatewayParam.mode = mode; }
if (!p1.empty()) { this->gatewayParam.param1 = p1; }
if (!p2.empty()) { this->gatewayParam.param2 = p2; }
if (!p3.empty()) { this->gatewayParam.param3 = p3; }
njson json;
if (JSON::parse(gatewayParam.param3, json))
@@ -695,3 +701,59 @@ void Station::writeStatistic()
dao->duplicateUpdate(fields, {"value"});
}
}
void Station::predict()
{
int64_t tNow = Utils::time();
string dt1 = Utils::dateStr(tNow-86400*7);
string dt2 = Utils::dateStr(tNow-86400);
/// 预测实现方案:
// 查询前7天的历史数据根据历史数据计算今日数据每10分钟一个数据一天共144个数据点
string sql = "SELECT * FROM predict_day pd WHERE pd.station_id='" + std::to_string(stationId)
+ "'AND dt>='" + dt1 + "' AND dt<='" + dt2 + "'";
vector<Fields> result;
DAO::exec(NULL, sql, result);
// 数据源的条数1天的数据算作1条
int countStorageIn = 0;
int countStorageOut = 0;
int countCharge = 0;
for (int row=0; row<result.size(); ++row)
{
auto& fields = result[row];
vector<int>* vdptr = NULL;
int datatype = fields.get<int>("datatype"); // 1储能充电2储能放电3充电桩充电4发电
if (datatype == 1)
{
countStorageIn++;
vdptr = &predictStorageIn;
}
else if (datatype == 2)
{
countStorageOut++;
vdptr = &predictStorageOut;
}
else if (datatype == 3)
{
countCharge++;
vdptr = &predictCharge;
}
if (vdptr)
{
string& strval = fields.value("value");
std::vector<int> vec;
JSON::parseArray(strval, vec);
for (int i = 0; i<vdptr->size() && i<vec.size(); ++i)
{
(*vdptr)[i] += vec[i];
}
}
}
}

View File

@@ -112,6 +112,7 @@ public:
void polling();
int64_t getPollingTS();
void setGarewayWorkMode();
void setGarewayParams();
void checkDevice();
string getGatewayMode();
@@ -132,6 +133,8 @@ public:
void writeStatistic();
int posDayStat {0};
void predict();
public:
int stationId {};
std::string name;
@@ -279,4 +282,10 @@ public:
std::map<int, float> mapCacheElectOut;
// 充电桩充电量缓存key位置索引0->144val电量
std::map<int, float> mapCacheElectCharger;
///////////////////////////////////////////////////////////////////////////////////////////////
/// 预测
vector<int> predictStorageIn;
vector<int> predictStorageOut;
vector<int> predictCharge;
};

View File

@@ -92,6 +92,7 @@ static int MysqlQuery(MYSQL* mysql, const std::string& sql)
int MysqlClient::exec(std::string sql)
{
if (!mysql_) return 1;
int err = MysqlQuery(mysql_, sql);
// 确保读取并释放结果集,否则会产生 [2014,Commands out of sync;] 错误
MYSQL_RES* res = mysql_store_result(mysql_);

View File

@@ -1667,7 +1667,7 @@ Errcode HttpEntity::updateGatewayParams(const httplib::Request& req, njson& json
if (station)
{
params.get("work_mode", station->workMode);
//params.get("work_mode", station->workMode);
params.get("soc_min", station->gatewayParam.socMin); // 储能放电下限值 SOC 40038 (%, 0-99)
params.get("soc_max", station->gatewayParam.socMax); // 储能充电上限值 SOC 40039 (%:1-100)
params.get("capacity", station->gatewayParam.capacity); // 台区变压器容量 40040 (KVA 160-1600)
@@ -1677,7 +1677,7 @@ Errcode HttpEntity::updateGatewayParams(const httplib::Request& req, njson& json
params.get("backflow", station->gatewayParam.backflow); // 防逆流回差 40058(1KW 10-300)
params.get("overload", station->gatewayParam.overload); // 防过载回差 40059(1KW 10-300)
station->setGarewayWorkMode();
station->setGarewayParams();
return Errcode::OK;
}
return Errcode::ERR_PARAM;

View File

@@ -297,7 +297,7 @@ int MqttClient::onMessageArrived(char* topic, int topicLen, MQTTAsync_message* m
int mode = -1;
std::string param1;
std::string param2;
std::string param3 = json.dump();
std::string param3;
JSON::read(json, "40001", mode);
if (json.contains("40002")) { param1 = json["40002"].dump(); }

200
src/qt/QWSwitch.cpp Normal file
View File

@@ -0,0 +1,200 @@
#include "QWSwitch.h"
#include <QDebug>
QWSwitch::QWSwitch(QWidget* parent)
: QWidget {parent}
{
}
bool QWSwitch::getSwitch() {
return mOnOff;
}
void QWSwitch::setSwitch(bool onoff) {
if (mWaitSigModel) return;
/// 状态切换
mOnOff = onoff;
/// 发送信号
signalSwitchChanged(mOnOff);
/// 动画-背景颜色
QPropertyAnimation* colorAnimation = new QPropertyAnimation(this, "pBackColor");
colorAnimation->setDuration(mAnimationPeriod);
colorAnimation->setStartValue(mBackColor);
colorAnimation->setEndValue(mOnOff ? mBackOnColor : mBackOffColor);
colorAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped); //停止后删除
/// 动画-开关按钮位置
QVariantAnimation* posAnimation = new QVariantAnimation(this);
posAnimation->setDuration(mAnimationPeriod);
posAnimation->setStartValue(mButtonRect.topLeft());
posAnimation->setEndValue(mOnOff ? mRightPos : mLeftPos);
connect(posAnimation, &QPropertyAnimation::valueChanged, [=](const QVariant& value) {
mButtonRect.moveTo(value.toPointF());
update();
});
posAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped); //停止后删除
}
void QWSwitch::setSwitchForWaitModel(bool onoff)
{
if (!mWaitSigModel) return;
if (mOnOff == onoff) {
/// 表示值未改变先运行按钮位置动画
QVariantAnimation* posAnimation = new QVariantAnimation(this);
posAnimation->setDuration(mAnimationPeriod);
posAnimation->setStartValue(mOnOff ? mLeftPos : mRightPos);
posAnimation->setEndValue(mOnOff ? mRightPos : mLeftPos);
connect(posAnimation, &QVariantAnimation::valueChanged, [=](const QVariant& value) {
mButtonRect.moveTo(value.toPointF());
update();
});
posAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped); //停止后删除
return;
}
/// 状态切换
mOnOff = onoff;
/// 发送信号
signalSwitchChanged(mOnOff);
/// 后运行背景颜色动画
QPropertyAnimation* colorAnimation = new QPropertyAnimation(this, "pBackColor");
colorAnimation->setDuration(mAnimationPeriod);
colorAnimation->setStartValue(mBackColor);
colorAnimation->setEndValue(mOnOff ? mBackOnColor : mBackOffColor);
colorAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped); //停止后删除
connect(colorAnimation, &QPropertyAnimation::valueChanged, [=](const QVariant& value) {
update();
});
}
void QWSwitch::setEnabled(bool enable) {
QWidget::setEnabled(enable);
mEnable = enable;
emit signalEnableChanged(mEnable);
update();
}
bool QWSwitch::getEnabled() {
return mEnable;
}
void QWSwitch::setAnimationPeriod(int period) {
mAnimationPeriod = period;
}
void QWSwitch::setPrecisionClick(bool flag) {
mPrecisionClickFlagh = flag;
}
void QWSwitch::setWaitModel(bool flag)
{
mWaitSigModel = flag;
}
void QWSwitch::setButtonColor(QColor color) {
mButtonColor = color;
update();
}
void QWSwitch::setBackOnColor(QColor color) {
mBackOnColor = color;
update();
}
void QWSwitch::setBackOffColor(QColor color) {
mBackOffColor = color;
update();
}
void QWSwitch::setEdgeColor(QColor color) {
mEdgeColor = color;
update();
}
void QWSwitch::paintEvent(QPaintEvent* event) {
Q_UNUSED(event)
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(Qt::NoPen);
/// 绘制边缘颜色
QPainterPath path;
path.addRect(this->rect());
path.addRoundedRect(this->rect(), mRadius, mRadius);
path.setFillRule(Qt::OddEvenFill);
painter.setBrush(mEdgeColor);
painter.drawPath(path);
/// 绘制背景颜色
painter.setBrush(mBackColor);
painter.drawRoundedRect(this->rect(), mRadius, mRadius);
/// 绘制圆形按钮
painter.setBrush(mButtonColor);
painter.drawEllipse(mButtonRect);
/// 绘制按钮阴影
painter.setBrush(Qt::NoBrush);
QColor color(Qt::black);
int count = (this->height() - mButtonRect.height())/2;
float stepColor = (0.15-0.0)/count;
for (int i = mButtonRect.height()/2 + 1; i < this->height()/2; i++) {
color.setAlphaF(0.15 - stepColor*(i - mButtonRect.height()/2));
painter.setPen(color);
painter.drawEllipse(mButtonRect.center(), i, i);
}
/// 失能显示,添加一层蒙层
if (!mEnable) {
QColor disable(Qt::black);
disable.setAlphaF(0.5);
painter.setBrush(disable);
painter.drawRoundedRect(this->rect(), mRadius, mRadius);
}
}
void QWSwitch::resizeEvent(QResizeEvent* event) {
Q_UNUSED(event)
/// 更新按钮大小、圆角大小、动画两个位置
int size = qMin(this->width(), this->height());
mRadius = size/2;
float width = size * 3 / 4;
float border = (size - width) / 2;
mLeftPos = QPoint(border+2, border);
mRightPos = QPoint(this->width() - border - width-2, border);
mButtonRect.setWidth(width);
mButtonRect.setHeight(width);
mButtonRect.moveTo(mOnOff ? mRightPos : mLeftPos);
mBackColor = mOnOff ? mBackOnColor : mBackOffColor;
update();
}
void QWSwitch::mouseReleaseEvent(QMouseEvent* event) {
if (mWaitSigModel) {
/// 先运行按钮位置动画
QVariantAnimation* posAnimation = new QVariantAnimation(this);
posAnimation->setDuration(mAnimationPeriod);
posAnimation->setStartValue(mOnOff ? mRightPos : mLeftPos);
posAnimation->setEndValue(mOnOff ? mLeftPos : mRightPos);
connect(posAnimation, &QVariantAnimation::valueChanged, [=](const QVariant& value) {
mButtonRect.moveTo(value.toPointF());
update();
});
posAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped); //停止后删除
return;
}
if (!mEnable) return;
if (mButtonRect.contains(event->pos()) || !mPrecisionClickFlagh) {
setSwitch(!mOnOff);
}
}
void QWSwitch::enterEvent(QEvent* event) {
Q_UNUSED(event)
mHover = true;
}
void QWSwitch::leaveEvent(QEvent* event) {
Q_UNUSED(event)
mHover = false;
}

74
src/qt/QWSwitch.h Normal file
View File

@@ -0,0 +1,74 @@
/*
* 继承自QWidget拨动按钮开关效果将QWidget提升为此类即可使用
* 最为美观
*/
#ifndef SWITCHWIDGET_H
#define SWITCHWIDGET_H
#include <QWidget>
#include <QPropertyAnimation>
#include <QPainterPath>
#include <QPainter>
#include <QRadialGradient>
#include <QMouseEvent>
///
/// \brief 基础控件-Switch开关按钮
///
class QWSwitch : public QWidget
{
Q_OBJECT
public:
Q_PROPERTY(QColor pBackColor MEMBER mBackColor) //新增背景颜色属性,用于动画
explicit QWSwitch(QWidget* parent = nullptr);
bool getSwitch(); /// 获取开关状态
public slots:
void setSwitch(bool onoff); /// 设置开关状态,default:0
void setEnabled(bool enable); /// 设置使能状态,default:1
bool getEnabled(); /// 获取使能状态
void setAnimationPeriod(int period); /// 设置切换状态周期
void setPrecisionClick(bool flag); /// 设置精确点击,即只有点中按钮的时候才开关
void setWaitModel(bool flag); /// 设置等待模式点击后不会主动切换开关需要setSwitch
void setSwitchForWaitModel(bool onoff); /// 设置开关状态,default:0
void setButtonColor(QColor color); /// 设置开关(圆形按钮)颜色
void setBackOnColor(QColor color); /// 设置背景颜色-开
void setBackOffColor(QColor color); /// 设置背景颜色-关
void setEdgeColor(QColor color); /// 设置边缘颜色,默认透明
signals:
void signalEnableChanged(bool enable); /// 使能状态变化信号
void signalSwitchChanged(bool onoff); /// 开关状态变化信号
protected:
void paintEvent(QPaintEvent* event);
void resizeEvent(QResizeEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
private:
bool mOnOff {0}; //开关状态
bool mEnable {1}; //使能状态
bool mPrecisionClickFlagh {0}; //精确点击标志位
//bool mWaitSigModel{1}; //等待模式,点击后按钮位置会进行切换,但是颜色需要等待外部信号变动
bool mWaitSigModel {0}; //非等待模式,点击后按钮位置会进行切换,颜色同时切换
bool mAnimationOnOff {1}; //动画开关default1
bool mHover {0};
QColor mButtonColor {Qt::white}; //开关(圆形按钮)颜色
QColor mBackColor {Qt::red};
QColor mEdgeColor {Qt::transparent}; //边缘颜色
QRectF mButtonRect; //开关按钮rect
int mRadius {8}; // 开关外观边缘圆角
int mAnimationPeriod {200}; //动画周期
QPointF mRightPos; // 动画位置-开
QPointF mLeftPos; // 动画位置-关
QColor mBackOnColor {Qt::green}; //背景颜色-开
QColor mBackOffColor {Qt::darkGray}; //背景颜色-关
};
#endif // SWITCHWIDGET_H

View File

@@ -7,31 +7,34 @@
#include "app/Station.h"
#include "protocol/MqttEntity.h"
#include "qt/QWSwitch.h"
QWHome::QWHome(QWidget* parent) : MyWidget(parent)
{
this->setObjectName("home");
this->setStyleSheet("#home { background-color:rgba(100,100,100,50); }");
int x = 10, y = 0;
{
this->groupSys = MyQUI::GroupBox(this, x, y, 1190, 120, "系统");
auto pw = groupSys.get();
//this->groupSys = MyQUI::GroupBox(this, x, y, 1190, 120, "系统");
//auto pw = groupSys.get();
}
{
x = 10, y += 130;
this->groupHttp = MyQUI::GroupBox(this, x, y, 390, 120, "HTTP");
x = 10;
this->groupHttp = MyQUI::GroupBox(this, x, y, 390, 200, "HTTP");
auto pw = groupHttp.get();
this->addPair("http-t", pw, 20, 20, "服务类型: ", "服务端");
this->addPair("http-p", pw, 20, 50, "服务端口: ", Utils::toStr(Config::option.http.port));
this->addPair("http-s", pw, 20, 80, "服务状态: ", "运行");
QWSwitch* switchBtn = new QWSwitch(this);
switchBtn->setGeometry(30, 110, 50, 20);
}
{
x += 400;
this->groupMqtt = MyQUI::GroupBox(this, x, y, 390, 120, "MQTT");
this->groupMqtt = MyQUI::GroupBox(this, x, y, 390, 200, "MQTT");
auto pw = groupMqtt.get();
this->addPair("mqtt-t", pw, 20, 20, "服务类型: ", "客户端");
this->addPair("mqtt-h", pw, 20, 50, "服务地址: ", Config::option.mqtt.host);
@@ -39,7 +42,7 @@ QWHome::QWHome(QWidget* parent) : MyWidget(parent)
}
{
x += 400;
this->groupDB = MyQUI::GroupBox(this, x, y, 390, 120, "数据库");
this->groupDB = MyQUI::GroupBox(this, x, y, 390, 200, "数据库");
auto pw = groupDB.get();
this->addPair("db-n", pw, 20, 20, "数据库名: ", Config::option.database.dbname);
this->addPair("db-h", pw, 20, 50, "主机地址: ", Config::option.database.host);
@@ -51,7 +54,7 @@ QWHome::QWHome(QWidget* parent) : MyWidget(parent)
<< "日充电\n费用" << "日放电\n费用" << "总充电\n费用" << "总放电\n费用"
<< "日收益" << "总收益" << "--";
table = MyQUI::TableWidget(this, 10, y += 130, 1190, 265);
table = MyQUI::TableWidget(this, 10, y += 210, 1190, 265);
// 设置为水平表头
table->setColumnCount(headerTextList.size());
table->setHorizontalHeaderLabels(headerTextList);
@@ -70,6 +73,7 @@ QWHome::QWHome(QWidget* parent) : MyWidget(parent)
texteditLog->setGeometry(10, y += 30, 1190, 280);
texteditLog->setStyleSheet("background-color: transparent; border: 1px solid gray; font-weight: 400;");
texteditLog->setReadOnly(true);
texteditLog->document()->setMaximumBlockCount(1000);
{
////////////////////////////////////////////////////////////////////////////////////////