mirror of
https://gitee.com/js-yhsec/energy_storage.git
synced 2026-05-27 18:59:26 +08:00
实现服务端的QT应用界面
This commit is contained in:
Binary file not shown.
@@ -6,7 +6,7 @@
|
|||||||
"exportpath": "D:/Programs/openresty-1.27.1.1-win64/zdownload",
|
"exportpath": "D:/Programs/openresty-1.27.1.1-win64/zdownload",
|
||||||
"database": {"host": "localhost", "port": 3306, "user": "root", "passwd": "123456", "dbname": "ess"},
|
"database": {"host": "localhost", "port": 3306, "user": "root", "passwd": "123456", "dbname": "ess"},
|
||||||
"http": {"token":0, "port": 19801, "encryption":0, "encryptKey":""},
|
"http": {"token":0, "port": 19801, "encryption":0, "encryptKey":""},
|
||||||
"mqtt": {"host":"mqtt://43.136.119.46:6203","username":"jsyhsec","password":"123456"},
|
"mqtt": {"host":"mqtt://118.195.161.113:8883","username":"admin","password":"jsyh@2025", "interval": 120},
|
||||||
"topic": {
|
"topic": {
|
||||||
"EMS_YX": {"deviceType":101, "polling":0, "enabled": 1},
|
"EMS_YX": {"deviceType":101, "polling":0, "enabled": 1},
|
||||||
"EMS_YC": {"deviceType":101, "polling":0, "enabled": 1},
|
"EMS_YC": {"deviceType":101, "polling":0, "enabled": 1},
|
||||||
@@ -32,5 +32,5 @@
|
|||||||
"video": {
|
"video": {
|
||||||
"1":{"host":"", "port":9000, "user":"", "passwd":""}
|
"1":{"host":"", "port":9000, "user":"", "passwd":""}
|
||||||
},
|
},
|
||||||
"statistics": {"enabled": 1, "interval": 30}
|
"statistics": {"enabled": 1, "interval": 120}
|
||||||
}
|
}
|
||||||
@@ -547,7 +547,7 @@
|
|||||||
{"key": "0x0041", "datatype": "uint16", "alert": 0, "name":"单体最小SOc簇号", "remark": ""},
|
{"key": "0x0041", "datatype": "uint16", "alert": 0, "name":"单体最小SOc簇号", "remark": ""},
|
||||||
{"key": "0x0042", "datatype": "uint16", "alert": 0, "name":"单体最小SOc节号", "remark": ""},
|
{"key": "0x0042", "datatype": "uint16", "alert": 0, "name":"单体最小SOc节号", "remark": ""},
|
||||||
{"key": "0x0043", "datatype": "uint16", "alert": 0, "name":"单体最小SOc", "remark": "(0.001)"},
|
{"key": "0x0043", "datatype": "uint16", "alert": 0, "name":"单体最小SOc", "remark": "(0.001)"},
|
||||||
{"key": "0x0043", "datatype": "uint32", "alert": 0, "name":"系统剩余最大可充电功率", "remark": "(1KW)"},
|
{"key": "0x0044", "datatype": "uint32", "alert": 0, "name":"系统剩余最大可充电功率", "remark": "(1KW)"},
|
||||||
{"key": "0x0045", "datatype": "uint32", "alert": 0, "name":"系统剩余最大可放电功率", "remark": "(1KW)"},
|
{"key": "0x0045", "datatype": "uint32", "alert": 0, "name":"系统剩余最大可放电功率", "remark": "(1KW)"},
|
||||||
{"key": "0x0047", "datatype": "uint16", "alert": 0, "name":"可充电状态", "remark": ""},
|
{"key": "0x0047", "datatype": "uint16", "alert": 0, "name":"可充电状态", "remark": ""},
|
||||||
{"key": "0x0048", "datatype": "uint16", "alert": 0, "name":"可放电状态", "remark": ""},
|
{"key": "0x0048", "datatype": "uint16", "alert": 0, "name":"可放电状态", "remark": ""},
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 66 KiB |
@@ -69,9 +69,9 @@ bool AppData::initFromDB()
|
|||||||
std::string name = fields.value(DMDefWorkMode::NAME);
|
std::string name = fields.value(DMDefWorkMode::NAME);
|
||||||
this->mapping.policyType.push_back({std::to_string(policyTypeId), name});
|
this->mapping.policyType.push_back({std::to_string(policyTypeId), name});
|
||||||
this->mapPolicyType[policyTypeId] = name;
|
this->mapPolicyType[policyTypeId] = name;
|
||||||
str += ("策略类型: {" + std::to_string(policyTypeId) + ":" + name + "},");
|
//str += ("策略类型: {" + std::to_string(policyTypeId) + ":" + name + "},");
|
||||||
}
|
}
|
||||||
spdlog::info(str);
|
//spdlog::info(str);
|
||||||
}
|
}
|
||||||
{ // 数据库读取设备类型定义
|
{ // 数据库读取设备类型定义
|
||||||
str = "", result.clear();
|
str = "", result.clear();
|
||||||
@@ -87,9 +87,9 @@ bool AppData::initFromDB()
|
|||||||
item->fieldsAttr.parseJson(item->attr);
|
item->fieldsAttr.parseJson(item->attr);
|
||||||
mapDeviceType[item->typeId] = item;
|
mapDeviceType[item->typeId] = item;
|
||||||
mapping.deviceType.push_back({std::to_string(item->typeId), item->name});
|
mapping.deviceType.push_back({std::to_string(item->typeId), item->name});
|
||||||
str += ("设备类型: {" + std::to_string(item->typeId) + ":" + item->name + "},");
|
//str += ("设备类型: {" + std::to_string(item->typeId) + ":" + item->name + "},");
|
||||||
}
|
}
|
||||||
spdlog::info(str);
|
//spdlog::info(str);
|
||||||
}
|
}
|
||||||
{ // 数据库读取角色定义
|
{ // 数据库读取角色定义
|
||||||
str = "", result.clear();
|
str = "", result.clear();
|
||||||
@@ -103,9 +103,9 @@ bool AppData::initFromDB()
|
|||||||
item->isOpen = fields.get<int>(DMRole::IS_OPEN);
|
item->isOpen = fields.get<int>(DMRole::IS_OPEN);
|
||||||
mapRole[item->roleId] = item;
|
mapRole[item->roleId] = item;
|
||||||
mapping.role.push_back({std::to_string(item->roleId), item->name});
|
mapping.role.push_back({std::to_string(item->roleId), item->name});
|
||||||
str += ("角色: {" + std::to_string(item->roleId) + ":" + item->name + "},");
|
//str += ("角色: {" + std::to_string(item->roleId) + ":" + item->name + "},");
|
||||||
}
|
}
|
||||||
spdlog::info(str);
|
//spdlog::info(str);
|
||||||
}
|
}
|
||||||
{ // 数据库读取场站信息
|
{ // 数据库读取场站信息
|
||||||
str = "", result.clear();
|
str = "", result.clear();
|
||||||
@@ -118,9 +118,8 @@ bool AppData::initFromDB()
|
|||||||
station->setFields(fields);
|
station->setFields(fields);
|
||||||
this->mapStation[station->stationId] = station;
|
this->mapStation[station->stationId] = station;
|
||||||
this->mapping.stationName.push_back({std::to_string(station->stationId), station->name});
|
this->mapping.stationName.push_back({std::to_string(station->stationId), station->name});
|
||||||
str += ("场站: {" + std::to_string(station->stationId) + ":" + station->name + "},");
|
spdlog::info("场站: {}:{}, {}", station->stationId, station->name, station->status>0 ? "启用" : "未启用");
|
||||||
}
|
}
|
||||||
spdlog::info(str);
|
|
||||||
}
|
}
|
||||||
{ // 数据库读取设备信息
|
{ // 数据库读取设备信息
|
||||||
str = "", result.clear();
|
str = "", result.clear();
|
||||||
@@ -158,32 +157,32 @@ bool AppData::initFromDB()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{ // 数据库读取电价分段信息
|
{ // 数据库读取电价分段信息
|
||||||
result.clear();
|
//result.clear();
|
||||||
vecElectPeriods.resize(12);
|
//vecElectPeriods.resize(12);
|
||||||
DAO::exec(dao, "SELECT * FROM configure;", result);
|
//DAO::exec(dao, "SELECT * FROM configure;", result);
|
||||||
|
//
|
||||||
|
//Fields info;
|
||||||
|
//for (auto& fields: result)
|
||||||
|
//{
|
||||||
|
// auto k = fields.value("key");
|
||||||
|
// auto v = fields.value("val");
|
||||||
|
// info.set(k, v);
|
||||||
|
//}
|
||||||
|
|
||||||
Fields info;
|
//for (int month = 1; month<=12; month++)
|
||||||
for (auto& fields: result)
|
//{
|
||||||
{
|
// if (month-1 < vecElectPeriods.size())
|
||||||
auto k = fields.value("key");
|
// {
|
||||||
auto v = fields.value("val");
|
// auto& vecItems = vecElectPeriods[month-1];
|
||||||
info.set(k, v);
|
// std::string str = info.value("period_" + std::to_string(month));
|
||||||
}
|
// std::vector<std::string> vec;
|
||||||
|
// Utils::split(str, ",", vecItems);
|
||||||
for (int month = 1; month<=12; month++)
|
// }
|
||||||
{
|
//}
|
||||||
if (month-1 < vecElectPeriods.size())
|
//electPriceSuperPeak = info.get<double>("price_super_peak");
|
||||||
{
|
//electPricePeak = info.get<double>("price_peak");
|
||||||
auto& vecItems = vecElectPeriods[month-1];
|
//electPriceShoulder = info.get<double>("price_shoulder");
|
||||||
std::string str = info.value("period_" + std::to_string(month));
|
//electPriceOffPeak = info.get<double>("price_off_peak");
|
||||||
std::vector<std::string> vec;
|
|
||||||
Utils::split(str, ",", vecItems);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
electPriceSuperPeak = info.get<double>("price_super_peak");
|
|
||||||
electPricePeak = info.get<double>("price_peak");
|
|
||||||
electPriceShoulder = info.get<double>("price_shoulder");
|
|
||||||
electPriceOffPeak = info.get<double>("price_off_peak");
|
|
||||||
}
|
}
|
||||||
{ // 数据库读取统计数据
|
{ // 数据库读取统计数据
|
||||||
vector<Fields> result;
|
vector<Fields> result;
|
||||||
@@ -457,32 +456,32 @@ int AppData::getPolicyTypeId(std::string name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::string> AppData::getElectPreiodVals(int month)
|
//std::vector<std::string> AppData::getElectPreiodVals(int month)
|
||||||
{
|
//{
|
||||||
if (month > 0 && month-1 < vecElectPeriods.size())
|
// if (month > 0 && month-1 < vecElectPeriods.size())
|
||||||
{
|
// {
|
||||||
return vecElectPeriods[month-1];
|
// return vecElectPeriods[month-1];
|
||||||
}
|
// }
|
||||||
return {};
|
// return {};
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
std::string AppData::getElectPreiodVal(int month, int hour)
|
//std::string AppData::getElectPreiodVal(int month, int hour)
|
||||||
{
|
//{
|
||||||
if (month > 0 && month-1 < vecElectPeriods.size())
|
// if (month > 0 && month-1 < vecElectPeriods.size())
|
||||||
{
|
// {
|
||||||
auto& vec = vecElectPeriods[month-1];
|
// auto& vec = vecElectPeriods[month-1];
|
||||||
if (hour > 0 && hour-1 < vec.size())
|
// if (hour > 0 && hour-1 < vec.size())
|
||||||
{
|
// {
|
||||||
auto& val = vec[hour-1];
|
// auto& val = vec[hour-1];
|
||||||
if (val == "尖") return "尖峰";
|
// if (val == "尖") return "尖峰";
|
||||||
if (val == "峰") return "高峰";
|
// if (val == "峰") return "高峰";
|
||||||
if (val == "平") return "平段";
|
// if (val == "平") return "平段";
|
||||||
if (val == "谷") return "低谷";
|
// if (val == "谷") return "低谷";
|
||||||
return val;
|
// return val;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return "";
|
// return "";
|
||||||
}
|
//}
|
||||||
|
|
||||||
void AppData::storeRuntimeDB()
|
void AppData::storeRuntimeDB()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -99,9 +99,8 @@ public:
|
|||||||
// 根据策略类型名称获取策略类型ID
|
// 根据策略类型名称获取策略类型ID
|
||||||
int getPolicyTypeId(std::string name);
|
int getPolicyTypeId(std::string name);
|
||||||
|
|
||||||
std::vector<std::string> getElectPreiodVals(int month);
|
//std::vector<std::string> getElectPreiodVals(int month);
|
||||||
|
//std::string getElectPreiodVal(int month, int hour);
|
||||||
std::string getElectPreiodVal(int month, int hour);
|
|
||||||
|
|
||||||
void storeRuntimeDB();
|
void storeRuntimeDB();
|
||||||
|
|
||||||
@@ -134,10 +133,12 @@ public:
|
|||||||
VecPairSS stationName;
|
VecPairSS stationName;
|
||||||
} mapping;
|
} mapping;
|
||||||
|
|
||||||
double electPriceSuperPeak {};
|
//double electPriceSuperPeak {};
|
||||||
double electPricePeak {};
|
//double electPricePeak {};
|
||||||
double electPriceShoulder {};
|
//double electPriceShoulder {};
|
||||||
double electPriceOffPeak {};
|
//double electPriceOffPeak {};
|
||||||
|
// 电力峰谷分段 (12个月,每个月按小时分成24个时段)
|
||||||
|
//std::vector<std::vector<std::string>> vecElectPeriods;
|
||||||
|
|
||||||
// 场站信息
|
// 场站信息
|
||||||
std::unordered_map<int, std::shared_ptr<Station>> mapStation;
|
std::unordered_map<int, std::shared_ptr<Station>> mapStation;
|
||||||
@@ -157,8 +158,7 @@ public:
|
|||||||
// 策略信息
|
// 策略信息
|
||||||
std::unordered_map<int, std::shared_ptr<SysPolicy>> mapPolicy;
|
std::unordered_map<int, std::shared_ptr<SysPolicy>> mapPolicy;
|
||||||
|
|
||||||
// 电力峰谷分段 (12个月,每个月按小时分成24个时段)
|
|
||||||
std::vector<std::vector<std::string>> vecElectPeriods;
|
|
||||||
|
|
||||||
std::map<int64_t, double> mapDataDay;
|
std::map<int64_t, double> mapDataDay;
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ void Application::runThreadDevice()
|
|||||||
|
|
||||||
void Application::runThreadMain()
|
void Application::runThreadMain()
|
||||||
{
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(10)); // 延迟10秒执行
|
||||||
|
|
||||||
while (!isQuit)
|
while (!isQuit)
|
||||||
{
|
{
|
||||||
if (!this->isInit) // 初始化失败
|
if (!this->isInit) // 初始化失败
|
||||||
@@ -84,29 +86,31 @@ void Application::runThreadMain()
|
|||||||
|
|
||||||
static TimeTick ttMqtt; // 检查 场站的 MQTT 连接
|
static TimeTick ttMqtt; // 检查 场站的 MQTT 连接
|
||||||
if (ttMqtt.elapse(30))
|
if (ttMqtt.elapse(30))
|
||||||
{
|
|
||||||
auto& optionMqtt = Config::option.mqtt;
|
|
||||||
if (!optionMqtt.host.empty())
|
|
||||||
{
|
{
|
||||||
for (auto& item : appdata.mapStation)
|
for (auto& item : appdata.mapStation)
|
||||||
{
|
{
|
||||||
auto& station = item.second;
|
auto& station = item.second;
|
||||||
if (station)
|
// 检查MQTT的连接状态
|
||||||
{
|
if (!Config::option.mqtt.host.empty()) { station->initMqtt(); }
|
||||||
if (station->isOpen)
|
|
||||||
{
|
|
||||||
// 该函数检查连接状态,若已经连接,则无操作;若未连接,则进行连接操作
|
|
||||||
item.second->initMqtt();
|
|
||||||
// 召测
|
|
||||||
item.second->polling();
|
|
||||||
}
|
|
||||||
// 检查设备的在线状态
|
// 检查设备的在线状态
|
||||||
station->checkDevice();
|
station->checkDevice();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TimeTick ttMqttPolling; // 召测
|
||||||
|
if (!Config::option.mqtt.host.empty() && ttMqttPolling.elapse(10))
|
||||||
|
{
|
||||||
|
for (auto& item : appdata.mapStation)
|
||||||
|
{
|
||||||
|
auto& station = item.second;
|
||||||
|
if (Utils::time() - station->getPollingTS() > Config::option.mqtt.interval)
|
||||||
|
{
|
||||||
|
item.second->polling();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::runThreadStat()
|
void Application::runThreadStat()
|
||||||
|
|||||||
@@ -47,8 +47,7 @@ bool Config::init(std::string filename)
|
|||||||
JSON::read(json, "token", option.http.useToken);
|
JSON::read(json, "token", option.http.useToken);
|
||||||
JSON::read(json, "port", option.http.port);
|
JSON::read(json, "port", option.http.port);
|
||||||
JSON::read(json, "encryption", option.http.encryption);
|
JSON::read(json, "encryption", option.http.encryption);
|
||||||
JSON::read(json, "encryptKey", option.http.encryptKey);
|
JSON::read(json, "encryptKey", option.http.encryptKey); }
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
spdlog::error("[config] parse http failed: not found.");
|
spdlog::error("[config] parse http failed: not found.");
|
||||||
@@ -60,6 +59,7 @@ bool Config::init(std::string filename)
|
|||||||
JSON::read(json, "host", option.mqtt.host);
|
JSON::read(json, "host", option.mqtt.host);
|
||||||
JSON::read(json, "username", option.mqtt.username);
|
JSON::read(json, "username", option.mqtt.username);
|
||||||
JSON::read(json, "password", option.mqtt.password);
|
JSON::read(json, "password", option.mqtt.password);
|
||||||
|
JSON::read(json, "interval", option.mqtt.interval);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ struct AppOption
|
|||||||
std::string host;
|
std::string host;
|
||||||
std::string username;
|
std::string username;
|
||||||
std::string password;
|
std::string password;
|
||||||
|
int interval {60};
|
||||||
} mqtt;
|
} mqtt;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|||||||
@@ -317,7 +317,10 @@ void Device::setParam(std::string k, int v)
|
|||||||
}
|
}
|
||||||
else if (type == int(EDeviceType::BMS)) // 104 BMS
|
else if (type == int(EDeviceType::BMS)) // 104 BMS
|
||||||
{
|
{
|
||||||
if (k == "0x0049") { err = (v==1); } //运行状态 R uint16 0 运行状态 0-正常 1-告警 2-保护 0x0049
|
if (k == "0x0049")
|
||||||
|
{
|
||||||
|
err = (v==1);
|
||||||
|
} //运行状态 R uint16 0 运行状态 0-正常 1-告警 2-保护 0x0049
|
||||||
else if (k == "0x004A") { running = (v==1 || v==2); } //充放电状态 R uint16 0 0-待机 1-充电 2-放电 0x004A
|
else if (k == "0x004A") { running = (v==1 || v==2); } //充放电状态 R uint16 0 0-待机 1-充电 2-放电 0x004A
|
||||||
}
|
}
|
||||||
else if (type == int(EDeviceType::BCU)) // BCU
|
else if (type == int(EDeviceType::BCU)) // BCU
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ void Station::setFields(Fields& fields)
|
|||||||
this->workMode = fields.get<int>(DMStation::WORK_MODE);
|
this->workMode = fields.get<int>(DMStation::WORK_MODE);
|
||||||
this->code = fields.value(DMStation::CODE);
|
this->code = fields.value(DMStation::CODE);
|
||||||
this->status = fields.get<int>(DMStation::STATUS);
|
this->status = fields.get<int>(DMStation::STATUS);
|
||||||
|
|
||||||
this->operationDate = fields.value(DMStation::OPERATION_DATE);
|
this->operationDate = fields.value(DMStation::OPERATION_DATE);
|
||||||
this->isOpen = fields.get<int>(DMStation::STATUS);
|
|
||||||
this->launchDate = fields.value("operation_date");
|
this->launchDate = fields.value("operation_date");
|
||||||
this->policy.setFields(fields);
|
this->policy.setFields(fields);
|
||||||
}
|
}
|
||||||
@@ -156,10 +156,23 @@ void Station::initMqtt()
|
|||||||
|
|
||||||
void Station::polling()
|
void Station::polling()
|
||||||
{
|
{
|
||||||
if (mqttCli)
|
if (status > 0 && mqttCli)
|
||||||
|
{
|
||||||
|
if (mqttCli->isConnected)
|
||||||
{
|
{
|
||||||
mqttCli->polling();
|
mqttCli->polling();
|
||||||
}
|
}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// // 该函数检查连接状态,若已经连接,则无操作;若未连接,则进行连接操作
|
||||||
|
// this->initMqtt();
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t Station::getPollingTS()
|
||||||
|
{
|
||||||
|
return (mqttCli != nullptr) ? mqttCli->tsPolling : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Station::setGarewayWorkMode()
|
void Station::setGarewayWorkMode()
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ public:
|
|||||||
|
|
||||||
void initMqtt();
|
void initMqtt();
|
||||||
void polling();
|
void polling();
|
||||||
|
int64_t getPollingTS();
|
||||||
void setGarewayWorkMode();
|
void setGarewayWorkMode();
|
||||||
void checkDevice();
|
void checkDevice();
|
||||||
|
|
||||||
@@ -129,7 +130,6 @@ public:
|
|||||||
int stationId {};
|
int stationId {};
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string code;
|
std::string code;
|
||||||
bool isOpen {false};
|
|
||||||
int status {0};
|
int status {0};
|
||||||
std::string operationDate;
|
std::string operationDate;
|
||||||
SysPolicy policy;
|
SysPolicy policy;
|
||||||
@@ -236,6 +236,28 @@ public:
|
|||||||
float dayFeeOut {0.0}; // 日放电费用 R uint32 1RMB 0 0x1114
|
float dayFeeOut {0.0}; // 日放电费用 R uint32 1RMB 0 0x1114
|
||||||
float dayIncome {0.0}; // 日收益 R int32 1RMB 0 0x1116
|
float dayIncome {0.0}; // 日收益 R int32 1RMB 0 0x1116
|
||||||
|
|
||||||
|
//日正向尖有功电能 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 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
|
||||||
|
|
||||||
} statData;
|
} statData;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#include "Spdlogger.h"
|
#include "Spdlogger.h"
|
||||||
|
|
||||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
#include <spdlog/sinks/daily_file_sink.h>
|
#include "spdlog/sinks/daily_file_sink.h"
|
||||||
|
|
||||||
|
|
||||||
//std::shared_ptr<spdlog::logger> Spdlogger::logger_ = spdlog::stdout_color_mt("sys");
|
//std::shared_ptr<spdlog::logger> Spdlogger::logger_ = spdlog::stdout_color_mt("sys");
|
||||||
//std::shared_ptr<spdlog::logger> Spdlogger::logger = spdlog::daily_logger_mt("daily_logger", "ees.log", 0, 0);
|
//std::shared_ptr<spdlog::logger> Spdlogger::logger = spdlog::daily_logger_mt("daily_logger", "ees.log", 0, 0);
|
||||||
@@ -12,18 +13,21 @@ void Spdlogger::init(spdlog::level::level_enum log_level, std::string filename)
|
|||||||
//logger = spdlog::daily_logger_mt("daily_logger", filename, 0, 0);
|
//logger = spdlog::daily_logger_mt("daily_logger", filename, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//logger_ = spdlog::stdout_color_mt("sys");
|
std::vector<spdlog::sink_ptr> mySinks;
|
||||||
//logger_ = spdlog::daily_logger_mt("daily_logger", "log/pvs.log", 0, 0);
|
|
||||||
// set log level : spdlog::level::debug
|
|
||||||
//logger->set_level(log_level);
|
|
||||||
//logger->flush_on(log_level);
|
|
||||||
|
|
||||||
|
|
||||||
|
bool consoleEnabled = false;
|
||||||
// 创建控制台接收器
|
// 创建控制台接收器
|
||||||
|
if (consoleEnabled)
|
||||||
|
{
|
||||||
auto consoleSink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
auto consoleSink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
||||||
//consoleSink->set_level(log_level); // 设置控制台日志等级
|
//consoleSink->set_level(log_level); // 设置控制台日志等级
|
||||||
//consoleSink->set_pattern("[%T] [%^%l%$] %v"); // 设置日志格式
|
//consoleSink->set_pattern("[%T] [%^%l%$] %v"); // 设置日志格式
|
||||||
|
mySinks.push_back(consoleSink);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fileEnabled = true;
|
||||||
|
if (fileEnabled)
|
||||||
|
{
|
||||||
// 创建文件接收器
|
// 创建文件接收器
|
||||||
//auto fileSink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/mcs.log", true);
|
//auto fileSink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/mcs.log", true);
|
||||||
//fileSink->set_level(spdlog::level::debug); // 设置文件日志等级
|
//fileSink->set_level(spdlog::level::debug); // 设置文件日志等级
|
||||||
@@ -34,9 +38,11 @@ void Spdlogger::init(spdlog::level::level_enum log_level, std::string filename)
|
|||||||
//dailySink->set_level(log_level);
|
//dailySink->set_level(log_level);
|
||||||
//dailySink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
|
//dailySink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
|
||||||
|
|
||||||
|
mySinks.push_back(dailySink);
|
||||||
|
}
|
||||||
|
|
||||||
// 创建一个多重接收器的 logger
|
// 创建一个多重接收器的 logger
|
||||||
std::vector<spdlog::sink_ptr> sinks {consoleSink, dailySink};
|
auto logger = std::make_shared<spdlog::logger>("", mySinks.begin(), mySinks.end());
|
||||||
auto logger = std::make_shared<spdlog::logger>("", sinks.begin(), sinks.end());
|
|
||||||
|
|
||||||
// 设置全局 logger
|
// 设置全局 logger
|
||||||
spdlog::set_default_logger(logger);
|
spdlog::set_default_logger(logger);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
#include <spdlog/sinks/stdout_color_sinks.h> // 彩色控制台日志
|
#include <spdlog/sinks/stdout_color_sinks.h> // 彩色控制台日志
|
||||||
#include <spdlog/sinks/basic_file_sink.h> // 文件日志
|
#include <spdlog/sinks/basic_file_sink.h> // 文件日志
|
||||||
|
#include "spdlog/sinks/qt_sinks.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class Spdlogger
|
class Spdlogger
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "common/JsonN.h"
|
#include "common/JsonN.h"
|
||||||
#include "app/Application.h"
|
#include "app/Application.h"
|
||||||
#include "app/AppData.h"
|
#include "app/AppData.h"
|
||||||
|
#include "common/Crypto.h"
|
||||||
|
|
||||||
std::string DAO::sqlPageLimit(int index, int size)
|
std::string DAO::sqlPageLimit(int index, int size)
|
||||||
{
|
{
|
||||||
@@ -169,8 +170,6 @@ Errcode DAO::login(std::shared_ptr<DaoEntity> dao, std::string account, std::str
|
|||||||
if (!dao) { dao = std::make_shared<DaoEntity>(""); }
|
if (!dao) { dao = std::make_shared<DaoEntity>(""); }
|
||||||
if (!dao->isConnected())
|
if (!dao->isConnected())
|
||||||
{
|
{
|
||||||
|
|
||||||
//DAO1::writeSystemLog(dao, 2, "", account, "用户登录失败:" + err);
|
|
||||||
return Errcode::ERR_DB_CONN;
|
return Errcode::ERR_DB_CONN;
|
||||||
}
|
}
|
||||||
std::string t = Utils::timeStr();
|
std::string t = Utils::timeStr();
|
||||||
@@ -181,12 +180,10 @@ Errcode DAO::login(std::shared_ptr<DaoEntity> dao, std::string account, std::str
|
|||||||
int ret = dao->exec(sql, result);
|
int ret = dao->exec(sql, result);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
{
|
{
|
||||||
//DAO1::writeSystemLog(dao, 2, "", account, "用户登录失败:" + err);
|
|
||||||
return Errcode(ret);
|
return Errcode(ret);
|
||||||
}
|
}
|
||||||
if (result.size() <=0)
|
if (result.size() <=0)
|
||||||
{
|
{
|
||||||
//DAO1::writeSystemLog(dao, 2, "", account, "用户登录失败:" + err);
|
|
||||||
return Errcode::ERR_LOGIN_USER_NOTEXIST;
|
return Errcode::ERR_LOGIN_USER_NOTEXIST;
|
||||||
}
|
}
|
||||||
fields = result[0];
|
fields = result[0];
|
||||||
@@ -194,9 +191,9 @@ Errcode DAO::login(std::shared_ptr<DaoEntity> dao, std::string account, std::str
|
|||||||
int loginCount = fields.get<int>("login_count");
|
int loginCount = fields.get<int>("login_count");
|
||||||
|
|
||||||
// 判断密码
|
// 判断密码
|
||||||
if (passwd != fields.remove("passwd"))
|
|
||||||
|
if (passwd != Crypto::sm3(fields.remove("passwd")))
|
||||||
{
|
{
|
||||||
//DAO1::writeSystemLog(dao, 2, userId, account, "用户登录失败:" + err);
|
|
||||||
return Errcode::ERR_LOGIN_PASSWD;
|
return Errcode::ERR_LOGIN_PASSWD;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,8 +568,8 @@ Errcode DAO::queryStatStationGroup(std::shared_ptr<DaoEntity> dao, string statio
|
|||||||
}
|
}
|
||||||
if (!category.empty() && category != "0")
|
if (!category.empty() && category != "0")
|
||||||
{
|
{
|
||||||
if (!sqlCondition.empty()) sqlCondition += " AND ";
|
//if (!sqlCondition.empty()) sqlCondition += " AND ";
|
||||||
sqlCondition += "category='" + category + "'";;
|
//sqlCondition += "category='" + category + "'";;
|
||||||
}
|
}
|
||||||
if (!sqlCondition.empty()) { sqlCondition = " WHERE " + sqlCondition; }
|
if (!sqlCondition.empty()) { sqlCondition = " WHERE " + sqlCondition; }
|
||||||
|
|
||||||
@@ -615,8 +612,8 @@ Errcode DAO::queryStatStationList(PageInfo& pageInfo, Fields& params, vector<Fie
|
|||||||
}
|
}
|
||||||
if (!category.empty() && category != "0")
|
if (!category.empty() && category != "0")
|
||||||
{
|
{
|
||||||
if (!sqlCondition.empty()) sqlCondition += " AND ";
|
//if (!sqlCondition.empty()) sqlCondition += " AND ";
|
||||||
sqlCondition += "ss.category='" + category + "'";;
|
//sqlCondition += "ss.category='" + category + "'";;
|
||||||
}
|
}
|
||||||
if (!sqlCondition.empty()) { sqlCondition = " WHERE " + sqlCondition; }
|
if (!sqlCondition.empty()) { sqlCondition = " WHERE " + sqlCondition; }
|
||||||
|
|
||||||
|
|||||||
BIN
src/icon.ico
Normal file
BIN
src/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
@@ -171,9 +171,13 @@ int main(int argc, char** argv)
|
|||||||
// 设置控制台输入为 UTF-8 编码(如果需要输入中文)
|
// 设置控制台输入为 UTF-8 编码(如果需要输入中文)
|
||||||
SetConsoleCP(CP_UTF8);
|
SetConsoleCP(CP_UTF8);
|
||||||
|
|
||||||
|
// 启动数据库服务
|
||||||
|
system("net start mysql");
|
||||||
|
|
||||||
//OpensslSM2_test();
|
//OpensslSM2_test();
|
||||||
//SM2_test();
|
//SM2_test();
|
||||||
|
|
||||||
|
|
||||||
// 初始化日志
|
// 初始化日志
|
||||||
Spdlogger::init(spdlog::level::info, "");
|
Spdlogger::init(spdlog::level::info, "");
|
||||||
spdlog::info("[main] start ... ======================================================================");
|
spdlog::info("[main] start ... ======================================================================");
|
||||||
@@ -181,7 +185,7 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
// 运行后台服务
|
// 运行后台服务
|
||||||
Application::instance().init();
|
Application::instance().init();
|
||||||
|
std::cout << "ess server is running ... " << std::endl;
|
||||||
|
|
||||||
if (Config::option.windowEnabled)
|
if (Config::option.windowEnabled)
|
||||||
{
|
{
|
||||||
@@ -194,6 +198,7 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|||||||
@@ -369,7 +369,7 @@ Errcode HttpEntity::login(const httplib::Request& req, njson& json, std::string&
|
|||||||
json["permission"] = nodePermission;
|
json["permission"] = nodePermission;
|
||||||
}
|
}
|
||||||
|
|
||||||
DAO::insertSystemLogUser(token, "用户登录:" + ErrcodeStr(err), (err==Errcode::OK) ? 1: 0);
|
DAO::insertSystemLogUser(token, "用户[" + account + "]登录:" + ErrcodeStr(err), (err==Errcode::OK) ? 1: 0);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -636,6 +636,7 @@ Errcode HttpEntity::updateStation(const httplib::Request& req, njson& json, std:
|
|||||||
params.check("lat", "", "0.0");
|
params.check("lat", "", "0.0");
|
||||||
params.check("status", "", "1");
|
params.check("status", "", "1");
|
||||||
params.check("policy_id", "", "NULL");
|
params.check("policy_id", "", "NULL");
|
||||||
|
params.check("operation_date", "", "NULL");
|
||||||
Errcode err = DAO::updateStationById(params);
|
Errcode err = DAO::updateStationById(params);
|
||||||
if (err == Errcode::OK)
|
if (err == Errcode::OK)
|
||||||
{
|
{
|
||||||
@@ -861,7 +862,7 @@ Errcode HttpEntity::queryDevicByCategory(const httplib::Request& req, njson& jso
|
|||||||
|
|
||||||
njson jsondata = njson::array();
|
njson jsondata = njson::array();
|
||||||
auto station = Application::data().getStation(stationId);
|
auto station = Application::data().getStation(stationId);
|
||||||
if (station && station->status == 1)
|
if (station)
|
||||||
{
|
{
|
||||||
std::vector<std::shared_ptr<Device>> vecDevice;
|
std::vector<std::shared_ptr<Device>> vecDevice;
|
||||||
station->getDeviceByCategory(category, vecDevice);
|
station->getDeviceByCategory(category, vecDevice);
|
||||||
@@ -1158,8 +1159,8 @@ static std::string VerifyStatSqlCondition(Fields& params)
|
|||||||
}
|
}
|
||||||
if (!category.empty() && category != "0")
|
if (!category.empty() && category != "0")
|
||||||
{
|
{
|
||||||
if (!sqlCondition.empty()) sqlCondition += " AND ";
|
//if (!sqlCondition.empty()) sqlCondition += " AND ";
|
||||||
sqlCondition += "ss.category='" + category + "'";;
|
//sqlCondition += "ss.category='" + category + "'";;
|
||||||
}
|
}
|
||||||
if (!sqlCondition.empty()) { sqlCondition = " WHERE " + sqlCondition; }
|
if (!sqlCondition.empty()) { sqlCondition = " WHERE " + sqlCondition; }
|
||||||
return sqlCondition;
|
return sqlCondition;
|
||||||
@@ -1399,8 +1400,8 @@ Errcode HttpEntity::exportStatReport(const httplib::Request& req, njson& json, s
|
|||||||
std::string endDate = params.value("end_date");
|
std::string endDate = params.value("end_date");
|
||||||
|
|
||||||
std::string sql = "SELECT s.name station_name, sd.* FROM stat_day sd LEFT JOIN station s ON sd.station_id=s.station_id";
|
std::string sql = "SELECT s.name station_name, sd.* FROM stat_day sd LEFT JOIN station s ON sd.station_id=s.station_id";
|
||||||
" WHERE station_id = '" + stationId + "' AND category = '" + category + "'"
|
sql += " WHERE sd.station_id = '" + stationId + "' AND category = '" + category + "'";
|
||||||
" AND dt BETWEEN '" + startDate + "' AND '" + endDate + "2025-09-19' AND sd.category = '1'";
|
sql += " AND dt BETWEEN '" + startDate + "' AND '" + endDate + "2025-09-19' AND sd.category = '1'";
|
||||||
|
|
||||||
std::string filename;
|
std::string filename;
|
||||||
std::vector<Fields> result;
|
std::vector<Fields> result;
|
||||||
|
|||||||
@@ -218,6 +218,7 @@ int MqttClient::polling()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tsPolling = Utils::time();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,6 +257,7 @@ void MqttClient::onConnectSuccess( MQTTAsync_successData* resp)
|
|||||||
spdlog::info("[mqtt] connect to {} success, clientId={}.", addr, clientId);
|
spdlog::info("[mqtt] connect to {} success, clientId={}.", addr, clientId);
|
||||||
this->isConnected = true;
|
this->isConnected = true;
|
||||||
this->subscribe();
|
this->subscribe();
|
||||||
|
this->polling();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttClient::onConnectFaiure(MQTTAsync_failureData* resp)
|
void MqttClient::onConnectFaiure(MQTTAsync_failureData* resp)
|
||||||
@@ -302,9 +304,9 @@ int MqttClient::onMessageArrived(char* topic, int topicLen, MQTTAsync_message* m
|
|||||||
std::string key = item.key();
|
std::string key = item.key();
|
||||||
auto& val = item.value();
|
auto& val = item.value();
|
||||||
if (key == "40001") { station->readGatewayMode(val.get<int>()); }
|
if (key == "40001") { station->readGatewayMode(val.get<int>()); }
|
||||||
else if (key == "40002") { spdlog::info("[mqtt] read register addr: [{}]={}, {}", key, val.dump(), "峰谷时间段"); }
|
//else if (key == "40002") { spdlog::info("[mqtt] read register addr: [{}]={}, {}", key, val.dump(), "峰谷时间段"); }
|
||||||
else if (key == "40021") { spdlog::info("[mqtt] read register addr: [{}]={}, {}", key, val.dump(), "自定时间段"); }
|
//else if (key == "40021") { spdlog::info("[mqtt] read register addr: [{}]={}, {}", key, val.dump(), "自定时间段"); }
|
||||||
else if (key == "40038") { spdlog::info("[mqtt] read register addr: [{}]={}, {}", key, val.dump(), "其他参数"); }
|
//else if (key == "40038") { spdlog::info("[mqtt] read register addr: [{}]={}, {}", key, val.dump(), "其他参数"); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (command == "Gateway_YX")
|
else if (command == "Gateway_YX")
|
||||||
@@ -388,13 +390,13 @@ void MqttClient::ParseArrivedMessage(njson& json, string command, std::shared_pt
|
|||||||
{
|
{
|
||||||
auto addr = iter->first;
|
auto addr = iter->first;
|
||||||
auto& regUnit = iter->second;
|
auto& regUnit = iter->second;
|
||||||
|
spdlog::debug("[mqtt] read [{}]={}, {}{}", addr, val, regUnit.name, regUnit.remark);
|
||||||
|
|
||||||
if (regUnit.alert && val > 0)
|
if (regUnit.alert && val > 0)
|
||||||
{
|
{
|
||||||
station->readAlert(device, addr, val, "[" + command + "]" + regUnit.name + "(" + addr + ")");
|
station->readAlert(device, addr, val, "[" + command + "]" + regUnit.name + "(" + addr + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
device->setParam(addr, val);
|
device->setParam(addr, val);
|
||||||
spdlog::debug("[mqtt] read [{}]={}, {}{}", addr, val, regUnit.name, regUnit.remark);
|
|
||||||
|
|
||||||
if (command == "MEM_YC") { station->readRuntimeData(deviceNo, addr, val); }
|
if (command == "MEM_YC") { station->readRuntimeData(deviceNo, addr, val); }
|
||||||
else if (command == "Fire40_YX") { station->readFire40Data(deviceNo, addr, val); }
|
else if (command == "Fire40_YX") { station->readFire40Data(deviceNo, addr, val); }
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public:
|
|||||||
int qos {0};
|
int qos {0};
|
||||||
bool isConnected {false};
|
bool isConnected {false};
|
||||||
bool isSubscribed {false};
|
bool isSubscribed {false};
|
||||||
|
int64_t tsPolling {0};
|
||||||
static std::map<std::string, TopicInfo> s_mapTopicInfo;
|
static std::map<std::string, TopicInfo> s_mapTopicInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "MainApp.h"
|
#include "MainApp.h"
|
||||||
|
#include "common/Spdlogger.h"
|
||||||
|
|
||||||
#include <QtCore/QVariant>
|
#include <QtCore/QVariant>
|
||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
@@ -10,6 +11,16 @@
|
|||||||
#include <QtWidgets/QWidget>
|
#include <QtWidgets/QWidget>
|
||||||
#include <QPalette>
|
#include <QPalette>
|
||||||
#include <QGroupBox>
|
#include <QGroupBox>
|
||||||
|
#include <QTableWidget>
|
||||||
|
#include <QHeaderView>
|
||||||
|
|
||||||
|
#include "app/Config.h"
|
||||||
|
#include "common/Utils.h"
|
||||||
|
#include "QUI.h"
|
||||||
|
#include "app/Application.h"
|
||||||
|
#include "app/AppData.h"
|
||||||
|
#include "app/Station.h"
|
||||||
|
#include "protocol/MqttEntity.h"
|
||||||
|
|
||||||
static const std::string QSS_BTN_MENU =
|
static const std::string QSS_BTN_MENU =
|
||||||
"QPushButton {background:rgba(50,128,218,200);color:white;border-radius:5px;border:2px solid rgb(10,120,215);font:bold 18px;}"
|
"QPushButton {background:rgba(50,128,218,200);color:white;border-radius:5px;border:2px solid rgb(10,120,215);font:bold 18px;}"
|
||||||
@@ -18,11 +29,7 @@ static const std::string QSS_BTN_MENU =
|
|||||||
"QPushButton:disabled {color:rgb(150,150,150);}";
|
"QPushButton:disabled {color:rgb(150,150,150);}";
|
||||||
|
|
||||||
static const std::string QSS_LINE =
|
static const std::string QSS_LINE =
|
||||||
"QLineEdit { background-color: rgb(14, 49, 66); color: #ffffff; border: 1px solid rgb(18, 251, 255); border-radius: 5px; font: normal 13px; }";
|
"QLineEdit { background-color: rgb(14, 49, 66); color: #ffffff; border: 1px solid gray; border-radius: 5px; font: bold 13px; }";
|
||||||
|
|
||||||
static const std::string QSS_GROUP =
|
|
||||||
"QGroupBox { border: 1px solid gray; margin-top: 8px; border-radius: 5px;}"
|
|
||||||
"QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; left:10px; margin-left: 0px; padding:0 1px; }";
|
|
||||||
|
|
||||||
MainApp::MainApp()
|
MainApp::MainApp()
|
||||||
{
|
{
|
||||||
@@ -45,8 +52,14 @@ MainApp::MainApp()
|
|||||||
//ui.weburl->setValue("http://www.baidu.com");
|
//ui.weburl->setValue("http://www.baidu.com");
|
||||||
|
|
||||||
this->setMyLayout();
|
this->setMyLayout();
|
||||||
|
|
||||||
|
timer = std::make_shared<QTimer>(this);
|
||||||
|
connect(timer.get(), &QTimer::timeout, this, &MainApp::onTimer);
|
||||||
|
timer->start(1000); // 每隔1000毫秒更新一次
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MyMenu : public QWidget
|
class MyMenu : public QWidget
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -61,7 +74,7 @@ public:
|
|||||||
layout.setContentsMargins(2, 2, 2, 2);
|
layout.setContentsMargins(2, 2, 2, 2);
|
||||||
|
|
||||||
this->addMenuItem("系统总览");
|
this->addMenuItem("系统总览");
|
||||||
this->addMenuItem("运行监控");
|
//this->addMenuItem("运行监控");
|
||||||
}
|
}
|
||||||
|
|
||||||
void addMenuItem(std::string name)
|
void addMenuItem(std::string name)
|
||||||
@@ -95,8 +108,9 @@ void PairLine(QWidget* parent, int x, int y, string k, string v)
|
|||||||
key->setGeometry(x, y, 80, 26);
|
key->setGeometry(x, y, 80, 26);
|
||||||
auto value = new QLineEdit(parent);
|
auto value = new QLineEdit(parent);
|
||||||
value->setText(v.c_str());
|
value->setText(v.c_str());
|
||||||
value->setGeometry(x+80, y, 180, 26);
|
value->setGeometry(x+80, y, 260, 26);
|
||||||
value->setStyleSheet(QSS_LINE.c_str());
|
value->setStyleSheet(QSS_LINE.c_str());
|
||||||
|
value->setReadOnly(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -110,36 +124,144 @@ public:
|
|||||||
|
|
||||||
int x=10, y=10;
|
int x=10, y=10;
|
||||||
{
|
{
|
||||||
QGroupBox* groupBox = new QGroupBox("HTTP", this);
|
this->groupSys = UI::GroupBox(this, x, y, 1190, 120, "系统");
|
||||||
groupBox->setGeometry(x, y, 300, 120);
|
auto pw = groupSys.get();
|
||||||
groupBox->setStyleSheet(QSS_GROUP.c_str());
|
|
||||||
|
|
||||||
PairLine(groupBox, 20, 20, "服务类型: ", "服务端");
|
|
||||||
PairLine(groupBox, 20, 50, "服务地址: ", "92.168.0.13:17900");
|
|
||||||
PairLine(groupBox, 20, 80, "服务状态: ", "---");
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
x += 320;
|
x = 10, y += 130;
|
||||||
QGroupBox* groupBox = new QGroupBox("MQTT", this);
|
this->groupHttp = UI::GroupBox(this, x, y, 390, 120, "HTTP");
|
||||||
groupBox->setGeometry(x, y, 300, 120);
|
auto pw = groupHttp.get();
|
||||||
groupBox->setStyleSheet(QSS_GROUP.c_str());
|
PairLine(pw, 20, 20, "服务类型: ", "服务端");
|
||||||
|
PairLine(pw, 20, 50, "服务端口: ", Utils::toStr(Config::option.http.port));
|
||||||
PairLine(groupBox, 20, 20, "服务类型: ", "客户端");
|
PairLine(pw, 20, 80, "服务状态: ", "运行");
|
||||||
PairLine(groupBox, 20, 50, "服务地址: ", "92.168.0.13:17800");
|
|
||||||
PairLine(groupBox, 20, 80, "服务状态: ", "---");
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
x += 320;
|
x += 400;
|
||||||
QGroupBox* groupBox = new QGroupBox("数据库", this);
|
this->groupMqtt = UI::GroupBox(this, x, y, 390, 120, "MQTT");
|
||||||
groupBox->setGeometry(x, y, 300, 120);
|
auto pw = groupMqtt.get();
|
||||||
groupBox->setStyleSheet(QSS_GROUP.c_str());
|
PairLine(pw, 20, 20, "服务类型: ", "客户端");
|
||||||
|
PairLine(pw, 20, 50, "服务地址: ", Config::option.mqtt.host);
|
||||||
|
PairLine(pw, 20, 80, "服务状态: ", "---");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
x += 400;
|
||||||
|
this->groupDB = UI::GroupBox(this, x, y, 390, 120, "数据库");
|
||||||
|
auto pw = groupDB.get();
|
||||||
|
PairLine(pw, 20, 20, "数据库名: ", Config::option.database.dbname);
|
||||||
|
PairLine(pw, 20, 50, "主机地址: ", Config::option.database.host);
|
||||||
|
PairLine(pw, 20, 80, "用 户 名: ", Config::option.database.user);
|
||||||
|
|
||||||
PairLine(groupBox, 20, 20, "数据库名: ", "ees");
|
|
||||||
PairLine(groupBox, 20, 50, "主机地址: ", "92.168.0.13:17800");
|
const std::string QSS_TABLE = // 表格整体样式
|
||||||
PairLine(groupBox, 20, 80, "用 户 名: ", "root");
|
"QTableWidget {"
|
||||||
|
" background-color: transparent;" // 背景色
|
||||||
|
" gridline-color: #C0C0C0;" // 网格线颜色
|
||||||
|
" border: 1px solid gray;" // 边框
|
||||||
|
" color: white;" // 文字颜色
|
||||||
|
"}"
|
||||||
|
// 表头样式
|
||||||
|
"QHeaderView::section {"
|
||||||
|
" background-color: #404040;" // 表头背景
|
||||||
|
" padding: 4px;" // 内边距
|
||||||
|
" border: 1px solid #505050;" // 边框
|
||||||
|
" min-height: 25px;" // 最小高度
|
||||||
|
"}"
|
||||||
|
// 单元格样式
|
||||||
|
"QTableWidget::item {"
|
||||||
|
" padding-left: 5px;"
|
||||||
|
" border-bottom: 1px solid gray;" // 底部边框
|
||||||
|
"}"
|
||||||
|
// 选中状态
|
||||||
|
"QTableWidget::item:selected {"
|
||||||
|
" background-color: #B8D6FF;" // 选中背景色
|
||||||
|
" color: black;" // 选中文字颜色
|
||||||
|
"}";
|
||||||
|
|
||||||
|
table = std::make_shared<QTableWidget>(this);
|
||||||
|
table->setGeometry(10, y += 130, 1190, 300);
|
||||||
|
table->setStyleSheet(QSS_TABLE.c_str());
|
||||||
|
table->horizontalHeader()->setStretchLastSection(true); // 最后一列占满
|
||||||
|
table->verticalHeader()->setVisible(false); // 不显示垂直表头
|
||||||
|
table->setEditTriggers(QAbstractItemView::NoEditTriggers); // 单元格不可编辑
|
||||||
|
table->setSelectionMode(QAbstractItemView::SingleSelection); // 设置为单选模式
|
||||||
|
table->setSelectionBehavior(QAbstractItemView::SelectRows); // 设置为整行选中
|
||||||
|
|
||||||
|
|
||||||
|
QTableWidgetItem* headerItem;
|
||||||
|
QStringList headerText_Row, headerText_Col;
|
||||||
|
headerText_Row << "站ID" << "站名称" << "站编号" << "站状态" << "MQTT状态" << "召测(秒)" << "说明";
|
||||||
|
|
||||||
|
// 设置为水平表头
|
||||||
|
table->setColumnCount(headerText_Row.size());
|
||||||
|
table->setHorizontalHeaderLabels(headerText_Row);
|
||||||
|
}
|
||||||
|
|
||||||
|
textLog = std::make_shared<QTextEdit>(this);
|
||||||
|
textLog->setGeometry(10, y += 310, 1190, 280);
|
||||||
|
textLog->setStyleSheet("background-color: transparent; border: 1px solid gray; font-weight: 400;");
|
||||||
|
textLog->setReadOnly(true);
|
||||||
|
|
||||||
|
{
|
||||||
|
// 第二个参数是方法函数名称,即调用 QTextEdit的appeng函数;
|
||||||
|
auto qtSink = std::make_shared<spdlog::sinks::qt_sink_mt>(textLog.get(), "append");
|
||||||
|
spdlog::default_logger()->sinks().push_back(qtSink);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
//qtSink = std::make_shared<QtTextSink>(textLog.get());
|
||||||
|
//connect(qtSink.get(), &QtTextSink::appendText, textLog.get(), &QTextEdit::append);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setTableCell(int row, int col, std::string text, std::string style="")
|
||||||
|
{
|
||||||
|
auto item = table->item(row, col);
|
||||||
|
if (!item)
|
||||||
|
{
|
||||||
|
item = new QTableWidgetItem();
|
||||||
|
table->setItem(row, col, item);
|
||||||
|
}
|
||||||
|
item->setText(text.c_str());
|
||||||
|
if (style == "OK") { item->setForeground(QBrush(Qt::green)); }
|
||||||
|
else if (style == "ERR") { item->setForeground(QBrush(Qt::red)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
void onTimer()
|
||||||
|
{
|
||||||
|
auto& appdata = Application().data();
|
||||||
|
int rowNo = 0;
|
||||||
|
int tsNow = Utils::time();
|
||||||
|
for (auto& item : appdata.mapStation)
|
||||||
|
{
|
||||||
|
auto& station = item.second;
|
||||||
|
if (rowNo >= table->rowCount())
|
||||||
|
{
|
||||||
|
table->insertRow(rowNo);
|
||||||
|
}
|
||||||
|
bool isOpen = station->status > 0;
|
||||||
|
bool isConnected = station->mqttCli->isConnected;
|
||||||
|
|
||||||
|
setTableCell(rowNo, 0, std::to_string(station->stationId));
|
||||||
|
setTableCell(rowNo, 0, std::to_string(station->stationId));
|
||||||
|
setTableCell(rowNo, 1, station->name);
|
||||||
|
setTableCell(rowNo, 2, station->code);
|
||||||
|
setTableCell(rowNo, 3, isOpen ? "启用" : "未启用", isOpen ? "OK" : "ERR");
|
||||||
|
setTableCell(rowNo, 4, isConnected ? "连接成功" : "未连接", isConnected ? "OK" : "ERR");
|
||||||
|
|
||||||
|
int tsPolling = station->getPollingTS();
|
||||||
|
setTableCell(rowNo, 5, tsPolling > 0 ? std::to_string(tsNow - tsPolling) + "/" + std::to_string(Config::option.mqtt.interval) : "--");
|
||||||
|
rowNo++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建自定义sink
|
||||||
|
std::shared_ptr<QtTextSink> qtSink;
|
||||||
|
|
||||||
|
std::shared_ptr<QGroupBox> groupSys;
|
||||||
|
std::shared_ptr<QGroupBox> groupHttp;
|
||||||
|
std::shared_ptr<QGroupBox> groupMqtt;
|
||||||
|
std::shared_ptr<QGroupBox> groupDB;
|
||||||
|
std::shared_ptr<QTableWidget> table {};
|
||||||
|
std::shared_ptr<QTextEdit> textLog;
|
||||||
};
|
};
|
||||||
|
|
||||||
void MainApp::setMyLayout()
|
void MainApp::setMyLayout()
|
||||||
@@ -162,9 +284,9 @@ void MainApp::setMyLayout()
|
|||||||
menu->setSizePolicy(sizePolicy);
|
menu->setSizePolicy(sizePolicy);
|
||||||
layout.main->addWidget(menu, 0, 0, 1, 1);
|
layout.main->addWidget(menu, 0, 0, 1, 1);
|
||||||
|
|
||||||
MyWorkspace* workspace = new MyWorkspace(this);
|
ui.workspace = std::make_shared<MyWorkspace>(this);
|
||||||
workspace->setSizePolicy(sizePolicy);
|
ui.workspace->setSizePolicy(sizePolicy);
|
||||||
layout.main->addWidget(workspace, 0, 1, 1, 1);
|
layout.main->addWidget(ui.workspace.get(), 0, 1, 1, 1);
|
||||||
|
|
||||||
// 设置列宽和行高
|
// 设置列宽和行高
|
||||||
layout.main->setColumnMinimumWidth(0, 200); // 设置第0列的最小宽度为100像素
|
layout.main->setColumnMinimumWidth(0, 200); // 设置第0列的最小宽度为100像素
|
||||||
@@ -176,3 +298,10 @@ void MainApp::setMyLayout()
|
|||||||
//gridLayout->setRowStretch(0, 1); // 设置第0行的伸缩因子为1
|
//gridLayout->setRowStretch(0, 1); // 设置第0行的伸缩因子为1
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void MainApp::onTimer()
|
||||||
|
{
|
||||||
|
ui.workspace->onTimer();
|
||||||
|
}
|
||||||
@@ -2,9 +2,54 @@
|
|||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
|
#include <QTableWidget>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QTextEdit>
|
||||||
#include <string>
|
#include <string>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
#include <spdlog/sinks/base_sink.h>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QTextEdit>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
class QtTextSink : public QObject, public spdlog::sinks::base_sink<std::mutex>
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
// 构造函数接收一个 QTextEdit*,但用 QPointer 来包装它
|
||||||
|
explicit QtTextSink(QTextEdit* textEdit, QObject* parent = nullptr)
|
||||||
|
: QObject(parent), textEdit(textEdit) {
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// 定义一个信号,用于在主线程中追加文本
|
||||||
|
void appendText(const QString& message);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// 重写 sink_it_ 方法,处理每条日志
|
||||||
|
void sink_it_(const spdlog::details::log_msg& msg) override {
|
||||||
|
if (textEdit.isNull()) {
|
||||||
|
// 如果 QTextEdit 已经被删除,则忽略这条日志
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
spdlog::memory_buf_t formatted;
|
||||||
|
formatter_->format(msg, formatted);
|
||||||
|
QString logMessage = QString::fromStdString(fmt::to_string(formatted));
|
||||||
|
// 发射信号,通过Qt的事件循环在主线程中安全地更新UI
|
||||||
|
emit appendText(logMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_() override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPointer<QTextEdit> textEdit; // 关键:使用 QPointer
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class LabelPair
|
class LabelPair
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -34,6 +79,9 @@ public:
|
|||||||
QLabel value;
|
QLabel value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class MyWorkspace;
|
||||||
|
|
||||||
class MainApp : public QWidget
|
class MainApp : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -42,12 +90,19 @@ public:
|
|||||||
|
|
||||||
void setMyLayout();
|
void setMyLayout();
|
||||||
|
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onTimer();
|
||||||
|
|
||||||
|
public:
|
||||||
struct {
|
struct {
|
||||||
std::shared_ptr<LabelPair> weburl {};
|
std::shared_ptr<LabelPair> weburl {};
|
||||||
|
std::shared_ptr<MyWorkspace> workspace;
|
||||||
} ui;
|
} ui;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
std::shared_ptr<QGridLayout> main;
|
std::shared_ptr<QGridLayout> main;
|
||||||
} layout;
|
} layout;
|
||||||
|
|
||||||
|
std::shared_ptr<QTimer> timer;
|
||||||
};
|
};
|
||||||
14
src/qt/QUI.cpp
Normal file
14
src/qt/QUI.cpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#include "QUI.h"
|
||||||
|
|
||||||
|
static const std::string QSS_GROUP =
|
||||||
|
"QGroupBox { border: 1px solid gray; margin-top: 8px; border-radius: 5px;}"
|
||||||
|
"QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; left:10px; margin-left: 0px; padding:0 1px; }";
|
||||||
|
|
||||||
|
|
||||||
|
std::shared_ptr<QGroupBox> UI::GroupBox(QWidget* parent, int x, int y, int w, int h, std::string title)
|
||||||
|
{
|
||||||
|
auto groupBox = std::make_shared<QGroupBox>(title.c_str(), parent);
|
||||||
|
groupBox->setGeometry(x, y, w, h);
|
||||||
|
groupBox->setStyleSheet(QSS_GROUP.c_str());
|
||||||
|
return groupBox;
|
||||||
|
}
|
||||||
10
src/qt/QUI.h
Normal file
10
src/qt/QUI.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QGroupBox>
|
||||||
|
|
||||||
|
|
||||||
|
class UI
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<QGroupBox> GroupBox(QWidget* parent, int x, int y, int w, int h, std::string title);
|
||||||
|
};
|
||||||
BIN
src/resource.rc
BIN
src/resource.rc
Binary file not shown.
BIN
src/yhicon.ico
BIN
src/yhicon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 66 KiB |
Reference in New Issue
Block a user