修改设备显示信息的寄存器地址配置文件,修改服务端界面

This commit is contained in:
lixiaoyuan
2025-09-29 18:31:44 +08:00
parent af625fba49
commit 454310262b
23 changed files with 1000 additions and 334 deletions

View File

@@ -32,5 +32,6 @@
"video": {
"1":{"host":"", "port":9000, "user":"", "passwd":""}
},
"statistics": {"enabled": 1, "interval": 60}
"statistics": {"enabled": 1, "interval": 60},
"alert": { "interval":"1800"}
}

View File

@@ -2,6 +2,15 @@
"EMS":{
"deviceType":101,
"addrYC":[
["BCU总通信状态", "0x2018", "--", ""],
["PCU总通信状态", "0x2019", "--", ""],
["电表总通信状态", "0x201A", "--", ""],
["消防总通信状态", "0x201B", "--", ""],
["UPS总通信状态", "0x201C", "--", ""],
["温湿度总通信状态", "0x201D", "--", ""],
["空调总通信状态", "0x201E", "--", ""],
["EMU通信状态", "0x201F", "--", ""],
["冷机总通信状态", "0x2020", "--", ""],
["A相电压", "0x107E", "0.0", " V", "1"],
["A相电流", "0x1084", "0.0", " A"],
["B相电压", "0x1080", "0.0", " V", "1"],
@@ -11,7 +20,18 @@
["总有功功率", "0x1096", "0.0", " kW"]
],
"addrCurve": ["0x107E", "0x1084", "0x1096"]
"addrCurve": ["0x107E", "0x1084", "0x1096"],
"valstr": {
"0x2018": {"0":"正常", "1":"告警", "2":"故障"},
"0x2019": {"0":"正常", "1":"告警", "2":"故障"},
"0x201A": {"0":"正常", "1":"告警", "2":"故障"},
"0x201B": {"0":"正常", "1":"告警", "2":"故障"},
"0x201C": {"0":"正常", "1":"告警", "2":"故障"},
"0x201D": {"0":"正常", "1":"告警", "2":"故障"},
"0x201E": {"0":"正常", "1":"告警", "2":"故障"},
"0x201F": {"0":"正常", "1":"告警", "2":"故障"},
"0x2020": {"0":"正常", "1":"告警", "2":"故障"}
}
},
"PCS":{
"deviceType":102,
@@ -26,7 +46,11 @@
["C相电流", "0x001B", "0.0", " A"],
["总有功功率", "0x0025", "0.0", " kW"]
],
"addrCurve": ["0x0010", "0x0019", "0x0025"]
"addrCurve": ["0x0010", "0x0019", "0x0025"],
"valstr": {
"0x1009": {"0":"待机","1":"充电","2":"放电","3":"搁置"},
"0x100A": {"1":"离网","0":"并网"}
}
},
"PCU":{
"deviceType":103,
@@ -43,7 +67,11 @@
["C相电流", "0x001E", "0.0", " A"],
["总有功功率", "0x0028", "0.0", " kW"]
],
"addrCurve": ["0x0013", "0x001C", "0x0028"]
"addrCurve": ["0x0013", "0x001C", "0x0028"],
"valstr": {
"0x1007": {"1":"离网","0":"并网"},
"0x1008": {"1":"开机","0":"关机"}
}
},
"BMS":{
"deviceType":104,
@@ -59,7 +87,10 @@
["单体最大温度", "0x0029", "0.0", " ℃", "0.1"],
["单体最小温度", "0x002C", "0.0", " ℃", "0.1"]
],
"addrCurve": ["0x0003", "0x0005", ""]
"addrCurve": ["0x0003", "0x0005", ""],
"valstr": {
"0x004A": {"0":"待机","1":"充电","2":"放电"}
}
},
"BCU":{
"deviceType":105,
@@ -73,7 +104,11 @@
["簇电压", "0x0003", "0.0", " V", "0.1"],
["簇电流", "0x0005", "0", " A", "0.1"]
],
"addrCurve": ["0x0003", "0x0005", ""]
"addrCurve": ["0x0003", "0x0005", ""],
"valstr": {
"0xA003": {"17":"开路","34":"待机","51":"充电","68":"放电"},
"0xA004": {"17":"跳机","34":"待机","51":"放空","68":"充满","85":"预警","102":"正常"}
}
},
"MEM":{
"deviceType":3,

View File

@@ -0,0 +1,47 @@
[
{
"name": "EMS",
"devicetype": 101,
"valstr": [
{"addr": "0x2018", "val": {"0":"正常", "1""告警", "2""故障"}},
{"addr": "0x2019", "val": {"0":"正常", "1""告警", "2""故障"}},
{"addr": "0x201A", "val": {"0":"正常", "1""告警", "2""故障"}},
{"addr": "0x201A", "val": {"0":"正常", "1""告警", "2""故障"}},
]
},
{
"name": "PCS",
"devicetype": 102,
"valstr": [
{""}
]
},
{
"name": "PCU",
"devicetype": 103,
"valstr": [
{}
]
},
{
"name": "BMS",
"devicetype": 104,
"valstr": [
{}
]
},
{
"name": "BCU",
"devicetype": 105,
"valstr": [
{}
]
},
{
"name": "CHARGER",
"devicetype": 106,
"valstr": [
{}
]
},
]

View File

@@ -149,10 +149,7 @@ bool AppData::initFromDB()
for (auto& fields: result)
{
auto policy = std::make_shared<SysPolicy>();
policy->policyId = fields.get<int>(DMPolicy::POLICY_ID);
policy->type = fields.get<int>(DMPolicy::TYPE);
policy->name = fields.value(DMPolicy::NAME);
policy->value = fields.value(DMPolicy::VALUE);
policy->setFields(fields);
this->mapPolicy[policy->policyId] = policy;
}
}
@@ -452,13 +449,23 @@ std::vector<std::string> AppData::getPolicyNames()
return vec;
}
int AppData::getPolicyTypeId(std::string name)
std::shared_ptr<SysPolicy> AppData::getPolicyByType(int typeId)
{
for (auto iter = mapPolicyType.begin(); iter != mapPolicyType.end(); ++iter)
for (auto iter = mapPolicy.begin(); iter != mapPolicy.end(); ++iter)
{
if (iter->second == name) { return iter->first; }
if (iter->second->type == typeId) { return iter->second; }
}
return 0;
return nullptr;
}
std::shared_ptr<SysPolicy> AppData::getPolicyById(int policyId)
{
auto iter = mapPolicy.find(policyId);
if (iter != mapPolicy.end())
{
return iter->second;
}
return nullptr;
}

View File

@@ -95,9 +95,11 @@ public:
std::vector<std::string> getPolicyTypeNames();
// 获取策略名称
std::vector<std::string> getPolicyNames();
// 根据策略类型ID获取策略类型名称
// 根据策略类型名称获取策略类型ID
int getPolicyTypeId(std::string name);
// 根据策略类型ID获取策略信息
std::shared_ptr<SysPolicy> getPolicyByType(int typeId);
std::shared_ptr<SysPolicy> getPolicyById(int policyId);
//std::vector<std::string> getElectPreiodVals(int month);
//std::string getElectPreiodVal(int month, int hour);
@@ -158,8 +160,6 @@ public:
// 策略信息
std::unordered_map<int, std::shared_ptr<SysPolicy>> mapPolicy;
std::map<int64_t, double> mapDataDay;
};

View File

@@ -25,7 +25,7 @@ void Application::init()
// MQTT 数据结构
REGAddr::load("assets/config/regaddrs.json");
// 设备读取寄存器的地址定义
Device::loadParamAddr("assets/config/regaddrsShow.json");
REGAddr::loadParamAddr("assets/config/regaddrsShow.json");
// 设置数据库配置
DaoEntity::setOption(Config::option.database.host,

View File

@@ -2,8 +2,33 @@
#include "common/JsonN.h"
#include "common/Utils.h"
static map<string, int> g_mapTopicDeviceType =
{
{"EMS_YT", 101}, {"EMS_YX", 101}, {"EMS_YC", 101},
{"PCU_YC", 103}, {"PCU_YX", 103},
{"PCS_YC", 102}, {"PCS_YX", 102},
{"BMS_YC", 104},
{"BCU_YC", 105}, {"BCU_YX", 105},
{"MEM_YC", 3},
{"TH_YC", 10},
{"Fire40_YX", 7},
{"Cooling_YC", 14},
{"Cooling_YX", 14},
{"Gateway_YC", 15},
{"Gateway_YX", 15},
{"Charger_YC", 106}
};
// key: tpoic, 子key: addr
std::map<std::string, std::map<std::string, RegAddrUnit>> REGAddr::s_mapReg;
std::map<int, std::vector<DeviceParamAddr>> REGAddr::s_mapDeviceAddrParam;
std::map<int, std::vector<std::string>> REGAddr::s_mapDeviceAddrCurve;
std::map<int, std::map<std::string, std::map<std::string, std::string>>> REGAddr::g_mapRegAddrValStr;
// key: 设备类型, 子key addr
std::map<int, std::map<std::string, RegAddrUnit>> REGAddr::g_mapRegDeviceType;
void REGAddr::load(std::string filename)
{
njson json;
@@ -12,7 +37,9 @@ void REGAddr::load(std::string filename)
// 遍历 JSON 对象
for (auto& jsonitem : json.items())
{
std::string name = jsonitem.key();
std::string name = jsonitem.key(); // topic名称
int deviceType = g_mapTopicDeviceType[name];
auto& jsonnodeItem = jsonitem.value();
//int count = jsonnodeItem["count"];
auto jsonaddrs = jsonnodeItem["addr"];
@@ -28,12 +55,68 @@ void REGAddr::load(std::string filename)
std::string remark = JSON::read<std::string>(item, "remark");
std::string name = JSON::read<std::string>(item, "name");
int alert = JSON::read<int>(item, "alert");
mapItem[addr] = RegAddrUnit(addr, datatype, alert, name, remark);
g_mapRegDeviceType[deviceType][addr] = mapItem[addr] = RegAddrUnit(addr, datatype, alert, name, remark);
}
}
}
}
void REGAddr::loadParamAddr(std::string filename)
{
try
{
njson json;
if (!JSON::load(filename, json))
{
spdlog::error("[device] json load param addr error, filename={}", filename);
}
for (auto& jsonitem : json.items())
{
std::string key = jsonitem.key();
auto& jsonnodeItem = jsonitem.value();
int deviceType = jsonnodeItem["deviceType"];
auto& vec = s_mapDeviceAddrParam[deviceType];
for (auto& v : jsonnodeItem["addrYC"])
{
std::string name = JSON::get<std::string>(v[0]);
std::string addr = JSON::get<std::string>(v[1]);
std::string defaultVal = JSON::get<std::string>(v[2]);
std::string unit = JSON::get<std::string>(v[3]);
float ratio = Utils::toFloat(JSON::get<string>(v[4]));
vec.push_back(DeviceParamAddr(name, addr, defaultVal, unit, ratio));
}
if (jsonnodeItem.contains("addrCurve"))
{
auto& vec = s_mapDeviceAddrCurve[deviceType];
for (auto& v : jsonnodeItem["addrCurve"])
{
vec.push_back(v.get<std::string>());
}
}
if (jsonnodeItem.contains("valstr"))
{
auto& mapDT = g_mapRegAddrValStr[deviceType];
for (auto& itemaddr : jsonnodeItem["valstr"].items())
{
string addr = itemaddr.key();
auto& mapAddr = mapDT[addr];
for (auto& itemval: itemaddr.value().items())
{
string key = itemval.key();
mapAddr[key] = itemval.value().get<std::string>();
}
}
}
}
}
catch (nlohmann::json::parse_error& e)
{
spdlog::error("[device] parse [{}] error: ", filename, e.what());
}
}
std::map<std::string, RegAddrUnit>* REGAddr::getRegMap(std::string name)
{
auto iter = s_mapReg.find(name);
@@ -43,3 +126,24 @@ std::map<std::string, RegAddrUnit>* REGAddr::getRegMap(std::string name)
}
return nullptr;
}
std::map<std::string, RegAddrUnit>* REGAddr::getRegMapByDeviceType(int deviceType)
{
auto iter = g_mapRegDeviceType.find(deviceType);
if (iter != g_mapRegDeviceType.end())
{
return &(iter->second);
}
return nullptr;
}
std::vector<DeviceParamAddr>& REGAddr::GetDeviceParamAddrs(int deviceType)
{
static std::vector<DeviceParamAddr> vecAddrs = {};
auto iter = s_mapDeviceAddrParam.find(deviceType);
if (iter != s_mapDeviceAddrParam.end())
{
return iter->second;
}
return vecAddrs;
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include <map>
#include <string>
#include <vector>
enum class EAlertType
{
@@ -56,12 +57,43 @@ struct RegAddrUnit
}
};
// 需要在前端展示的设备参数
struct DeviceParamAddr
{
std::string name;
std::string addr;
std::string defaultVal;
std::string unit;
float ratio {1.0};
DeviceParamAddr() {};
DeviceParamAddr(std::string name, std::string addr, std::string defaultVal, std::string unit, float ratio = 1.0f)
: name(name), addr(addr), defaultVal(defaultVal), unit(unit), ratio(ratio)
{
if (this->ratio == 0.0)
{
this->ratio = 1.0f;
}
};
};
class REGAddr
{
public:
// key: 寄存器地址
static std::map<std::string, std::map<std::string, RegAddrUnit>> s_mapReg;
static std::map<int, std::vector<DeviceParamAddr>> s_mapDeviceAddrParam;
static std::map<int, std::vector<std::string>> s_mapDeviceAddrCurve;
static std::map<int, std::map<std::string, std::map<std::string, std::string>>> g_mapRegAddrValStr;
static std::map<int, std::map<std::string, RegAddrUnit>> g_mapRegDeviceType;
static void load(std::string filename);
static void loadParamAddr(std::string filename);
static std::map<std::string, RegAddrUnit>* getRegMap(std::string name);
static std::map<std::string, RegAddrUnit>* getRegMapByDeviceType(int deviceType);
static std::vector<DeviceParamAddr>& GetDeviceParamAddrs(int deviceType);
};

View File

@@ -7,19 +7,8 @@
#include "app/DataStruct.h"
#include <unordered_set>
std::map<int, std::vector<DeviceParamAddr>> Device::s_mapDeviceAddrParam;
std::map<int, std::vector<std::string>> Device::s_mapDeviceAddrCurve;
static std::vector<DeviceParamAddr>& GetDeviceParamAddrs(int deviceType)
{
static std::vector<DeviceParamAddr> vecAddrs = {};
auto iter = Device::s_mapDeviceAddrParam.find(deviceType);
if (iter != Device::s_mapDeviceAddrParam.end())
{
return iter->second;
}
return vecAddrs;
}
static std::unordered_set<int> g_setCacheDeviceType = {3, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110};
static bool CheckCacheType(int type)
@@ -35,46 +24,6 @@ std::shared_ptr<Device> Device::create(Fields& fields)
}
void Device::loadParamAddr(std::string filename)
{
try
{
njson json;
if (!JSON::load(filename, json))
{
spdlog::error("[device] json load param addr error, filename={}", filename);
}
for (auto& jsonitem : json.items())
{
std::string key = jsonitem.key();
auto& jsonnodeItem = jsonitem.value();
int type = jsonnodeItem["deviceType"];
auto& vec = s_mapDeviceAddrParam[type];
for (auto& v : jsonnodeItem["addrYC"])
{
std::string name = JSON::get<std::string>(v[0]);
std::string addr = JSON::get<std::string>(v[1]);
std::string defaultVal = JSON::get<std::string>(v[2]);
std::string unit = JSON::get<std::string>(v[3]);
float ratio = Utils::toFloat(JSON::get<string>(v[4]));
vec.push_back(DeviceParamAddr(name, addr, defaultVal, unit, ratio));
}
if (jsonnodeItem.contains("addrCurve"))
{
auto& vec = s_mapDeviceAddrCurve[type];
for (auto& v : jsonnodeItem["addrCurve"])
{
vec.push_back(v.get<std::string>());
}
}
}
}
catch (nlohmann::json::parse_error& e)
{
spdlog::error("[device] parse [{}] error: ", filename, e.what());
}
}
static const int BCU_UNIT_SIZE = 256;
@@ -119,7 +68,7 @@ void Device::setFields(Fields& fields)
}
}
auto& vecAddrs = GetDeviceParamAddrs(this->type);
auto& vecAddrs = REGAddr::GetDeviceParamAddrs(this->type);
for (auto& item: vecAddrs)
{
this->mapMyParams[item.addr] = &item;
@@ -224,8 +173,8 @@ bool Device::cache(int npos)
std::string addrV;
std::string addrI;
std::string addrP;
auto iter = s_mapDeviceAddrCurve.find(this->type);
if (iter != s_mapDeviceAddrCurve.end())
auto iter = REGAddr::s_mapDeviceAddrCurve.find(this->type);
if (iter != REGAddr::s_mapDeviceAddrCurve.end())
{
auto& vecAddr = iter->second;
auto size = vecAddr.size();
@@ -340,79 +289,23 @@ std::string Device::getParam(std::string k, std::string defaultVal)
return defaultVal;
}
static map<int, map<string, string>> g_mapAddrValStr =
{
};
void Device::getRuntimeParams(std::vector<std::pair<std::string, std::string>>& params)
{
// 3 电表
// 101 EMS
// 102 PCS
// 103 PCU
// 104 BMS
// 105 BCU
// 106 充电桩
// 109 光伏板
auto& vecAddr = s_mapDeviceAddrParam[this->type];
auto& vecAddr = REGAddr::s_mapDeviceAddrParam[this->type];
for (auto& item: vecAddr)
{
std::string v = getParam(item.addr, item.defaultVal);
if (type == int(EDeviceType::BCU) )
auto& mapValStr = REGAddr::g_mapRegAddrValStr[type][item.addr];
auto iter = mapValStr.find(v);
if (iter != mapValStr.end())
{
if (item.addr == "0xA003") //"0x11开路0x22待机0x33充电0x44放电"
{
if (v == "17") v = "开路";
else if (v == "34") v = "待机";
else if (v == "51") v = "充电";
else if (v == "68") v = "放电";
}
else if (item.addr == "0xA004") //"0x11跳机 0x22待机0x33放空0x44充满0x55预警0x66正常"
{
if (v == "17") v = "跳机";
else if (v == "34") v = "待机";
else if (v == "51") v = "放空";
else if (v == "68") v = "充满";
else if (v == "85") v = "预警";
else if (v == "102") v = "正常";
}
v = iter->second;
}
else if (type == int(EDeviceType::BMS))
{
if (item.addr == "0x004A") // 0-待机 1-充电 2-放电
{
if (v == "0") v = "待机";
else if (v == "1") v = "充电";
else if (v == "2") v = "放电";
}
}
else if (type == int(EDeviceType::PCU))
{
if (item.addr == "0x1007") // 电网状态 R uint16 1离网0并网 0x1007
{
if (v == "0") v = "并网";
else if (v == "1") v = "离网";
}
if (item.addr == "0x1006") // 启停状态 R uint16 1开机0关机 0x1008
{
if (v == "1") v = "开机";
else if (v == "0") v = "关机";
}
}
else if (type == int(EDeviceType::PCS))
{
if (item.addr == "0x1009") //充放状态 R uint16 0待机, 1充电, 2放电, 3搁置 0x1009
{
if (v == "0") v = "待机";
else if (v == "1") v = "充电";
else if (v == "2") v = "放电";
else if (v == "3") v = "搁置";
}
else if (item.addr == "0x100A") //电网状态 R uint16 1离网0并网 0x100A
{
if (v == "0") v = "并网";
else if (v == "1") v = "离网";
}
}
//if (this->online) { }
//else { v = "--"; }
params.push_back({item.name, v + item.unit});
}
}

View File

@@ -8,33 +8,14 @@
#include <unordered_map>
#include "common/Fields.h"
#include "app/DataStruct.h"
class CommEntity;
// 需要在前端展示的设备参数
struct DeviceParamAddr
{
std::string name;
std::string addr;
std::string defaultVal;
std::string unit;
float ratio {1.0};
DeviceParamAddr() {};
DeviceParamAddr(std::string name, std::string addr, std::string defaultVal, std::string unit, float ratio=1.0f)
: name(name), addr(addr), defaultVal(defaultVal), unit(unit), ratio(ratio)
{
if (this->ratio == 0.0)
{
this->ratio = 1.0f;
}
};
};
class Device
{
public:
static std::shared_ptr<Device> create(Fields& fields);
static void loadParamAddr(std::string filename);
Device();
@@ -61,8 +42,7 @@ public:
void setBCUUnit(std::string k, int pos, int v, int count);
public:
static std::map<int, std::vector<DeviceParamAddr>> s_mapDeviceAddrParam;
static std::map<int, std::vector<std::string>> s_mapDeviceAddrCurve;
int stationId = {0};
int deviceId = {0};
@@ -90,8 +70,11 @@ public:
std::map<int, float> mapCacheVoltage;
std::map<int, float> mapCacheCurrent;
std::map<int, float> mapCachePower;
// 参数值(保存从设备读取的参数值)
std::map<std::string, std::string> mapParams;
// 参数定义(定义参数的基础信息,包括寄存器地址,名称,单位,倍率)
std::map<std::string, DeviceParamAddr*> mapMyParams;
std::vector<std::vector<float>> vecBCUUnit;

View File

@@ -5,8 +5,8 @@
void SysPolicy::setFields(Fields& fields)
{
this->policyId = fields.get<int>("policy_id");
this->type = fields.get<int>("policy_type");
this->name = fields.value("policy_name");
this->type = fields.get<int>("type"); // policy_type
this->name = fields.value("name"); // policy_name
this->value = fields.value("value");
this->parseValue(value);
@@ -146,6 +146,7 @@ void SysPolicy::getGatewayJsonPeriods(njson& json)
{
if (vecPeriods1.size()>0)
{
njson jsonArray = njson::array();
for (auto& item: vecPeriods1[0])
{
int h = 0; int m = 0;
@@ -156,8 +157,9 @@ void SysPolicy::getGatewayJsonPeriods(njson& json)
else if (item.second == "") p = 3;
else if (item.second == "") p = 4;
else p = 0;
json.push_back({h, m, p});
jsonArray.push_back({h, m, p});
}
json.push_back(jsonArray);
}
}
}

View File

@@ -10,6 +10,7 @@
#include "app/Config.h"
#include "common/Snowflake.h"
#include "app/DataStruct.h"
#include "app/Application.h"
Station::Station() : stationId(0)
{
@@ -27,7 +28,7 @@ void Station::setFields(Fields& fields)
this->operationDate = fields.value(DMStation::OPERATION_DATE);
this->launchDate = fields.value("operation_date");
this->policy.setFields(fields);
//this->policy.setFields(fields);
}
void Station::addDevice(int deviceId, std::shared_ptr<Device> device)
@@ -133,17 +134,7 @@ void Station::setWorkMode(int modeId)
}
}
void Station::setPolicy(int policyId)
{
std::string sql = SQL(SQL::TYPE::update).table(DMStation::TABLENAME)
.update(DMStation::POLICY_ID, std::to_string(policyId))
.where(DMStation::STATION_ID + "=" + std::to_string(stationId)).str();
Errcode err = DAO::exec(NULL, sql);
if (err != Errcode::OK)
{
spdlog::error("set station policy failed.");
}
}
void Station::initMqtt()
{
@@ -181,23 +172,30 @@ void Station::setGarewayWorkMode()
{
return;
}
auto policy = Application::data().getPolicyByType(this->workMode);
njson json;
json["ts"] = Utils::time();
json["no"] = 1; // 设备编号
json["40001"] = this->workMode;
if (policy.type == 1)
if (policy)
{
json["40002"] = njson::array(); // 峰谷套利
policy.getGatewayJsonPeriods(json["40002"]);
}
else if (policy.type == 5)
{
json["40021"] = njson::array(); // 自定时段
policy.getGatewayJsonPeriods(json["40021"]);
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"]);
}
}
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);
mqttCli->publish("Gateway_YT", text);
@@ -229,7 +227,6 @@ string Station::getGatewayParam()
{
stringstream ss;
std::string str1 = "峰谷套利时段:<br>";
std::string str2 = "自定时段:<br>";
{
njson json;
if (JSON::parse(gatewayParam.param1, json))
@@ -249,6 +246,7 @@ string Station::getGatewayParam()
}
}
}
std::string str2 = "自定时段:<br>";
{
njson json;
if (JSON::parse(gatewayParam.param2, json))
@@ -267,7 +265,35 @@ string Station::getGatewayParam()
}
}
}
return str1 + "<br/>" + str2;
std::string str3 = "其它参数:<br>";
{
njson json;
if (JSON::parse(gatewayParam.param3, json))
{
for (int i = 0; i<json.size(); ++i)
{
int val = json[i].get<int>();
if (i==0) { str3 += "&nbsp&nbsp&nbsp&nbsp储能放电下限值SOC: " + std::to_string(val) + "<br>"; } //储能放电下限值 SOC : 40038 (%:0-99 且小于充电上限值)
else if (i==1) { str3 += "&nbsp&nbsp&nbsp&nbsp储能充电上限值SOC: " + std::to_string(val) + "<br>"; }//储能充电上限值 SOC : 40039 (%:1-100 且大于放电下限值)
else if (i==2) { str3 += "&nbsp&nbsp&nbsp&nbsp台区变压器容量: " + std::to_string(val) + "<br>"; }//台区变压器容量 : 40040 (KVVA 160-1600)
else if (i==3) { str3 += "&nbsp&nbsp&nbsp&nbsp安全输入功率: " + std::to_string(val) + "<br>"; }//安全输入功率 : 40041 (KW 0-400)
else if (i==4) { str3 += "&nbsp&nbsp&nbsp&nbsp储能最大放电功率: " + std::to_string(val) + "<br>"; }//储能最大放电功率 : 40042 (1KW0-150)
else if (i==5) { str3 += "&nbsp&nbsp&nbsp&nbsp储能最大充电功率: " + std::to_string(val) + "<br>"; }//储能最大充电功率::40043 (1KW0-150)
else if (i==6) { str3 += "&nbsp&nbsp&nbsp&nbsp运行状态: " + std::to_string(val) + "<br>"; }//运行状态 : 40044 (只读不写0:无 1:高峰放电 2:低谷充电)
else if (i==7) { str3 += "&nbsp&nbsp&nbsp&nbsp台区电表变比: " + std::to_string(val) + "<br>"; }//台区电表变比:40045
//else if (i==8) { str3 += "&nbsp&nbsp&nbsp&nbsp对时(年): " + std::to_string(val) + "<br>"; }//对时(年) : 40051
//else if (i==9) { str3 += "&nbsp&nbsp&nbsp&nbsp对时(月): " + std::to_string(val) + "<br>"; }//对时(月) : 40052
//else if (i==10) { str3 += "&nbsp&nbsp&nbsp&nbsp对时(日): " + std::to_string(val) + "<br>"; }//对时(日) : 40053
//else if (i==11) { str3 += "&nbsp&nbsp&nbsp&nbsp对时(时): " + std::to_string(val) + "<br>"; }//对时(时) : 40054
//else if (i==12) { str3 += "&nbsp&nbsp&nbsp&nbsp对时(分): " + std::to_string(val) + "<br>"; }//对时(分) : 40055
//else if (i==13) { str3 += "&nbsp&nbsp&nbsp&nbsp对时(秒): " + std::to_string(val) + "<br>"; }//对时(秒) : 40056
//else if (i==19) { str3 += "&nbsp&nbsp&nbsp&nbsp时间段月份: " + std::to_string(val) + "<br>"; }//时间段月份 : 40057(1-12 对应1月-12月)
else if (i==20) { str3 += "&nbsp&nbsp&nbsp&nbsp防逆流回差: " + std::to_string(val) + "<br>"; }//防逆流回差 : 40058(1KW 10-300)
else if (i==21) { str3 += "&nbsp&nbsp&nbsp&nbsp防过载回差: " + std::to_string(val) + "<br>"; }//防过载回差 : 40059(1KW 10-300)
}
}
}
return str1 + "<br/>" + str2 + "<br/>" + str3;
}
void Station::checkDevice()
@@ -290,7 +316,7 @@ void Station::readAlert(std::shared_ptr<Device> device, std::string addr, int v,
int64_t ts = Utils::time();
std::string alertId = std::to_string(device->deviceId) + "_" + addr;
int tsCache = mapAlertCache[alertId];
if (ts - tsCache > 60*5)
if (ts - tsCache > 60*30)
{
Fields fields;
fields.set("log_id", Snowflake::instance().getIdStr());
@@ -419,6 +445,14 @@ void Station::readCoolingData(int deviceNo, string addr, int val)
else if (addr == "0x100E") { ; }// 出水压力传感器 R uint16 0正常1告警 0x100E
}
static void JSONReadArrayItem(njson& json, int i, int& v)
{
if (json.is_array() && i < json.size())
{
v = json[i].get<int>();
}
}
void Station::readGatewayMode(int mode, string p1, string p2, string p3)
{
this->gatewayParam.mode = mode;
@@ -429,6 +463,22 @@ void Station::readGatewayMode(int mode, string p1, string p2, string p3)
{
//this->setGarewayWorkMode();
}
njson json;
if (JSON::parse(gatewayParam.param3, json))
{
JSONReadArrayItem(json, 0, gatewayParam.socMin); //储能放电下限值 SOC : 40038 (%:0-99 且小于充电上限值)
JSONReadArrayItem(json, 1, gatewayParam.socMax); //储能充电上限值 SOC : 40039 (%:1-100 且大于放电下限值)
JSONReadArrayItem(json, 2, gatewayParam.capacity); //台区变压器容量 : 40040 (KVVA 160-1600)
JSONReadArrayItem(json, 3, gatewayParam.powerSafe); //安全输入功率 : 40041 (KW 0-400)
JSONReadArrayItem(json, 4, gatewayParam.powerDischarge); //储能最大放电功率 : 40042 (1KW0-150)
JSONReadArrayItem(json, 5, gatewayParam.powerCharge); //储能最大充电功率::40043 (1KW0-150)
JSONReadArrayItem(json, 6, gatewayParam.status); //运行状态 : 40044 (只读不写0:无 1:高峰放电 2:低谷充电)
JSONReadArrayItem(json, 7, gatewayParam.vtRatio); //台区电表变比:40045
JSONReadArrayItem(json, 20, gatewayParam.backflow); //防逆流回差 : 40058(1KW 10-300)
JSONReadArrayItem(json, 21, gatewayParam.overload); //防过载回差 : 40059(1KW 10-300)
}
}
void Station::readGatewayStatus(int cdzStatus, int emuStatus)

View File

@@ -107,7 +107,6 @@ public:
void getDeviceByCategory(int category, std::vector<std::shared_ptr<Device>>& res);
void setWorkMode(int modeId);
void setPolicy(int policyId);
void initMqtt();
void polling();
@@ -139,7 +138,7 @@ public:
std::string code;
int status {0};
std::string operationDate;
SysPolicy policy;
//SysPolicy policy;
std::string launchDate {};
bool isConnected {false};
@@ -153,6 +152,17 @@ public:
std::string param1;
std::string param2;
std::string param3;
int socMin {}; // 储能放电下限值 SOC 40038 (%, 0-99)
int socMax {}; // 储能充电上限值 SOC 40039 (%:1-100)
int capacity {}; // 台区变压器容量 40040 (KVA 160-1600)
int powerSafe {}; // 安全输入功率 40041 (KW 0-400)
int powerDischarge {}; // 储能最大放电功率 40042 (1KW 0-150)
int powerCharge {}; // 储能最大充电功率 40043 (1KW 0-150)
int status {}; // 运行状态:40044 (只读不写0:无 1:高峰放电 2:低谷充电)
int vtRatio ;// 台区电表变比 40045
int backflow {}; // 防逆流回差 40058(1KW 10-300)
int overload {}; // 防过载回差 40059(1KW 10-300)
} gatewayParam;
///////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -218,6 +218,8 @@ static std::map<std::string, HandlerOptions> g_mapHttpHandlerPost
{"/insertServiceApi", HandlerOptions(&HttpEntity::insertServiceApi, {})},
{"/updateServiceApi", HandlerOptions(&HttpEntity::updateServiceApi, {"api_id"})},
{"/updateGatewayParams", HandlerOptions(&HttpEntity::updateGatewayParams, {DMStation::STATION_ID})},
};
bool CheckHttpToken(const httplib::Request& req)
@@ -935,6 +937,14 @@ Errcode HttpEntity::queryDevicByCategory(const httplib::Request& req, njson& jso
jsongateway["workmode"] = station->getGatewayMode();
jsongateway["emu"] = station->emuStatus == 1 ? "在线" : (station->emuStatus == 0 ? "离线" : "--");
jsongateway["cdz"] = station->cdzStatus == 1 ? "在线" : (station->cdzStatus == 0 ? "离线" : "--");
jsongateway["soc_min"] = station->gatewayParam.socMin;
jsongateway["soc_max"] = station->gatewayParam.socMax;
jsongateway["capacity"] = station->gatewayParam.capacity;
jsongateway["power_safe"] = station->gatewayParam.powerSafe;
jsongateway["power_discharge"] = station->gatewayParam.powerDischarge;
jsongateway["power_charge"] = station->gatewayParam.powerCharge;
jsongateway["backflow"] = station->gatewayParam.backflow;
jsongateway["overload"] = station->gatewayParam.overload;
}
json["gateway"] = jsongateway;
return Errcode::OK;
@@ -981,7 +991,7 @@ Errcode HttpEntity::queryDeviceBCUDetail(const httplib::Request& req, njson& jso
auto& row = device->vecBCUUnit[i];
std::string soc = Utils::toStr(row[0], 1);
std::string soh = Utils::toStr(row[1], 1);
std::string u = Utils::toStr(row[2], 0);
std::string u = Utils::toStr(row[2], 3);
std::string t = Utils::toStr(row[3], 2);
std::string r_i = Utils::toStr(row[4], 0);
jsondata.push_back({{"SOC", soc}, {"SOH", soh}, {"V", u}, {"T", t}, {"R_i", r_i}});
@@ -1011,8 +1021,18 @@ Errcode HttpEntity::insertPolicy(const httplib::Request& req, njson& json, std::
Errcode HttpEntity::updatePolicy(const httplib::Request& req, njson& json, std::string& errmsg)
{
Fields params;
GetRequestParams(req, {"policy_id", "type", "describe", "value", "is_open"}, params);
return DAO::updatePolicyById(params);
GetRequestParams(req, {"policy_id", "type", "name", "describe", "value", "is_open"}, params);
auto err = DAO::updatePolicyById(params);
if (err == Errcode::OK)
{
int policyId = params.get<int>("policy_id");
auto policy = Application::data().getPolicyById(policyId);
if (policy)
{
policy->setFields(params);
}
}
return err;
};
Errcode HttpEntity::deletePolicy(const httplib::Request& req, njson& json, std::string& errmsg)
{
@@ -1626,3 +1646,39 @@ Errcode HttpEntity::deleteServiceApi(const httplib::Request& req, njson& json, s
GetRequestParams(req, {"api_id"}, params);
return DAO::remove(NULL, "serviceapi", "api_id", params.value("api_id"));
}
Errcode HttpEntity::updateGatewayParams(const httplib::Request& req, njson& json, std::string& errmsg)
{
//上下限SOC安全输入功率储能最大充放电功率防逆流防过载回差。
Fields params;
GetRequestParams(req, {"station_id", "work_mode", "soc_min", "soc_max", "capacity", "power_safe", "power_discharge", "power_charge", "backflow", "overload"}, params);
int stationId = params.get<int>("station_id");
auto station = Application::data().getStation(stationId);
if (params.contains("work_mode"))
{
int workMode = params.get<int>("work_mode");
Fields fields;
fields.set("station_id", params.value("station_id"));
fields.set("work_mode", workMode);
Errcode err = DAO::updateStationById(params);
}
if (station)
{
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)
params.get("power_safe", station->gatewayParam.powerSafe); // 安全输入功率 40041 (KW 0-400)
params.get("power_discharge", station->gatewayParam.powerDischarge); // 储能最大放电功率 40042 (1KW 0-150)
params.get("power_charge", station->gatewayParam.powerCharge); // 储能最大充电功率 40043 (1KW 0-150)
params.get("backflow", station->gatewayParam.backflow); // 防逆流回差 40058(1KW 10-300)
params.get("overload", station->gatewayParam.overload); // 防过载回差 40059(1KW 10-300)
station->setGarewayWorkMode();
return Errcode::OK;
}
return Errcode::ERR_PARAM;
}

View File

@@ -99,7 +99,7 @@ public:
// 场站按类某一天的历史曲线数据
Errcode queryStatCharts(const httplib::Request& req, njson& json, std::string& errmsg);
Errcode exportStatReport(const httplib::Request& req, njson& json, std::string& errmsg);
Errcode exportStatReport(const httplib::Request& req, njson& json, std::string& errmsg);
Errcode queryEnvironment(const httplib::Request& req, njson& json, std::string& errmsg);
@@ -107,4 +107,6 @@ public:
Errcode insertServiceApi(const httplib::Request& req, njson& json, std::string& errmsg);
Errcode updateServiceApi(const httplib::Request& req, njson& json, std::string& errmsg);
Errcode deleteServiceApi(const httplib::Request& req, njson& json, std::string& errmsg);
Errcode updateGatewayParams(const httplib::Request& req, njson& json, std::string& errmsg);
};

View File

@@ -11,7 +11,6 @@
#include "protocol/MqttEntity.h"
MainApp::MainApp()
{
QPalette palette(this->palette());
@@ -32,58 +31,75 @@ MainApp::MainApp()
//ui.weburl->setTitle("页面地址:");
//ui.weburl->setValue("http://www.baidu.com");
this->initMenu();
this->setMyLayout();
this->onActiveMenu("系统总览");
timer = std::make_shared<QTimer>(this);
connect(timer.get(), &QTimer::timeout, this, &MainApp::onTimer);
timer->start(1000); // 每隔1000毫秒更新一次
}
class MyMenu : public QWidget
void MainApp::initMenu()
{
public:
MyMenu(QWidget* parent) : QWidget(parent), layout(this)
ui.widgetMenu = std::make_shared<QWidget>(this);
ui.widgetMenu->setObjectName("menu");
ui.widgetMenu->setStyleSheet("#menu { background-color:rgba(120,120,120,80); border-radius:5px; }");
ui.widgetMenu->show();
ui.vecMenuItems.reserve(20);
ui.layoutMenu = std::make_shared<QGridLayout>(ui.widgetMenu.get());
ui.layoutMenu->setSpacing(2);
ui.layoutMenu->setContentsMargins(2, 2, 2, 2);
ui.labelDT = std::make_shared< QLabel>();
ui.labelDT->setAlignment(Qt::AlignCenter);
ui.labelDT->setText(Utils::timeStr().c_str());
ui.layoutMenu->addWidget(ui.labelDT.get(), 0, 0, 1, 1);
// 设置列宽和行高
ui.layoutMenu->setRowMinimumHeight(0, 40); // 设置第0列的最小宽度为100像素
// 设置列和行的伸缩因子
ui.layoutMenu->setRowStretch(0, 0); // 设置第0列的伸缩因子为0不伸缩
std::vector<std::string > menuItems = {"系统总览", "运行监控"};
for (auto& name: menuItems)
{
this->setObjectName("menu");
this->setStyleSheet("#menu { background-color:rgba(120,120,120,80); }");
this->show();
vecMenuItems.reserve(20);
layout.setSpacing(2);
layout.setContentsMargins(2, 2, 2, 2);
int row = ui.vecMenuItems.size() + 1;
auto itemBtn = std::make_shared<QPushButton>(ui.widgetMenu.get());
ui.vecMenuItems.push_back(itemBtn);
itemBtn->setText(name.c_str());
itemBtn->setStyleSheet(QSS_BTN_MENU.c_str());
if (!ui.curActiveMenuBtn)
{
ui.curActiveMenuBtn = itemBtn;
ui.curActiveMenuBtn->setStyleSheet(QSS_BTN_MENU_ACTIVE.c_str());
}
this->addMenuItem("系统总览");
//this->addMenuItem("运行监控");
}
void addMenuItem(std::string name)
{
int row = vecMenuItems.size();
auto item = std::make_shared<QPushButton>(this);
vecMenuItems.push_back(item);
item->setText(name.c_str());
item->setStyleSheet(QSS_BTN_MENU.c_str());
connect(itemBtn.get(), &QPushButton::clicked, this, [=]()
{
if (ui.curActiveMenuBtn == itemBtn) { return; }
if (ui.curActiveMenuBtn) { ui.curActiveMenuBtn->setStyleSheet(QSS_BTN_MENU.c_str()); }
ui.curActiveMenuBtn = itemBtn;
if (ui.curActiveMenuBtn)
{
ui.curActiveMenuBtn->setStyleSheet(QSS_BTN_MENU_ACTIVE.c_str());
this->onActiveMenu(name);
}
});
QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
item->setSizePolicy(sizePolicy);
layout.addWidget(item.get(), row, 0, 1, 1);
itemBtn->setSizePolicy(sizePolicy);
ui.layoutMenu->addWidget(itemBtn.get(), row, 0, 1, 1);
// 设置列宽和行高
layout.setRowMinimumHeight(row, 50); // 设置第0列的最小宽度为100像素
ui.layoutMenu->setRowMinimumHeight(row, 50); // 设置第0列的最小宽度为100像素
// 设置列和行的伸缩因子
layout.setRowStretch(row, 0); // 设置第0列的伸缩因子为0不伸缩
layout.setRowStretch(row+1, 2); // 设置第1列的伸缩因子为2使其更宽
ui.layoutMenu->setRowStretch(row, 0); // 设置第0列的伸缩因子为0不伸缩
ui.layoutMenu->setRowStretch(row+1, 2); // 设置第1列的伸缩因子为2使其更宽
}
std::vector<std::shared_ptr<QPushButton>> vecMenuItems;
QGridLayout layout;
};
ui.widgetMenu->show();
}
void MainApp::setMyLayout()
{
@@ -99,15 +115,9 @@ void MainApp::setMyLayout()
//layout->setVerticalSpacing(10); // 设置行间距为10像素
//layout->setContentsMargins(10, 10, 10, 10); // 设置内容边距为10像素
QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
MyMenu* menu = new MyMenu(this);
menu->setSizePolicy(sizePolicy);
layout.main->addWidget(menu, 0, 0, 1, 1);
ui.wigetHome = std::make_shared<QWHome>(this);
ui.wigetHome->setSizePolicy(sizePolicy);
layout.main->addWidget(ui.wigetHome.get(), 0, 1, 1, 1);
ui.widgetMenu->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
layout.main->addWidget(ui.widgetMenu.get(), 0, 0, 1, 1);
// 设置列宽和行高
layout.main->setColumnMinimumWidth(0, 200); // 设置第0列的最小宽度为100像素
@@ -117,12 +127,39 @@ void MainApp::setMyLayout()
layout.main->setColumnStretch(0, 0); // 设置第0列的伸缩因子为0不伸缩
layout.main->setColumnStretch(1, 2); // 设置第1列的伸缩因子为2使其更宽
//gridLayout->setRowStretch(0, 1); // 设置第0行的伸缩因子为1
}
void MainApp::onActiveMenu(std::string name)
{
//ui.wigetHome->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
//layout.main->addWidget(ui.wigetHome.get(), 0, 1, 1, 1);
std::shared_ptr<MyWidget> widget {};
if (name == "系统总览") { widget = (ui.wigetHome ? ui.wigetHome : (ui.wigetHome = make_shared<QWHome>(this))); }
else if (name == "运行监控") { widget = (ui.wigetMonitor ? ui.wigetMonitor : (ui.wigetMonitor = make_shared<QWMonitor>(this))); }
else { }
if (widget == ui.curActiveWidget) { return; }
if (ui.curActiveWidget)
{
ui.curActiveWidget->hide();
}
if (widget)
{
widget->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
widget->show();
layout.main->addWidget(widget.get(), 0, 1, 1, 1);
//layout.main->replaceWidget(ui.curActiveWidget.get(), widget.get());
}
ui.curActiveWidget = widget;
}
void MainApp::onTimer()
{
ui.wigetHome->onTimer();
ui.labelDT->setText(Utils::timeStr().c_str());
if (ui.curActiveWidget && ui.curActiveWidget->isVisible())
{
ui.curActiveWidget->onTimer();
}
}

View File

@@ -14,16 +14,17 @@ using namespace std;
#include <mutex>
#include "widgets/QWHome.h"
#include "widgets/QWMonitor.h"
class MainApp : public QWidget
{
Q_OBJECT
public:
MainApp();
void initMenu();
void setMyLayout();
void onActiveMenu(std::string name);
private slots:
void onTimer();
@@ -31,7 +32,18 @@ private slots:
public:
struct {
std::shared_ptr<LabelPair> weburl {};
std::shared_ptr<QLabel> labelDT {};
std::shared_ptr<QWidget> widgetMenu;
std::vector<std::shared_ptr<QPushButton>> vecMenuItems;
std::shared_ptr<QGridLayout> layoutMenu;
std::shared_ptr<QPushButton> curActiveMenuBtn;
std::shared_ptr<MyWidget> curActiveWidget = nullptr;
std::shared_ptr<MyWidget> wigetHome;
std::shared_ptr<MyWidget> wigetMonitor;
} ui;
struct {

View File

@@ -1,19 +1,66 @@
#include "MyQUI.h"
#include "MyQUI.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; }";
static const std::string QSS_BTN =
"QPushButton {background:rgba(80, 80, 80,80);border-radius:5px;border:1px solid gray;color:white;font:bold 13px;}";
static const std::string QSS_BTN_ACTIVE =
"QPushButton {background:rgba(80, 80, 80,80);border-radius:5px;border:1px solid green;color:green;font:bold 15px;}";
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(19, 35, 71,200);color:white;border-radius:5px;border:2px solid rgb(10,120,215);font:bold 18px;}"
"QPushButton:hover {background-color:rgb(32,164,128);}"
"QPushButton:pressed {border-width:3px 0 0 3px;background-color:rgb(1,32,54);border-style:inset;}"
"QPushButton:pressed {border-width:3px 0 0 3px;border-style:inset;}"
"QPushButton:disabled {color:rgb(150,150,150);}";
static const std::string QSS_BTN_MENU_ACTIVE =
"QPushButton {background:rgba(32,164,128, 200);color:white;border-radius:5px;border:2px solid rgb(10,120,215);font:bold 18px;}"
"QPushButton:hover {background-color:rgb(32,164,128);}"
"QPushButton:pressed {border-width:3px 0 0 3px;border-style:inset;}"
"QPushButton:disabled {color:rgb(150,150,150);}";
static const std::string QSS_LINE =
"QLineEdit { background-color: rgb(14, 49, 66); color: #ffffff; border: 1px solid gray; border-radius: 5px; font: bold 13px; }";
static const std::string QSS_TABLE = // 表格整体样式
"QTableWidget {"
" background-color: transparent;" // 背景色
" gridline-color: #C0C0C0;" // 网格线颜色
" border: 1px solid gray;" // 边框
" color: white;" // 文字颜色
"}"
// 表头样式
"QHeaderView { background-color: rgba(150,150,150,50); }"
"QHeaderView::section {"
" background-color: rgba(120,120,120,0);" // 表头背景
" padding: 4px;" // 内边距
" border: 1px solid #505050;" // 边框
" min-height: 25px;" // 最小高度
"}"
// 单元格样式
"QTableWidget::item {"
" padding-left: 5px;"
" border-bottom: 1px solid gray;" // 底部边框
"}"
// 选中状态
"QTableWidget::item:selected {"
" background-color: rgba(184, 214, 255, 50);" // 选中背景色
" color: rgb(220,220,220);" // 选中文字颜色
"}";
MyWidget::MyWidget(QWidget* parent) : QWidget(parent)
{
// 可以在这里设置样式表,也可以在其他地方设置
setObjectName("MyWidget");
setStyleSheet("#MyWidget {background-color:rgba(120,120,120,80); border-radius:5px;}");
// 确保自动填充背景
setAutoFillBackground(true);
}
std::shared_ptr<QGroupBox> MyQUI::GroupBox(QWidget* parent, int x, int y, int w, int h, std::string title)
{
auto groupBox = std::make_shared<QGroupBox>(title.c_str(), parent);
@@ -22,14 +69,33 @@ std::shared_ptr<QGroupBox> MyQUI::GroupBox(QWidget* parent, int x, int y, int w,
return groupBox;
}
void MyQUI::PairLine(QWidget* parent, int x, int y, string k, string v)
MyPairLabelLine MyQUI::PairLine(QWidget* parent, int x, int y, string k, string v, bool readonly/* = true*/)
{
auto key = new QLabel(parent);
shared_ptr<QLabel> key = make_shared<QLabel>(parent);
key->setText(k.c_str());
key->setGeometry(x, y, 80, 26);
auto value = new QLineEdit(parent);
shared_ptr<QLineEdit> value = make_shared<QLineEdit>(parent);
value->setText(v.c_str());
value->setGeometry(x+80, y, 260, 26);
value->setStyleSheet(QSS_LINE.c_str());
value->setReadOnly(true);
value->setReadOnly(readonly);
return {key, value};
}
void MyQUI::setTableCell(std::shared_ptr<QTableWidget> table, int row, int col, std::string text, std::string style /*= ""*/)
{
if (row >= table->rowCount())
{
table->insertRow(row);
}
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)); }
else if (style == "WARN") { item->setForeground(QBrush(Qt::cyan)); }
}

View File

@@ -1,4 +1,4 @@
#pragma once
#pragma once
#include <QGroupBox>
#include <QMainWindow>
@@ -22,13 +22,22 @@
#include <QHeaderView>
#include <memory>
#include <string>
#include <vector>
#include <map>
using namespace std;
extern const std::string QSS_GROUP;
extern const std::string QSS_BTN;
extern const std::string QSS_BTN_ACTIVE;
extern const std::string QSS_BTN_MENU;
extern const std::string QSS_BTN_MENU_ACTIVE;
extern const std::string QSS_LINE;
extern const std::string QSS_TABLE;
class LabelPair
{
public:
@@ -60,17 +69,20 @@ class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget* parent) : QWidget(parent) {}
MyWidget(QWidget* parent);
virtual void onTimer() {};
};
using MyPairLabelLine = pair<shared_ptr<QLabel>, shared_ptr<QLineEdit>>;
class MyQUI
{
public:
static std::shared_ptr<QGroupBox> GroupBox(QWidget* parent, int x, int y, int w, int h, std::string title);
static shared_ptr<QGroupBox> GroupBox(QWidget* parent, int x, int y, int w, int h, std::string title);
static MyPairLabelLine PairLine(QWidget* parent, int x, int y, string k, string v, bool readonly=true);
static void setTableCell(std::shared_ptr<QTableWidget> table, int row, int col, std::string text, std::string style = "");
static void PairLine(QWidget* parent, int x, int y, string k, string v);
};

View File

@@ -7,12 +7,16 @@
#include "app/Station.h"
#include "protocol/MqttEntity.h"
QWHome::QWHome(QWidget* parent) : MyWidget(parent)
{
this->setObjectName("workspace");
this->setStyleSheet("#workspace { background-color:rgba(100,100,100,50); }");
this->setObjectName("home");
this->setStyleSheet("#home { background-color:rgba(100,100,100,50); }");
int x = 10, y = 10;
int x = 10, y = 0;
{
this->groupSys = MyQUI::GroupBox(this, x, y, 1190, 120, "系统");
auto pw = groupSys.get();
@@ -21,54 +25,29 @@ QWHome::QWHome(QWidget* parent) : MyWidget(parent)
x = 10, y += 130;
this->groupHttp = MyQUI::GroupBox(this, x, y, 390, 120, "HTTP");
auto pw = groupHttp.get();
MyQUI::PairLine(pw, 20, 20, "服务类型: ", "服务端");
MyQUI::PairLine(pw, 20, 50, "服务端口: ", Utils::toStr(Config::option.http.port));
MyQUI::PairLine(pw, 20, 80, "服务状态: ", "运行");
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, "服务状态: ", "运行");
}
{
x += 400;
this->groupMqtt = MyQUI::GroupBox(this, x, y, 390, 120, "MQTT");
auto pw = groupMqtt.get();
MyQUI::PairLine(pw, 20, 20, "服务类型: ", "客户端");
MyQUI::PairLine(pw, 20, 50, "服务地址: ", Config::option.mqtt.host);
MyQUI::PairLine(pw, 20, 80, "服务状态: ", "---");
this->addPair("mqtt-t", pw, 20, 20, "服务类型: ", "客户端");
this->addPair("mqtt-h", pw, 20, 50, "服务地址: ", Config::option.mqtt.host);
this->addPair("mqtt-s", pw, 20, 80, "服务状态: ", "---");
}
{
x += 400;
this->groupDB = MyQUI::GroupBox(this, x, y, 390, 120, "数据库");
auto pw = groupDB.get();
MyQUI::PairLine(pw, 20, 20, "数据库名: ", Config::option.database.dbname);
MyQUI::PairLine(pw, 20, 50, "主机地址: ", Config::option.database.host);
MyQUI::PairLine(pw, 20, 80, "用 户 名: ", Config::option.database.user);
this->addPair("db-n", pw, 20, 20, "数据库名: ", Config::option.database.dbname);
this->addPair("db-h", pw, 20, 50, "主机地址: ", Config::option.database.host);
this->addPair("db-u", pw, 20, 80, "用 户 名: ", Config::option.database.user);
const std::string QSS_TABLE = // 表格整体样式
"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->setGeometry(10, y += 130, 1190, 265);
table->setStyleSheet(QSS_TABLE.c_str());
table->horizontalHeader()->setStretchLastSection(true); // 最后一列占满
table->verticalHeader()->setVisible(false); // 不显示垂直表头
@@ -92,36 +71,55 @@ QWHome::QWHome(QWidget* parent) : MyWidget(parent)
table->setColumnWidth(1, 120);
table->setColumnWidth(2, 50);
table->setColumnWidth(4, 80);
}
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);
logFilter = MyQUI::PairLine(this, 10, y += 270, "日志过滤: ", "", false);
btnLogClean = std::make_shared<QPushButton>("清除", this);
btnLogClean->setGeometry(400, y, 80, 26);
connect(btnLogClean.get(), &QPushButton::clicked, this, [=]() { texteditLog->clear(); });
texteditLog = std::make_shared<QTextEdit>(this);
texteditLog->setGeometry(10, y += 30, 1190, 280);
texteditLog->setStyleSheet("background-color: transparent; border: 1px solid gray; font-weight: 400;");
texteditLog->setReadOnly(true);
{
////////////////////////////////////////////////////////////////////////////////////////
// QT显示spdlog: 方法一
// 第二个参数是方法函数名称,即调用 QTextEdit的appeng函数
auto qtSink = std::make_shared<spdlog::sinks::qt_sink_mt>(textLog.get(), "append");
spdlog::default_logger()->sinks().push_back(qtSink);
//auto qtSink = std::make_shared<spdlog::sinks::qt_sink_mt>(textLog.get(), "append");
//spdlog::default_logger()->sinks().push_back(qtSink);
////////////////////////////////////////////////////////////////////////////////////////
// QT显示spdlog: 方法二, 自定义实现 QtLogSink
myqtSink = std::make_shared<QtLogSink>(this);
spdlog::default_logger()->sinks().push_back(myqtSink);
//auto logger = std::make_shared<spdlog::logger>("custom_logger", myqtSink);
//spdlog::register_logger(logger);
// 连接信号和槽使用Qt::QueuedConnection确保线程安全
connect(myqtSink.get(), &QtLogSink::logMessageReceived, this, &QWHome::onLogMessageReceived, Qt::QueuedConnection);
}
}
void QWHome::setTableCell(int row, int col, std::string text, std::string style /*= ""*/)
QWHome::~QWHome()
{
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)); }
table->clear();
mapPairs.clear();
}
void QWHome::addPair(string name, QWidget* parent, int x, int y, string k, string v, bool readonly /*= true*/)
{
if (name.empty()) name = k;
mapPairs[name] = MyQUI::PairLine(parent, x, y, k, v);
}
void QWHome::onTimer()
{
if (!this->isVisible()) { return; }
auto& appdata = Application().data();
int rowNo = 0;
int tsNow = Utils::time();
@@ -135,27 +133,46 @@ void QWHome::onTimer()
bool isOpen = station->status > 0;
bool isConnected = station->mqttCli->isConnected;
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");
MyQUI::setTableCell(table, rowNo, 0, std::to_string(station->stationId));
MyQUI::setTableCell(table, rowNo, 1, station->name);
MyQUI::setTableCell(table, rowNo, 2, station->code);
MyQUI::setTableCell(table, rowNo, 3, isOpen ? "启用" : "未启用", isOpen ? "OK" : "ERR");
MyQUI::setTableCell(table, 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) : "--");
MyQUI::setTableCell(table, rowNo, 5, tsPolling > 0 ? std::to_string(tsNow - tsPolling) + "/" + std::to_string(Config::option.mqtt.interval) : "--");
setTableCell(rowNo, 6, Utils::toStr(station->statData.dayElectIn, 0));
setTableCell(rowNo, 7, Utils::toStr(station->statData.dayElectOut, 0));
setTableCell(rowNo, 8, Utils::toStr(station->statData.totalElectIn, 0));
setTableCell(rowNo, 9, Utils::toStr(station->statData.totalElectOut, 0));
setTableCell(rowNo, 10, Utils::toStr(station->statData.dayFeeIn, 0));
setTableCell(rowNo, 11, Utils::toStr(station->statData.dayFeeOut, 0));
setTableCell(rowNo, 12, Utils::toStr(station->statData.totalFeeIn, 0));
setTableCell(rowNo, 13, Utils::toStr(station->statData.totalFeeOut, 0));
setTableCell(rowNo, 14, Utils::toStr(station->statData.dayIncome, 0));
setTableCell(rowNo, 15, Utils::toStr(station->statData.totalIncome, 0));
setTableCell(rowNo, 16, station->statData.ts > 0 ? Utils::timeStr(station->statData.ts) : "--");
MyQUI::setTableCell(table, rowNo, 6, Utils::toStr(station->statData.dayElectIn, 0));
MyQUI::setTableCell(table, rowNo, 7, Utils::toStr(station->statData.dayElectOut, 0));
MyQUI::setTableCell(table, rowNo, 8, Utils::toStr(station->statData.totalElectIn, 0));
MyQUI::setTableCell(table, rowNo, 9, Utils::toStr(station->statData.totalElectOut, 0));
MyQUI::setTableCell(table, rowNo, 10, Utils::toStr(station->statData.dayFeeIn, 0));
MyQUI::setTableCell(table, rowNo, 11, Utils::toStr(station->statData.dayFeeOut, 0));
MyQUI::setTableCell(table, rowNo, 12, Utils::toStr(station->statData.totalFeeIn, 0));
MyQUI::setTableCell(table, rowNo, 13, Utils::toStr(station->statData.totalFeeOut, 0));
MyQUI::setTableCell(table, rowNo, 14, Utils::toStr(station->statData.dayIncome, 0));
MyQUI::setTableCell(table, rowNo, 15, Utils::toStr(station->statData.totalIncome, 0));
MyQUI::setTableCell(table, rowNo, 16, station->statData.ts > 0 ? Utils::timeStr(station->statData.ts) : "--");
rowNo++;
}
}
void QWHome::onLogMessageReceived(const QString& message)
{
if (!this->isVisible()) { return; }
bool show = true;
if (logFilter.second)
{
auto filter = logFilter.second->text();
if (!filter.isEmpty() && !message.contains(filter))
{
show = false;
}
}
if (show)
{
texteditLog->append(message.trimmed());
}
}

View File

@@ -6,23 +6,54 @@
#include <QTableWidget>
#include <spdlog/sinks/base_sink.h>
#include <QObject>
class QtLogSink : public QObject, public spdlog::sinks::base_sink<std::mutex>
{
Q_OBJECT
public:
QtLogSink(QObject* parent = nullptr) : QObject(parent) {}
protected:
void sink_it_(const spdlog::details::log_msg& msg) override {
spdlog::memory_buf_t formatted;
spdlog::sinks::base_sink<std::mutex>::formatter_->format(msg, formatted);
QString logMessage = QString::fromStdString(fmt::to_string(formatted));
// 发射信号,传递日志消息
emit logMessageReceived(logMessage);
}
void flush_() override {}
signals:
void logMessageReceived(const QString& message);
};
class QWHome : public MyWidget
{
Q_OBJECT
public:
QWHome(QWidget* parent);
~QWHome();
void addPair(string name, QWidget* parent, int x, int y, string k, string v, bool readonly = true);
// 创建自定义sink
//std::shared_ptr<QtTextSink> qtSink;
std::map<string, MyPairLabelLine> mapPairs;
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;
std::shared_ptr<QTextEdit> texteditLog;
MyPairLabelLine logFilter {};
std::shared_ptr<QPushButton> btnLogClean;
std::shared_ptr<QtLogSink> myqtSink;
void onTimer() override;
void setTableCell(int row, int col, std::string text, std::string style = "");
void onLogMessageReceived(const QString& message);
};

View File

@@ -0,0 +1,218 @@
#include "QWMonitor.h"
#include "app/Application.h"
#include "app/AppData.h"
#include "app/Station.h"
#include "app/Device.h"
#include "common/Utils.h"
#include "app/DataStruct.h"
QWMonitor::QWMonitor(QWidget* parent) : MyWidget(parent)
{
//this->setStyleSheet("background-color: red;");
int x = 10, y = 10;
for (auto& iter: Application::data().mapStation)
{
auto station = iter.second;
auto btn = make_shared<QPushButton>(station->name.c_str(), this);
btn->setGeometry(x, y, 120, 36);
btn->setStyleSheet(QSS_BTN.c_str());
x += 130;
if (!curActiveBtn)
{
curActiveBtn = btn;
curActiveBtn->setStyleSheet(QSS_BTN_ACTIVE.c_str());
}
mapBtnStation[btn] = station;
connect(btn.get(), &QPushButton::clicked, this, [=]()
{
if (btn != curActiveBtn)
{
if (curActiveBtn) { curActiveBtn->setStyleSheet(QSS_BTN.c_str()); }
if (btn) { btn->setStyleSheet(QSS_BTN_ACTIVE.c_str()); }
curActiveBtn = btn;
this->initStation(station);
}
});
}
table = std::make_shared<QTableWidget>(this);
table->setGeometry(10, y += 50, 700, 800);
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); // 设置为整行选中
table->horizontalHeader()->setFixedHeight(50);
table->horizontalHeader()->setDefaultSectionSize(60);
QTableWidgetItem* headerItem;
QStringList headerText_Row, headerText_Col;
headerText_Row << "ID" << "类型ID" << "类型名称" << "设备名称" << "编号" << "状态" << "通讯\n状态" << "工作\n状态" << "故障\n状态";
// 设置为水平表头
table->setColumnCount(headerText_Row.size());
table->setHorizontalHeaderLabels(headerText_Row);
table->setColumnWidth(0, 50);
table->setColumnWidth(1, 60);
table->setColumnWidth(2, 120);
table->setColumnWidth(3, 160);
connect(table.get(), &QTableWidget::currentCellChanged, this, &QWMonitor::onCurrentCellChanged);
if (curActiveBtn)
{
this->initStation(mapBtnStation[curActiveBtn]);
}
this->groupStation = MyQUI::GroupBox(this, 730, y, 480, 800, "设备信息");
labDeviceInfo = std::make_shared<QLabel>(groupStation.get());
labDeviceInfo->setGeometry(10, 20, 400, 26);
// 将内容widget设置为滚动区域的widget
uiReg.scroll = new QScrollArea(groupStation.get());
uiReg.scroll->setFixedHeight(600); // 设置滚动区域固定高度600px
uiReg.scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); // 垂直滚动条在需要时出现
//scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); // 水平滚动条在需要时出现
uiReg.scroll->setGeometry(10, 90, 460, 700);
uiReg.scroll->setObjectName("MyScroll");
uiReg.scroll->setStyleSheet("#MyScroll {background-color: transparent; border: 1px solid gray;}");
uiReg.scroll->setWidgetResizable(true);
std::string QSS_LISTVIEW =
"QListView { background-color: transparent; color: white; }"
"QListView::item { background-color: transparent; border-bottom: 0px solid gray; padding: 0px; height: 20px; }";
uiReg.listReg = new QListWidget(groupStation.get());
uiReg.listReg->setStyleSheet(QSS_LISTVIEW.c_str());
uiReg.scroll->setWidget(uiReg.listReg);
};
QWMonitor::~QWMonitor()
{
curDevice = nullptr;
curStation = nullptr;
curActiveBtn = nullptr;
mapBtnStation.clear();
table->clear();
}
void QWMonitor::initStation(shared_ptr<Station> station)
{
table->clearSelection();
this->curDevice = NULL;
if (uiReg.listReg)
{
uiReg.listReg->clearSelection();
uiReg.listReg->clear();
}
this->updateStation(station);
}
void QWMonitor::updateStation(shared_ptr<Station> station)
{
auto& appdata = Application::data();
//table->clearContents();
int row = 0;
for (auto& iter : station->mapDevice)
{
auto& device = iter.second;
MyQUI::setTableCell(table, row, 0, Utils::toStr(device->deviceId));
MyQUI::setTableCell(table, row, 1, Utils::toStr(device->type));
MyQUI::setTableCell(table, row, 2, appdata.getDeviceNameById(device->type));
MyQUI::setTableCell(table, row, 3, device->name);
MyQUI::setTableCell(table, row, 4, device->code);
MyQUI::setTableCell(table, row, 5, device->isOpen ? "启用" : "未启用", device->isOpen ? "OK" : "ERR");
MyQUI::setTableCell(table, row, 6, device->online ? "在线" : "离线", device->online ? "OK" : "ERR");
MyQUI::setTableCell(table, row, 7, device->running ? "工作" : "空闲", device->running ? "OK" : "WARN");
MyQUI::setTableCell(table, row, 8, device->err ? "故障" : "正常", device->err ? "ERR" : "OK");
++row;
}
int rowCount = table->rowCount();
for (int i = station->mapDevice.size(); i<rowCount; ++i)
{
table->removeRow(i);
}
}
shared_ptr<Station> QWMonitor::getCurrentStation()
{
return curActiveBtn ? mapBtnStation[curActiveBtn] : nullptr;
}
void QWMonitor::onCurrentCellChanged(int row, int col, int oldRow, int oldCol)
{
auto station = getCurrentStation();
if (!station)
{
labDeviceInfo->setText("--");
return;
}
if (row != oldRow)
{
auto item = table->item(row, 0);
if (item)
{
auto deviceId = item->text().toInt();
curDevice = station->getDevice(deviceId);
if (curDevice)
{
string info = curDevice->name;
labDeviceInfo->setText(info.c_str());
return;
}
}
}
}
void QWMonitor::onTimer()
{
auto station = getCurrentStation();
if (!station) return;
this->updateStation(station);
if (curDevice)
{
auto mapRegPtr = REGAddr::getRegMapByDeviceType(curDevice->type);
auto& mapDeviceParams = curDevice->mapParams;
stringstream ss;
int ind = 0;
for (auto& iter : mapDeviceParams)
{
ss.str("");
auto& addr = iter.first;
ss << std::setw(3) << std::setfill('0') << ind << ": " << addr << ": " << iter.second;
if (mapRegPtr) {
auto iter = mapRegPtr->find(addr);
if (iter != mapRegPtr->end())
{
ss << " (" << iter->second.name << ")";
}
}
ss << "\n";
if (ind < uiReg.listReg->count())
{
uiReg.listReg->item(ind)->setText(ss.str().c_str());
}
else
{
uiReg.listReg->addItem(ss.str().c_str());
}
++ind;
}
for (int i = uiReg.listReg->count()-1; i>=ind; --i)
{
uiReg.listReg->takeItem(i);
}
}
}

View File

@@ -0,0 +1,51 @@
#pragma once
#include "qt/MyQUI.h"
#include <QScrollArea>
#include <QListWidget>
class Station;
class Device;
class QWMonitor : public MyWidget
{
Q_OBJECT
public:
QWMonitor(QWidget* parent);
~QWMonitor();
void initStation(shared_ptr<Station> station);
shared_ptr<Station> getCurrentStation();
void updateStation(shared_ptr<Station> station);
void onTimer() override;
public slots:
void onCurrentCellChanged(int row, int col, int oldRow, int oldCol);
public:
std::shared_ptr<QPushButton> curActiveBtn;
std::map<std::shared_ptr<QPushButton>, std::shared_ptr<Station>> mapBtnStation;
std::shared_ptr<Station> curStation;
std::shared_ptr<Device> curDevice;
shared_ptr<QTableWidget> table;
std::shared_ptr<QGroupBox> groupStation;
std::shared_ptr<QLabel> labDeviceInfo;
std::shared_ptr<QTextEdit> texteditDevice;
//std::vector<std::shared_ptr<QLabel>> vecLabelRegData;
struct {
//QWidget* widget;
//QVBoxLayout* layout;
QScrollArea* scroll {};
QListWidget* listReg {};
} uiReg;
};