搭建PVB架构,实现前端的基础布局、菜单、表格、图示等功能

This commit is contained in:
lixiaoyuan
2025-08-20 19:00:22 +08:00
parent 5de7687bcc
commit 7e965b6fb4
142 changed files with 28270 additions and 411 deletions

View File

@@ -1,5 +1,23 @@
cmake_minimum_required(VERSION 3.23)
###############################################################################
### define funcitons ###
## function: visual studio source group
macro(ADD_SOURCE_GROUP srcpath)
file(GLOB src_h ${PROJECT_SOURCE_DIR}/${srcpath}/*.h)
file(GLOB src_cpp ${PROJECT_SOURCE_DIR}/${srcpath}/*.cpp)
set(src_tmp ${src_h} ${src_cpp})
if (${srcpath} STREQUAL "./" OR ${srcpath} STREQUAL "." OR ${srcpath} STREQUAL "")
set(groupName src)
else()
set(groupName src/${srcpath})
endif()
source_group(${groupName} FILES ${src_tmp})
list(APPEND SOURCE_FILE ${src_tmp})
endmacro(ADD_SOURCE_GROUP)
###############################################################################
set(PROJECT_NAME EES)
project(${PROJECT_NAME})
@@ -15,6 +33,8 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Od /Ob2")
# 设置 Release 模式下链接器的调试信息
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG")
add_definitions(-DWIN32_LEAN_AND_MEAN)
# Qt_PATH 为 Qt 的安装地址
set(QT_PATH "D:/Programs/Qt5/5.15.2/msvc2019_64")
set(CMAKE_PREFIX_PATH ${QT_PATH}/lib/cmake)
@@ -33,10 +53,10 @@ find_package(Qt5 COMPONENTS
WebEngineWidgets
REQUIRED)
add_definitions(-DWIN32_LEAN_AND_MEAN)
set(ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
set(THIRDPARTY_PATH ${ROOT_PATH}/../thirdparty)
set(PVLIBS_PATH ${ROOT_PATH}/../libs/pvb)
include_directories(
${ROOT_PATH}
@@ -46,24 +66,20 @@ include_directories(
${THIRDPARTY_PATH}
${THIRDPARTY_PATH}/mysql/include
${THIRDPARTY_PATH}/nlohmann_json-3.11.2
${PVLIBS_PATH}/include/pvserver
${PVLIBS_PATH}/include/rllib
)
macro(ADD_SOURCE_GROUP srcpath)
file(GLOB src_h ${PROJECT_SOURCE_DIR}/${srcpath}/*.h)
file(GLOB src_cpp ${PROJECT_SOURCE_DIR}/${srcpath}/*.cpp)
set(src_tmp ${src_h} ${src_cpp})
source_group(src/${srcpath} FILES ${src_tmp})
list(APPEND SOURCE_FILE ${src_tmp})
endmacro(ADD_SOURCE_GROUP)
# 设置编译源文件
ADD_SOURCE_GROUP(./)
ADD_SOURCE_GROUP(.)
ADD_SOURCE_GROUP(common)
ADD_SOURCE_GROUP(app)
ADD_SOURCE_GROUP(database)
ADD_SOURCE_GROUP(protocol)
ADD_SOURCE_GROUP(widgets)
ADD_SOURCE_GROUP(widgets/pages)
#ADD_SOURCE_GROUP(widgets)
#ADD_SOURCE_GROUP(widgets/pages)
ADD_SOURCE_GROUP(pv)
ADD_SOURCE_GROUP(pv/pages)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../bin)
add_executable(${PROJECT_NAME} ${SOURCE_FILE})
@@ -81,5 +97,7 @@ target_link_libraries(${PROJECT_NAME}
target_link_libraries(${PROJECT_NAME}
ws2_32 iphlpapi
${THIRDPARTY_PATH}/mysql/lib/x64/libmysql.lib
${PVLIBS_PATH}/x64/serverlib.lib
${PVLIBS_PATH}/x64/rllib.lib
)

View File

@@ -1,5 +1,4 @@
#include "Admin.h"
#include "app/Dao.h"
#include "common/Logger.h"
Errcode Admin::longin(std::string username, std::string passwd)

49
src/app/AppData.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include "AppData.h"
#include "app/Station.h"
std::shared_ptr<Station> AppData::getStation(int stationId)
{
auto iter = mapStation.find(stationId);
if (iter!=mapStation.end())
{
return iter->second;
}
return nullptr;
}
std::shared_ptr<Station> AppData::getStationByName(std::string name)
{
for (auto iter = mapStation.begin(); iter!=mapStation.end(); ++iter)
{
if (iter->second->name == name)
{
return iter->second;
}
}
return nullptr;
}
void AppData::getStationNames(std::vector<std::string>& vecNames)
{
vecNames.resize(mapStation.size());
int i = 0;
for (auto iter = mapStation.begin(); iter!=mapStation.end(); ++iter)
{
vecNames[i] = iter->second->name;
}
}
std::shared_ptr<Device> AppData::getDevice(int stationId, int deviceId)
{
auto station = getStation(stationId);
if (station)
{
return station->getDevice(deviceId);
}
return nullptr;
}
void AppData::loadStatData()
{
}

48
src/app/AppData.h Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include <cstdint>
#include <string>
#include <vector>
#include <memory>
#include <map>
#include <unordered_map>
class Station;
class Device;
class AppData
{
public:
std::shared_ptr<Station> getStation(int stationId);
std::shared_ptr<Station> getStationByName(std::string name);
void getStationNames(std::vector<std::string>& vecNames);
std::shared_ptr<Device> getDevice(int stationId, int deviceId);
// 读取统计数据: 今日统计数据,累计统计数据
void loadStatData();
public:
///////////////////////////////////////////////////////////////////////////////////////////////
// === 系统 ===
int64_t sysActivationTime {};
///////////////////////////////////////////////////////////////////////////////////////////////
// === 数据库 ===
struct {
std::string host;
int port;
std::string user;
std::string passwd;
} db;
///////////////////////////////////////////////////////////////////////////////////////////////
// === 场站信息 ===
std::unordered_map<int, std::shared_ptr<Station>> mapStation;
///////////////////////////////////////////////////////////////////////////////////////////////
// === 角色定义 ===
};

View File

@@ -1,12 +0,0 @@
#pragma once
class AppSetting
{
public:
static AppSetting& instance() {
static AppSetting inst;
return inst;
}
};

View File

@@ -1,13 +1,92 @@
#include "Application.h"
#include "common/Utils.h"
#include "Config.h"
#include "app/Dao.h"
#include "app/Device.h"
#include "database/DaoEntity.h"
#include "database/Dao.h"
#include "app/Station.h"
#include "app/Device.h"
void InitStation()
{
AppData& appdata = Application::instance().getAppData();
// 读取数据库
std::vector<DataFields> result;
DAO::queryStationList(result);
for (auto& fields: result)
{
int stationId = fields.getInt(DMStation::STATION_ID);
auto station = std::make_shared<Station>(stationId);
station->name = fields.getStr(DMStation::NAME);
station->energyCapacity = fields.getDouble(DMStation::CAPACITY);
appdata.mapStation[stationId] = station;
}
}
void InitDevice()
{
AppData& appdata = Application::instance().getAppData();
vector<DataFields> result;
DAO::queryDeviceList(result);
for (auto& fields: result)
{
int deviceId = fields.getInt(DMDevice::DEVICE_ID);
int stationId = fields.getInt(DMDevice::STATION_ID);
auto station = appdata.getStation(stationId);
if (station)
{
auto device = Device::create(fields);
station->addDevice(deviceId, device);
}
else
{
XLOGE() << "init device error: unknown station_id:[" << stationId << "] device_id=" << deviceId;
}
}
}
void InitStatData()
{
AppData& appdata = Application::instance().getAppData();
std::string curDate = Utils::dateStr();
vector<DataFields> result;
DAO::queryStatDataList(curDate, curDate, result);
for (auto& fields: result)
{
std::string dt = fields.getStr(DMStatStation::DT);
int stationId = fields.getInt(DMStatStation::STATION_ID);
auto station = appdata.getStation(stationId);
if (station)
{
station->storageIn = fields.getFloat(DMStatStation::STORAGE_ELECT_IN);
station->storageOut = fields.getFloat(DMStatStation::STORAGE_ELECT_OUT);
//station->storageNumIn = fields.getFloat(DMStatStation::STORAGE_NUM);
//station->storageNumOut = fields.getFloat(DMStatStation::STORAGE_NUM);
station->storageNumErr = fields.getFloat(DMStatStation::STORAGE_NUM_ERR);
station->solarGen = fields.getFloat(DMStatStation::SOLAR_ELECT_GEN);
station->solarGrid = fields.getFloat(DMStatStation::SOLAR_ELECT_GRID);
station->solarNumErr = fields.getFloat(DMStatStation::SOLAR_NUM_ERR);
station->chargeElect = fields.getFloat(DMStatStation::CHARGE_ELECT);
station->chargeNum = fields.getFloat(DMStatStation::CHARGE_NUM);
station->chargeNumErr = fields.getFloat(DMStatStation::CHARGE_NUM_ERR);
}
else
{
XLOGE() << "init staticis data error: unknown station_id:[" << stationId << "] dt=" << dt;
}
}
}
void Application::init()
{
// 初始化系统配置,读取配置文件
Config::init("assets/config/app.json");
// 设置数据库配置
@@ -23,79 +102,23 @@ void Application::init()
// 连接数据库,读取基础信息
// 初始化场站信息
InitStation();
// 读取设备信息,连接设备
this->initDevice();
InitDevice();
// 读取基础统计信息,在系统总览中需要展示
InitStatData();
// 创建设备处理线程
std::thread([=]() { runThreadDevice(); }).detach();
// 创建主业务循环线程
std::thread([=]() { runThreadMain(); }).detach();
}
static void addDeviceTest(vector<DataFields>& v, int device_id, int type, std::string name, std::string code, int is_open, std::string attrs = "{}")
AppData& Application::getAppData()
{
DataFields fields;
fields.set("device_id", device_id);
fields.set("type", type);
fields.set("name", name);
fields.set("code", code);
fields.set("is_open", is_open);
fields.set("attrs", attrs);
v.push_back(fields);
}
void Application::initDevice()
{
DaoEntity dao("");
std::string sql = "select * from device;";
vector<DataFields> result;
//dao.exec(sql, result);
addDeviceTest(result, 1, 1, "变压器", "", 1);
addDeviceTest(result, 2, 2, "配电柜1", "", 1);
addDeviceTest(result, 3, 3, "电表", "", 1);
addDeviceTest(result, 4, 4, "门禁", "", 1);
addDeviceTest(result, 5, 5, "空调", "", 1);
addDeviceTest(result, 6, 6, "照明", "", 1);
addDeviceTest(result, 7, 7, "消防", "", 1);
addDeviceTest(result, 8, 8, "光照监测设备", "", 1);
addDeviceTest(result, 9, 9, "风速监测设备", "", 1);
addDeviceTest(result, 10, 10, "温湿度监测设备", "", 1);
addDeviceTest(result, 11, 11, "烟感监测设备", "", 1);
addDeviceTest(result, 12, 12, "水浸传感器", "", 1);
addDeviceTest(result, 13, 13, "视频监控", "", 1);
addDeviceTest(result, 14, 101, "逆变器", "", 1);
addDeviceTest(result, 15, 102, "汇流箱", "", 1);
addDeviceTest(result, 16, 103, "光伏板", "", 1);
addDeviceTest(result, 17, 104, "风力发电机", "", 1);
addDeviceTest(result, 18, 105, "储能变流器", "", 1);
addDeviceTest(result, 19, 106, "储能电池", "", 1);
addDeviceTest(result, 20, 107, "BMS", "", 1);
addDeviceTest(result, 21, 108, "充电桩", "", 1);
addDeviceTest(result, 22, 103, "光伏板-1000", "", 1);
addDeviceTest(result, 23, 108, "充电桩-000001", "", 1);
addDeviceTest(result, 24, 103, "光伏板-1001", "", 1);
addDeviceTest(result, 25, 103, "光伏板-1002", "", 1);
addDeviceTest(result, 26, 103, "光伏板-1003", "", 1);
addDeviceTest(result, 27, 103, "光伏板-1004", "", 1);
addDeviceTest(result, 28, 103, "光伏板-1005", "", 1);
addDeviceTest(result, 29, 103, "光伏板-1006", "", 1);
addDeviceTest(result, 30, 103, "光伏板-1007", "", 1);
addDeviceTest(result, 31, 103, "光伏板-1008", "", 1);
addDeviceTest(result, 32, 103, "光伏板-1009", "", 1);
addDeviceTest(result, 33, 103, "光伏板-1010", "", 1);
addDeviceTest(result, 34, 103, "光伏板-1011", "", 1);
addDeviceTest(result, 35, 103, "光伏板-1012", "", 1);
addDeviceTest(result, 36, 106, "储能电池-001", "", 1);
addDeviceTest(result, 37, 106, "储能电池-005", "", 1);
addDeviceTest(result, 38, 106, "储能电池-002", "", 1);
addDeviceTest(result, 39, 106, "储能电池-003", "", 1);
addDeviceTest(result, 40, 106, "储能电池-004", "", 1);
for (auto& fields: result)
{
Device::add(fields);
}
return appdata_;
}
void Application::runThreadMain()
@@ -110,7 +133,6 @@ void Application::runThreadMain()
}
void Application::runThreadDevice()
{
while (!isQuit())

View File

@@ -1,85 +1,10 @@
#pragma once
#include <thread>
#include "common/Logger.h"
#include "Operator.h"
struct AppData
{
/////////////////////////////////////////////
/// === 系统 ===
int64_t sysActivationTime {};
/////////////////////////////////////////////
/// === 数据库 ===
struct {
std::string host;
int port;
std::string user;
std::string passwd;
} db;
/////////////////////////////////////////////
/// === 系统统计 ===
// 累计发电量单位kWh
double electGenTatal {};
// 累计入网电量单位kWh
double electInTotal {};
// 累计收益,单位:元
double incomeTotal {};
// 碳减排量, 单位:吨
double ccers {};
/////////////////////////////////////////////
/// === 环境 ===
// 光照度
double illuminance {};
// 辐照度
double irradiance {};
// 风速
double windspeed {};
// 温度
double temperature {};
// 湿度
double humidity {};
/////////////////////////////////////////////
/// === 日统计 ===
struct {
// 发电量
double electGen {};
// 入网电量
double electIn {};
// 发电收益金额
double incomeElect {};
// 储能电量
double electStorage {};
// 储能次数
int numStore {};
// 放电电量
double electDischarge {};
// 放电次数
int numDischarge {};
// 用电电量
double electLoad {};
// 充电电量
double electCharge {};
// 充电次数
int numCharge {};
// 充电收益
double incomeCharge {};
// 故障次数
int numFault {};
// 故障次数:光伏设备
int numFaultSolar {};
// 故障次数:储能设备
int numFaultStorage {};
// 故障次数:负荷设备
int numFaultLoad {};
} statDay;
};
#include "app/AppData.h"
class Application
{
@@ -91,8 +16,9 @@ public:
}
void init();
void initDevice();
AppData& getAppData();
bool isQuit() { return isQuit_; }
Operator& getOperator() { return op_; }
@@ -100,9 +26,12 @@ public:
void runThreadDevice();
private:
public:
bool isQuit_ = false;
// 登录的管理员信息
Operator op_;
// 应用数据
AppData appdata_;
};

View File

@@ -1,4 +1,4 @@
#include "Dao.h"
#include "Dao1.h"
#include "common/Logger.h"
#include "common/Utils.h"
#include "common/Snowflake.h"
@@ -17,7 +17,7 @@ std::shared_ptr<DaoEntity> DAO::get(std::string tableName)
Errcode DAO::login(std::shared_ptr<DaoEntity> dao, std::string account, std::string passwd, std::string& err)
{
std::string t = Utils::timeNowStr();
std::string t = Utils::timeStr();
if (!dao)
{
dao = std::make_shared<DaoEntity>("");
@@ -90,7 +90,7 @@ bool DAO::writeSystemLog(std::shared_ptr<DaoEntity> dao, int type, std::string u
fieldsLog.set("user_id", userId);
fieldsLog.set("user_account", account);
fieldsLog.set("content", text);
fieldsLog.set("create_time", Utils::timeNowStr());
fieldsLog.set("create_time", Utils::timeStr());
bool ret = dao->insertFields({fieldsLog});
return ret;
}
@@ -131,7 +131,7 @@ int DAO::insertUser(DataFields& fields)
}
fields.set("user_id", Snowflake::instance().getIdStr());
fields.set("create_time", Utils::timeNowStr());
fields.set("create_time", Utils::timeStr());
ret = dao->insertFields(fields);
return (ret) ? 0 : 1;
}

View File

@@ -33,7 +33,7 @@
// return iter->second;
//}
int DeviceEntity::startComm()
int Device::startComm()
{
if (!isOpen)
{
@@ -64,63 +64,58 @@ int DeviceEntity::startComm()
return 0;
}
// ================================================================================================
// $$Device
std::map<int, std::shared_ptr<DeviceEntity>> Device::mapDevices;
void Device::add(DataFields& fields)
std::shared_ptr<Device> Device::create(DataFields& fields)
{
auto entity = std::make_shared<DeviceEntity>();
entity->deviceId = fields.getInt("device_id");
entity->type = fields.getInt("type");
entity->name = fields.getStr("name");
entity->code = fields.getStr("code");
entity->isOpen = fields.getInt("is_open");
entity->attrsJson = fields.getStr("attrs");
auto device = std::make_shared<Device>();
device->deviceId = fields.getInt("device_id");
device->type = fields.getInt("type");
device->name = fields.getStr("name");
device->code = fields.getStr("code");
device->isOpen = fields.getInt("is_open");
device->attrsJson = fields.getStr("attrs");
// 解析属性的JSON字符串转换成键值对
NJson jsonroot;
bool ret = NJsonParse(entity->attrsJson, jsonroot);
bool ret = NJsonParse(device->attrsJson, jsonroot);
if (!ret) // 解析错误
{
XLOGE() << "device attr json parse error, device_id=" << entity->deviceId;
XLOGE() << "device attr json parse error, device_id=" << device->deviceId;
}
else
{
for (auto& [key, val] : jsonroot.items()) {
std::string valType = val.type_name();
if (valType == "string") {
entity->attrs.set(key, val.get<std::string>());
device->attrs.set(key, val.get<std::string>());
}
else if (valType == "number") {
entity->attrs.set(key, val.get<int>());
device->attrs.set(key, val.get<int>());
}
else {
else {
XLOGE() << key << ": [" << valType << "]";
}
}
}
// 保存设备 entity 到 map
if (entity->deviceId != -1)
{
mapDevices[entity->deviceId] = entity;
}
// 启动通讯该函数中会自动判断isOpen状态选择是否进行通讯连接
entity->startComm();
device->startComm();
return device;
}
std::vector<std::shared_ptr<DeviceEntity>> Device::getDeviceByType(int type)
{
std::vector<std::shared_ptr<DeviceEntity>> vecDevice;
for (auto iter = mapDevices.begin(); iter!=mapDevices.end(); ++iter)
{
auto device = iter->second;
if (device && (type<=0 || device->type == type))
{
vecDevice.push_back(device);
}
}
return vecDevice;
}
//
//std::vector<std::shared_ptr<DeviceEntity>> Device::getDeviceByType(int type)
//{
// std::vector<std::shared_ptr<DeviceEntity>> vecDevice;
// for (auto iter = mapDevices.begin(); iter!=mapDevices.end(); ++iter)
// {
// auto device = iter->second;
// if (device && (type<=0 || device->type == type))
// {
// vecDevice.push_back(device);
// }
// }
// return vecDevice;
//}

View File

@@ -9,7 +9,7 @@
class CommEntity;
class DeviceEntity
class Device
{
public:
int deviceId = -1;
@@ -38,17 +38,19 @@ public:
// 启动通讯
int startComm();
static std::shared_ptr<Device> create(DataFields& fields);
};
class Device
{
public:
static void add(DataFields& fields);
static std::vector<std::shared_ptr<DeviceEntity>> getDeviceByType(int type);
public:
static std::map<int, std::shared_ptr<DeviceEntity>> mapDevices;
};
//class Device
//{
//public:
// static void add(DataFields& fields);
//
// static std::vector<std::shared_ptr<DeviceEntity>> getDeviceByType(int type);
//
//public:
// static std::map<int, std::shared_ptr<DeviceEntity>> mapDevices;
//};

View File

@@ -1,17 +1,17 @@
#include "Operator.h"
#include "DAO.h"
#include "common/Logger.h"
bool Operator::login(std::string account, std::string passwd, std::string& err)
{
auto ecode = DAO::login(nullptr, account, passwd, err);
if (ecode == Errcode::OK)
{
XLOGD() << "用户[" + account + "]登录成功";
}
else
{
XLOGD() << "用户[" + account + "]登录失败: " << err;
}
return (ecode == Errcode::OK);
//auto ecode; = DAO::login(nullptr, account, passwd, err);
//if (ecode == Errcode::OK)
//{
// XLOGD() << "用户[" + account + "]登录成功";
//}
//else
//{
// XLOGD() << "用户[" + account + "]登录失败: " << err;
//}
//return (ecode == Errcode::OK);
return true;
}

21
src/app/Station.cpp Normal file
View File

@@ -0,0 +1,21 @@
#include "Station.h"
Station::Station(int id) : id(id)
{
}
void Station::addDevice(int deviceId, std::shared_ptr<Device> device)
{
mapDevice_[deviceId] = device;
}
std::shared_ptr<Device> Station::getDevice(int deviceId)
{
auto iter = mapDevice_.find(deviceId);
if (iter!=mapDevice_.end())
{
return iter->second;
}
return nullptr;
}

74
src/app/Station.h Normal file
View File

@@ -0,0 +1,74 @@
#pragma once
#include <memory>
#include <unordered_map>
class Device;
class Station
{
public:
Station(int id);
void addDevice(int deviceId, std::shared_ptr<Device> device);
std::shared_ptr<Device> getDevice(int deviceId);
public:
int id {};
std::string name;
// 储能容量
double energyCapacity {};
///////////////////////////////////////////////////////////////////////////////////////////////
/// === 系统统计 ===
// 累计发电量单位kWh
double electGenTatal {};
// 累计入网电量单位kWh
double electGridTotal {};
// 累计收益,单位:元
double incomeTotal {};
// 碳减排量, 单位:吨
double ccers {};
// 累计储能充电电量
double electStorageIn {};
// 累计储能放电电量
double electStorageOut {};
///////////////////////////////////////////////////////////////////////////////////////////////
/// === 日统计 ===
double storageIn {}; // 储能充电电量
double storageOut {}; // 储能放电电量
int storageNumIn {}; // 储能充电次数
int storageNumOut {}; // 储能放电次数
int storageNumErr {}; // 储能故障次数
double solarGen {}; // 光伏发电电量
double solarGrid {}; // 光伏入网电量
int solarNumErr {}; // 光伏故障次数
double chargeElect {}; // 充电设备充电电量
int chargeNum {}; // 充电设备充电次数
int chargeNumErr {}; // 充电设备故障次数
double incomeElect {}; // 发电收益金额
double incomeCharge {}; // 充电收益金额
///////////////////////////////////////////////////////////////////////////////////////////////
/// === 环境 ===
// 光照度
double illuminance {};
// 辐照度
double irradiance {};
// 风速
double windspeed {};
// 温度
double temperature {};
// 湿度
double humidity {};
///////////////////////////////////////////////////////////////////////////////////////////////
/// === 设备信息 ===
std::unordered_map<int, std::shared_ptr<Device>> mapDevice_;
};

151
src/common/Crypto.cpp Normal file
View File

@@ -0,0 +1,151 @@
#include "Crypto.h"
#define MD5_A 0x67452301
#define MD5_B 0xefcdab89
#define MD5_C 0x98badcfe
#define MD5_D 0x10325476
const char MD5_S16[] = "0123456789abcdef";
const unsigned int T[] = {
0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,
0x6b901122,0xfd987193,0xa679438e,0x49b40821,
0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,
0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,
0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,
0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,
0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,
0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,
0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,
0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,
0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,
0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391 };
const unsigned int MD5_OFFSET[] = {
7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
};
// 填充字符串
vector<unsigned int> padding(string src, unsigned int& len)
{
// 以512位,64个字节为一组
unsigned int num = ((src.length() + 8) / 64) + 1;
vector<unsigned int> rec(num * 16);
len = num * 16;
for (unsigned int i = 0; i < src.length(); i++) {
// 一个unsigned int对应4个字节保存4个字符信息
rec[i >> 2] |= (int)(src[i]) << ((i % 4) * 8);
}
// 补充1000...000
rec[src.length() >> 2] |= (0x80 << ((src.length() % 4) * 8));
// 填充原文长度
rec[rec.size() - 2] = (src.length() << 3);
return rec;
}
// 整理输出
string md5_format(unsigned int num)
{
string res = "";
unsigned int base = 1 << 8;
for (int i = 0; i < 4; i++) {
string tmp = "";
unsigned int b = (num >> (i * 8)) % base & 0xff;
for (int j = 0; j < 2; j++) {
tmp = MD5_S16[b % 16] + tmp;
b /= 16;
}
res += tmp;
}
return res;
}
// F函数
unsigned int F(unsigned int b, unsigned int c, unsigned int d)
{
return (b & c) | ((~b) & d);
}
// G函数
unsigned int G(unsigned int b, unsigned int c, unsigned int d)
{
return (b & d) | (c & (~d));
}
// H函数
unsigned int H(unsigned int b, unsigned int c, unsigned int d)
{
return b ^ c ^ d;
}
// I函数
unsigned int I(unsigned int b, unsigned int c, unsigned int d)
{
return c ^ (b | (~d));
}
// 移位操作函数
unsigned int shift(unsigned int a, unsigned int n)
{
return (a << n) | (a >> (32 - n));
}
// 循环压缩
void md5_encode(unsigned int* X, int size, unsigned int& tempA, unsigned int& tempB, unsigned int& tempC, unsigned int& tempD)
{
unsigned int a = tempA,
b = tempB,
c = tempC,
d = tempD,
rec = 0,
g, k;
for (int i = 0; i < 64; i++) {
if (i < 16) {
// F迭代
g = F(b, c, d);
k = i;
} else if (i < 32) {
// G迭代
g = G(b, c, d);
k = (1 + 5 * i) % 16;
} else if (i < 48) {
// H迭代
g = H(b, c, d);
k = (5 + 3 * i) % 16;
} else {
// I迭代
g = I(b, c, d);
k = (7 * i) % 16;
}
rec = d;
d = c;
c = b;
b = b + shift(a + g + X[k] + T[i], MD5_OFFSET[i]);
a = rec;
}
tempA += a;
tempB += b;
tempC += c;
tempD += d;
}
string Crypto::md5(string src)
{
unsigned int tempA = MD5_A;
unsigned int tempB = MD5_B;
unsigned int tempC = MD5_C;
unsigned int tempD = MD5_D;
unsigned int len = 0;
vector<unsigned int> rec = padding(src, len);
for (unsigned int i = 0; i < len / 16; i++) {
unsigned int num[16];
for (int j = 0; j < 16; j++) {
num[j] = rec[i * 16 + j];
}
md5_encode(num, 16, tempA, tempB, tempC, tempD);
}
return md5_format(tempA) + md5_format(tempB) + md5_format(tempC) + md5_format(tempD);
}

20
src/common/Crypto.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef _Crypto_H_
#define _Crypto_H_
#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
using namespace std;
class Crypto
{
public:
static string md5(string src);
};
#endif // ! _Crypto_H_

View File

@@ -32,10 +32,17 @@ int DataFields::getInt(string key)
{
return mapFields_.count(key) > 0 ? Utils::toInt(mapFields_[key]) : 0;
}
float DataFields::getFloat(string key)
{
return mapFields_.count(key) > 0 ? Utils::toFloat(mapFields_[key]) : 0.0f;
}
double DataFields::getDouble(string key)
{
return mapFields_.count(key) > 0 ? Utils::toDouble(mapFields_[key]) : 0.0;
}
void DataFields::remove(string key)
{
auto it = mapFields_.find(key);
@@ -143,7 +150,7 @@ string DataFields::get_update_sql(string tbname, std::vector<std::string> vec_ke
return oss.str();
}
void DataFields::foreach_item(function<void(string key, string val)> on_foraach)
void DataFields::foreachItem(function<void(string key, string val)> on_foraach)
{
for (auto it = mapFields_.begin(); it != mapFields_.end(); it++)
{
@@ -176,12 +183,12 @@ bool DataFields::is_float_number(string key)
return true;
}
string DataFields::to_str()
string DataFields::toStr()
{
string s;
for (auto it = mapFields_.begin(); it != mapFields_.end(); it++)
{
s += ("[" + it->first + ":" + it->second + "] ");
s += ("{" + it->first + ":" + it->second + "} ");
}
return s;
}

View File

@@ -10,10 +10,10 @@ using namespace std;
struct PageInfo
{
int total = 0;
int page_id = 1;
int page_size = 10;
int page_max = 0;
int total {0};
int pageIndex {0};
int pageSize {10};
int pageCount {0};
};
class DataFields
@@ -64,7 +64,13 @@ public:
* @param: [string key] 索引名称
*/
float getFloat(string key);
/**
* 获取 double 值
* @param: [string key] 索引名称
*/
double getDouble(string key);
/**
* 删除指定索引的值
* @param: [string key] 索引名称
@@ -116,7 +122,7 @@ public:
* 遍历数据项
* @param: [function... on_foraach] 回调函数
*/
void foreach_item(function<void(string key, string val)> on_foraach);
void foreachItem(function<void(string key, string val)> on_foraach);
/**
* 判断是否含有数据项
@@ -133,7 +139,7 @@ public:
/**
* 转换成键值对的字符串格式
*/
string to_str();
string toStr();
/**
* 获取数据项的大小

View File

@@ -7,7 +7,7 @@
std::mutex g_mutex;
Logger::Logger(Logger::ELogType logtype, std::string filename, int line) : line_(line)
Logger::Logger(Logger::ELogType logtype, std::string filename, int lineEdit) : line_(lineEdit)
{
auto index = filename.find_last_of('\\');
if (index != std::string::npos)
@@ -64,7 +64,7 @@ Logger::~Logger()
}
//std::cout << "[" << filename_ << ":" << line_ << "] [" << Utils::time_now_string_ms() << "][" << strLogType[int(log_type_)] << "] ";
std::cout << "[" << Utils::timeNowStrMS() << "][" << strLogType[int(log_type_)] << "] ";
std::cout << "[" << Utils::timeStrMS() << "][" << strLogType[int(log_type_)] << "] ";
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
std::cout << "- " << text << std::endl << std::flush;
g_mutex.unlock();

View File

@@ -19,7 +19,7 @@ public:
};
public:
Logger(Logger::ELogType logType, std::string filename, int line);
Logger(Logger::ELogType logType, std::string filename, int lineEdit);
~Logger();
std::stringstream& Stream() { return oss_; }

View File

@@ -195,34 +195,13 @@ void parse_from_string()
cout << tp.time_since_epoch().count() << endl;
}
int64_t Utils::timeNow()
{
// return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
return std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()).time_since_epoch().count();
}
int64_t Utils::timeNowMS()
int64_t Utils::time()
{
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
//return std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::now()).time_since_epoch().count();
}
int64_t Utils::timeNowDate()
{
// 获取当前时间
auto now = std::chrono::system_clock::now();
time_t t = std::chrono::system_clock::to_time_t(now);
// 转换为本地时间结构体
struct tm* tmlocal = localtime(&t);
// 设置时分秒为0
tmlocal->tm_hour = 0;
tmlocal->tm_min = 0;
tmlocal->tm_sec = 0;
// 转换回time_t
return mktime(tmlocal);
}
string Utils::timeNowStr(std::string fmt /*= "%Y-%m-%dT%H:%M:%S"*/)
string Utils::timeStr(std::string fmt /*= "%Y-%m-%dT%H:%M:%S"*/)
{
auto t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::stringstream ss;
@@ -230,7 +209,28 @@ string Utils::timeNowStr(std::string fmt /*= "%Y-%m-%dT%H:%M:%S"*/)
return ss.str();
}
string Utils::timeNowStrMS(std::string fmt /*= "%Y-%m-%dT%H:%M:%S"*/)
int64_t Utils::date()
{
// 获取当前时间戳
std::time_t t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
// 转换为本地时间结构体设置时分秒为0
std::tm* tmlocal = localtime(&t);
tmlocal->tm_hour = 0;
tmlocal->tm_min = 0;
tmlocal->tm_sec = 0;
auto tp = std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::from_time_t(mktime(tmlocal)));
return tp.time_since_epoch().count();
}
string Utils::dateStr(std::string fmt /*= "%Y-%m-%d %H:%M:%S"*/)
{
return Utils::timeStr(fmt).substr(0, 10);
}
string Utils::timeStrMS(std::string fmt /*= "%Y-%m-%dT%H:%M:%S"*/)
{
auto tpNow = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(tpNow);
@@ -238,109 +238,110 @@ string Utils::timeNowStrMS(std::string fmt /*= "%Y-%m-%dT%H:%M:%S"*/)
std::stringstream ss;
ss << std::put_time(std::localtime(&time), fmt.c_str());
auto tms = std::chrono::duration_cast<std::chrono::milliseconds>(tpNow.time_since_epoch());
auto tseconds = std::chrono::duration_cast<std::chrono::seconds>(tpNow.time_since_epoch());
auto epoch = tpNow.time_since_epoch();
auto tms = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);
auto tseconds = std::chrono::duration_cast<std::chrono::seconds>(epoch);
ss << "." << std::setfill('0') << std::setw(3) << (tms - tseconds).count();
return ss.str();
}
string Utils::timeStr(int64_t ts, std::string fmt /*= "%Y-%m-%d %H:%M:%S"*/)
{
// std::localtime : 本地时区的时间
// std::gmtime : 格林威治时间
time_t t(ts);
std::stringstream ss;
ss << std::put_time(std::localtime(&t), fmt.c_str());
return ss.str();
}
string Utils::time_to_string(int64_t dt, std::string fmt /*= "%Y-%m-%d %H:%M:%S"*/)
{
auto ms = std::chrono::milliseconds(dt);
auto tp = std::chrono::time_point<std::chrono::system_clock>(ms);
time_t t = std::chrono::system_clock::to_time_t(tp);
std::stringstream ss;
ss << std::put_time(std::localtime(&t), fmt.c_str());
return ss.str();
}
// 2023-12-12 12:12:12
int64_t Utils::time(string dt, int zone)
{
if (dt.empty())
{
return std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()).time_since_epoch().count();
}
else
{
static string FMT_D = "yyyy-MM-dd";
static string FMT_DT = "yyyy-MM-dd HH:mm:ss";
static string FMT_DS = "yyyy-MM-dd HH:mm:ss.SSS";
string fmt;
if (dt.size() == FMT_D.size())
{
fmt = "%Y-%m-%d";
dt[4] = dt[7] = '-';
}
else if (dt.size() == FMT_DT.size())
{
fmt = "%Y-%m-%d %H:%M:%S";
dt[4] = dt[7] = '-';
dt[10] = ' ';
dt[13] = dt[16] = ':';
}
else if (dt.size() == FMT_DT.size())
{
}
else
{
return -1;
}
stringstream ss(dt);
std::tm t {};
ss >> std::get_time(&t, fmt.c_str());
t.tm_hour += zone;
auto tp = std::chrono::system_clock::from_time_t(std::mktime(&t));
return std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch()).count();
//return std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch()).count();
}
}
bool Utils::time_string_to_tm(string dt, std::tm& t)
{
t.tm_year = 1900;
t.tm_mon = 1;
t.tm_mday = 1;
static string FORMAT_D = "yyyy-mm-dd";
static string FORMAT_DT = "yyyy-mm-dd HH:MM:SS";
string fmt;
if (dt.size() == FORMAT_D.size())
{
fmt = "%Y-%m-%d";
dt[4] = dt[7] = '-';
}
else if (dt.size() >= FORMAT_DT.size())
{
fmt = "%Y-%m-%d %H:%M:%S";
dt[4] = dt[7] = '-';
dt[10] = ' ';
dt[13] = dt[16] = ':';
}
else
{
return false;
}
stringstream ss;
ss << dt;
ss >> std::get_time(&t, fmt.c_str());
t.tm_year += 1900;
t.tm_mon += 1;
return true;
}
//
//string Utils::timeStr(int64_t ts, std::string fmt /*= "%Y-%m-%d %H:%M:%S"*/)
//{
// // std::localtime : 本地时区的时间
// // std::gmtime : 格林威治时间
// time_t t(ts);
// std::stringstream ss;
// ss << std::put_time(std::localtime(&t), fmt.c_str());
// return ss.str();
//}
//
//string Utils::time_to_string(int64_t dt, std::string fmt /*= "%Y-%m-%d %H:%M:%S"*/)
//{
// auto ms = std::chrono::milliseconds(dt);
// auto tp = std::chrono::time_point<std::chrono::system_clock>(ms);
// time_t t = std::chrono::system_clock::to_time_t(tp);
// std::stringstream ss;
// ss << std::put_time(std::localtime(&t), fmt.c_str());
// return ss.str();
//}
//
//// 2023-12-12 12:12:12
//int64_t Utils::time(string dt, int zone)
//{
// if (dt.empty())
// {
// return std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()).time_since_epoch().count();
// }
// else
// {
// static string FMT_D = "yyyy-MM-dd";
// static string FMT_DT = "yyyy-MM-dd HH:mm:ss";
// static string FMT_DS = "yyyy-MM-dd HH:mm:ss.SSS";
// string fmt;
// if (dt.size() == FMT_D.size())
// {
// fmt = "%Y-%m-%d";
// dt[4] = dt[7] = '-';
// }
// else if (dt.size() == FMT_DT.size())
// {
// fmt = "%Y-%m-%d %H:%M:%S";
// dt[4] = dt[7] = '-';
// dt[10] = ' ';
// dt[13] = dt[16] = ':';
// }
// else if (dt.size() == FMT_DT.size())
// {
// }
// else
// {
// return -1;
// }
//
// stringstream ss(dt);
//
// std::tm t {};
// ss >> std::get_time(&t, fmt.c_str());
// t.tm_hour += zone;
// auto tp = std::chrono::system_clock::from_time_t(std::mktime(&t));
// return std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch()).count();
// //return std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch()).count();
// }
//}
//
//
//
//bool Utils::time_string_to_tm(string dt, std::tm& t)
//{
// t.tm_year = 1900;
// t.tm_mon = 1;
// t.tm_mday = 1;
// static string FORMAT_D = "yyyy-mm-dd";
// static string FORMAT_DT = "yyyy-mm-dd HH:MM:SS";
// string fmt;
// if (dt.size() == FORMAT_D.size())
// {
// fmt = "%Y-%m-%d";
// dt[4] = dt[7] = '-';
// }
// else if (dt.size() >= FORMAT_DT.size())
// {
// fmt = "%Y-%m-%d %H:%M:%S";
// dt[4] = dt[7] = '-';
// dt[10] = ' ';
// dt[13] = dt[16] = ':';
// }
// else
// {
// return false;
// }
// stringstream ss;
// ss << dt;
// ss >> std::get_time(&t, fmt.c_str());
// t.tm_year += 1900;
// t.tm_mon += 1;
// return true;
//}
void Utils::sleep_ms(int ms)
{

View File

@@ -39,19 +39,22 @@ public:
memcpy_s(&dest[start], len, &src, len);
}
static int64_t timeNow();
static int64_t timeNowMS();
static int64_t timeNowDate();
static string timeNowStr(std::string fmt = "%Y-%m-%d %H:%M:%S");
static string timeNowStrMS(std::string fmt = "%Y-%m-%d %H:%M:%S");
static string timeStr(int64_t ts, std::string fmt = "%Y-%m-%d %H:%M:%S");
// 获取当前时间的时间戳(毫秒)
static int64_t time();
// 获取当前时间的格式字符串
static string timeStr(std::string fmt = "%Y-%m-%d %H:%M:%S");
// 获取当前日期的时间戳(毫秒)
static int64_t date();
// 获取当前日期的格式字符串
static string dateStr(std::string fmt = "%Y-%m-%d %H:%M:%S");
static int64_t time(string dt="", int zone = 0);
static string time_to_string(int64_t dt, std::string fmt="%Y-%m-%d %H:%M:%S");
static bool time_string_to_tm(string dt, std::tm& t);
static string timeStrMS(std::string fmt = "%Y-%m-%d %H:%M:%S");
//static string timeStr(int64_t ts, std::string fmt = "%Y-%m-%d %H:%M:%S");
//static int64_t time(string dt="", int zone = 0);
//static string time_to_string(int64_t dt, std::string fmt="%Y-%m-%d %H:%M:%S");
//static bool time_string_to_tm(string dt, std::tm& t);
std::string duration_str(int64_t t)
{
@@ -89,12 +92,12 @@ public:
TimeTick()
{
tickMS_ = Utils::timeNowMS();
tickMS_ = Utils::time();
}
bool elapse(int64_t ms, bool reset = true)
{
auto tick_now = Utils::timeNowMS();
auto tick_now = Utils::time();
bool res = tick_now - tickMS_ > ms;
if (res && reset)
{
@@ -105,7 +108,7 @@ public:
void reset()
{
tickMS_ = Utils::timeNowMS();
tickMS_ = Utils::time();
}
};

172
src/database/Dao.cpp Normal file
View File

@@ -0,0 +1,172 @@
#include "Dao.h"
#include "common/Utils.h"
std::string DAO::sqlPageLimit(int index, int size)
{
int startIndex = index * size;
if (startIndex < 0) { startIndex = 0; }
return " LIMIT " + std::to_string(startIndex) + "," + std::to_string(size);
}
bool DAO::count(DaoEntity& dao, std::string tableName, std::string condition, int& count)
{
std::string sql = "SELECT COUNT(*) count FROM " + tableName;
if (!condition.empty()) { sql += " WHERE " + condition; };
sql += ";";
std::vector<DataFields> result;
bool ret = dao.exec(sql, result);
if (ret)
{
count = (result.size() > 0) ? result[0].getInt("count") : 0;
}
return ret;
}
static bool QueryCount(DaoEntity& dao, std::string sqlFrom, int& count)
{
std::vector<DataFields> result;
bool ret = dao.exec("SELECT COUNT(*) count " + sqlFrom, result);
if (ret)
{
count = (result.size() > 0) ? result[0].getInt("count") : 0;
}
return ret;
}
static bool QueryPagination(std::string sqlFrom, PageInfo& pageInfo, vector<DataFields>& result)
{
DaoEntity dao("");
int count {0};
if (!QueryCount(dao, sqlFrom, count))
{
return false;
}
pageInfo.total = count;
std::string sql = "SELECT * " + sqlFrom + DAO::sqlPageLimit(pageInfo.pageIndex, pageInfo.pageSize);
bool ret = dao.exec(sql, result);
if (!ret)
{
XLOGE() << "DAO database error: sql=" << sql;
}
return ret;
}
bool DAO::queryUserList(PageInfo& pageInfo, vector<DataFields>& result)
{
DaoEntity dao("");
std::string sqlFrom = "FROM " + DMUser::TABLENAME;
bool ret = QueryPagination(sqlFrom, pageInfo, result);
if (!ret)
{
XLOGE() << "DAO database error: queryUserList failed.";
}
return ret;
}
bool DAO::updateUserById(DataFields& params)
{
std::string userId = params.getStr(DMUser::USER_ID);
params.remove(DMUser::USER_ID);
DaoEntity dao(DMUser::TABLENAME);
return dao.updateFields(params, "WHERE " + DMUser::USER_ID + "='" + userId + "'");
}
bool DAO::queryRoleList(PageInfo& pageInfo, vector<DataFields>& result)
{
DaoEntity dao("");
std::string sqlFrom = "FROM " + DMRole::TABLENAME;
bool ret = QueryPagination(sqlFrom, pageInfo, result);
if (!ret)
{
XLOGE() << "DAO database error: queryRoleList failed.";
}
return ret;
}
bool DAO::queryPermissionList(PageInfo& pageInfo, vector<DataFields>& result)
{
DaoEntity dao("");
std::string sqlFrom = "FROM " + DMPermission::TABLENAME;
bool ret = QueryPagination(sqlFrom, pageInfo, result);
if (!ret)
{
XLOGE() << "DAO database error: queryPermissionList failed.";
}
return ret;
}
// 查询场站信息列表
bool DAO::queryStationList(vector<DataFields>& result)
{
std::string sql = "SELECT * FROM " + DMStation::TABLENAME;
return DaoEntity::execOnce(sql, result);
}
// 分页查询场站信息列表
bool DAO::queryStationList(PageInfo& pageInfo, vector<DataFields>& result)
{
DaoEntity dao("");
std::string sqlFrom = "FROM " + DMStation::TABLENAME;
bool ret = QueryPagination(sqlFrom, pageInfo, result);
if (!ret)
{
XLOGE() << "DAO database error: queryStationList failed.";
}
return ret;
}
// 查询设备信息列表
bool DAO::queryDeviceList(vector<DataFields>& result)
{
std::string sql = "SELECT * FROM " + DMDevice::TABLENAME;
return DaoEntity::execOnce(sql, result);
}
// 分页查询设备信息列表
bool DAO::queryDeviceList(PageInfo& pageInfo, vector<DataFields>& result)
{
DaoEntity dao("");
std::string sqlFrom = "FROM " + DMDevice::TABLENAME;
bool ret = QueryPagination(sqlFrom, pageInfo, result);
if (!ret)
{
XLOGE() << "DAO database error: queryDeviceList failed.";
}
return ret;
}
// 策略管理
bool DAO::queryPolicyList(PageInfo& pageInfo, vector<DataFields>& result)
{
DaoEntity dao("");
std::string sqlFrom = "FROM " + DMPolicy::TABLENAME;
bool ret = QueryPagination(sqlFrom, pageInfo, result);
if (!ret)
{
XLOGE() << "DAO database error: queryPolicyList failed.";
}
return ret;
}
// 系统日志管理
bool DAO::querySystemLogList(PageInfo& pageInfo, vector<DataFields>& result)
{
DaoEntity dao("");
std::string sqlFrom = "FROM " + DMSystemLog::TABLENAME;
bool ret = QueryPagination(sqlFrom, pageInfo, result);
if (!ret)
{
XLOGE() << "DAO database error: querySystemLogList failed.";
}
return ret;
}
bool DAO::queryStatDataList(std::string startDate, std::string endDate, vector<DataFields>& result)
{
std::string sql = "SELECT * FROM " + DMStatStation::TABLENAME + " WHERE dt BETWEEN '" + startDate + "' AND '" + endDate + "';";
return DaoEntity::execOnce(sql, result);
}

54
src/database/Dao.h Normal file
View File

@@ -0,0 +1,54 @@
#pragma once
#include "DaoEntity.h"
#include "DataModelDef.h"
#include "common/Logger.h"
class DAO
{
public:
static std::string sqlPageLimit(int index, int size);
static bool count(DaoEntity& dao, std::string tableName, std::string condition, int& count);
///////////////////////////////////////////////////////////////////////////////////////////////
// === 用户管理
static bool queryUserList(PageInfo& pageInfo, vector<DataFields>& result);
static bool updateUserById(DataFields& params);
///////////////////////////////////////////////////////////////////////////////////////////////
// === 角色管理
static bool queryRoleList(PageInfo& pageInfo, vector<DataFields>& result);
///////////////////////////////////////////////////////////////////////////////////////////////
// === 权限管理
static bool queryPermissionList(PageInfo& pageInfo, vector<DataFields>& result);
///////////////////////////////////////////////////////////////////////////////////////////////
// === 场站管理
// 查询场站信息列表
static bool queryStationList(vector<DataFields>& result);
// 分页查询场站信息列表
static bool queryStationList(PageInfo& pageInfo, vector<DataFields>& result);
///////////////////////////////////////////////////////////////////////////////////////////////
// === 设备管理
// 查询设备信息列表
static bool queryDeviceList(vector<DataFields>& result);
// 分页查询设备信息列表
static bool queryDeviceList(PageInfo& pageInfo, vector<DataFields>& result);
///////////////////////////////////////////////////////////////////////////////////////////////
// === 策略管理
// 分页查询策略信息列表
static bool queryPolicyList(PageInfo& pageInfo, vector<DataFields>& result);
///////////////////////////////////////////////////////////////////////////////////////////////
// === 系统日志管理
// 分页查询系统日志列表
static bool querySystemLogList(PageInfo& pageInfo, vector<DataFields>& result);
///////////////////////////////////////////////////////////////////////////////////////////////
// === 统计数据管理
static bool queryStatDataList(std::string startDate, std::string endDate, vector<DataFields>& result);
};

View File

@@ -192,20 +192,21 @@ bool DaoEntity::queryFields(string keys, const string& sql_c, PageInfo& pageinfo
return true;
}
pageinfo.page_max = pageinfo.total / pageinfo.page_size + (pageinfo.total % pageinfo.page_size > 0 ? 1 : 0);
pageinfo.pageCount = pageinfo.total / pageinfo.pageSize + (pageinfo.total % pageinfo.pageSize > 0 ? 1 : 0);
oss.str("");
if (pageinfo.page_id <= 0)
if (pageinfo.pageIndex <= 0)
{
pageinfo.page_id = 1;
pageinfo.pageIndex = 1;
}
int start = (pageinfo.page_id - 1) * pageinfo.page_size;
oss << "SELECT " << keys << " FROM `" << tableName_ << "` " << sql_c << " LIMIT " << start << "," << pageinfo.page_size << ";";
int start = (pageinfo.pageIndex - 1) * pageinfo.pageSize;
oss << "SELECT " << keys << " FROM `" << tableName_ << "` " << sql_c << " LIMIT " << start << "," << pageinfo.pageSize << ";";
return this->db_->exec(oss.str().c_str(), result);
}
bool DaoEntity::updateFields(DataFields& fields, const string& sql_c)
{
string sql = fields.get_update_sql(tableName_, sql_c);
std::cout << sql;
if (sql_c.empty())
{
//Spdlogger::error("[DB] update condition is empty, not exec, sql={}", sql);

138
src/database/DataModelDef.h Normal file
View File

@@ -0,0 +1,138 @@
#include <string>
using namespace std;
///////////////////////////////////////////////////////////////////////////////////////////////////
/// 用户 表结构字段
namespace DMUser
{
const string TABLENAME = "user"; // 表名称
const string USER_ID = "user_id";
const string ACCOUNT = "account";
const string PASSWD = "passwd";
const string NAME = "name";
const string GENDER = "gender";
const string AGE = "age";
const string PHONE = "phone";
const string EMAIL = "email";
const string IS_OPEN = "is_open";
const string LOGINTIME = "login_time";
const string CREATETIME = "create_time";
const string UPDATETIME = "update_time";
}
///////////////////////////////////////////////////////////////////////////////////////////////////
/// 角色 表结构字段
namespace DMRole
{
const string TABLENAME = "role";
const string ROLE_ID = "role_id";
const string NAME = "name";
const string TYPE = "type";
const string DESCRIBE = "describe";
const string IS_OPEN = "is_open";
const string CREATETIME = "create_time";
const string UPDATETIME = "update_time";
}
namespace DMPermission
{
const string TABLENAME = "permission";
const string PERMISSION_ID = "permission_id";
const string NAME = "name";
const string DESCRIBE = "describe";
const string IS_OPEN = "is_open";
const string CREATETIME = "create_time";
const string UPDATETIME = "update_time";
}
namespace DMRolePermission
{
const string TABLENAME = "role_permission";
const string FID_ID = "id";
const string FID_ROLE_ID = "role_id";
const string FID_PERMISSION_ID = "permission_id";
}
namespace DMStation
{
const string TABLENAME = "station";
const string STATION_ID = "station_id";
const string NAME = "name";
const string ADDRESS = "address";
const string LONGITUDE = "lon";
const string LATITUDE = "lat";
const string TEL = "tel";
const string CAPACITY = "capacity";
const string STATUS = "status";
}
namespace DMDevice
{
const string TABLENAME = "device";
const string DEVICE_ID = "device_id";
const string STATION_ID = "station_id";
const string TYPE = "type";
const string NAME = "name";
const string CODE = "code";
const string MODEL = "model";
const string FACTORY = "factory";
const string TEL = "factory_tel";
const string ATTRS = "attrs";
const string IS_OPEN = "is_open";
const string STATUS = "status";
const string CREATE_TIME = "create_time";
const string UPDATE_TIME = "update_time";
}
namespace DMPolicy
{
const string TABLENAME = "policy";
const string POLICY_ID = "policy_id";
const string TYPE = "type";
const string NAME = "name";
const string DESCRIBE = "describe";
const string VALUE = "value";
const string IS_OPEN = "is_open";
const string CREATE_TIME = "create_time";
const string UPDATE_TIME = "update_time";
}
namespace DMSystemLog
{
const string TABLENAME = "system_log";
const string LOG_ID = "log_id";
const string TYPE = "type";
const string USER_ID = "user_id";
const string USER_ACCOUNT = "user_account";
const string CONTENT = "content";
const string STATUS = "status";
const string CREATE_TIME = "create_time";
const string UPDATE_TIME = "update_time";
}
namespace DMAlertLog
{
const string TABLENAME = "alert_log";
const string LOG_ID = "log_id";
const string TYPE = "type";
const string DEVICE_ID = "device_id";
const string CONTENT = "content";
const string STATUS = "status";
const string CREATE_TIME = "create_time";
}
namespace DMStatStation
{
const string TABLENAME = "stat_staion";
const string DT = "dt";
const string STATION_ID = "station_id";
const string STORAGE_ELECT_IN = "storage_elect_in";
const string STORAGE_ELECT_OUT = "storage_elect_out";
const string STORAGE_NUM_ERR = "storage_num_err";
const string SOLAR_ELECT_GEN = "solar_elect_gen";
const string SOLAR_ELECT_GRID = "solar_elect_grid";
const string SOLAR_NUM_ERR = "solar_num_err";
const string CHARGE_ELECT = "charge_elect";
const string CHARGE_NUM = "charge_num";
const string CHARGE_NUM_ERR = "charge_num_err";
}

View File

@@ -5,17 +5,18 @@
#include <QtWebEngineWidgets/QtWebEngineWidgets>
#include <filesystem>
#include "widgets/MainWindow.h"
#include "common/Utils.h"
#include "app/Application.h"
#include "app/Dao.h"
#include "common/Snowflake.h"
#include "protocol/TcpEntity.h"
#include "common/JsonN.h"
#include "app/Application.h"
#include "app/Config.h"
#include "protocol/TcpEntity.h"
#include "widgets/MainWindow.h"
#include "pv/PvApp.h"
#include "pv/PvUser.h"
int main(int argc, char** argv)
{
@@ -69,15 +70,22 @@ int main(int argc, char** argv)
Application::instance().init();
// 运行QT主窗口
QApplication qapp(argc, argv);
MainWindow mainWin;
mainWin.setWindowTitle("风光储能站控制管理系统");
mainWin.resize(1920, 1080);
mainWin.show();
qapp.exec();
//QApplication qapp(argc, argv);
//MainWindow mainWin;
//mainWin.setWindowTitle("风光储能站控制管理系统");
//mainWin.resize(1920, 1080);
//mainWin.show();
//qapp.exec();
//while (1) {
// std::this_thread::sleep_for(std::chrono::seconds(1));
//}
PARAM p;
int s;
pvInit(argc, argv, &p);
/* here you may interpret ac,av and set p->user to your data */
while(1)
{
s = pvAccept(&p);
if(s != -1) pvCreateThread(&p,s);
}
return 0;
}

219
src/pv/#/PvPop.cpp Normal file
View File

@@ -0,0 +1,219 @@
#include "PvPop.h"
#include "PvInstance.h"
#include "common/Logger.h"
static const string STYLE_LAB = "color:white;font:bold 18px;border:1px solid rgba(120,120,120,0);";
string PvPop::POP_DEFAULT = "";
//PanelPop::PanelPop(PARAM* p, int parent, string title, int w, int h)
//
//{
//
//}
void PvPop::update(PARAM* p)
{
}
void PvPop::set_pop_cb(std::function<void(PARAM* p, int status)> cb)
{
cb_pop_ = cb;
};
void PvPop::set_enabled(string title, bool b)
{
if (!title.empty())
{
if (map_w_.find(title) != map_w_.end())
{
pvSetEnabled(p_, map_w_[title].pvid, b);
}
return;
}
for (auto it = map_w_.begin(); it != map_w_.end(); it++)
{
pvSetEnabled(p_, it->second.pvid, b);
}
}
void PvPop::on_click_ok()
{
};
void PvPop::set_status(string status)
{
status_ = status;
string s = title_;
if (!status_.empty())
{
s += (":" + status_);
}
pvSetText(p_, lab_title_, s.c_str());
}
string PvPop::status()
{
return status_;
}
void PvPop::set_err(PARAM* p, string err)
{
err_ = err;
pvSetText(p, id_err_, err.c_str());
}
string PvPop::err()
{
return err_;
}
void PvPop::reset(PARAM* p)
{
}
PvPop::PvPop(PARAM* p, int parent, string title, int w /*= 900*/, int h /*= 700*/)
: PvWidget(p, parent, PvRect(0, 0, 1920, 1080)), width_(w), height_(h), title_(title)
{
// 半透全屏背景,遮盖后面的控件,禁止操作底层控件
PvManager::label(p, pvid_, "", PvRect(0, 0, 1920, 1080), "background-color:rgba(50,50,50,0);");
// 弹框的有效工作区域
rt_pop_.set((1920 - w) * 0.5, (1080 - h) * 0.5, w, h);
// 工作区(包含顶部标题和底部按钮)
w_main_ = PvManager::widget(p, pvid_, rt_pop_);
// 工作区背景
PvManager::label(p, w_main_, "", PvRect(0, 0, w, h), "border:1px solid #26dcac;border-radius:10;background-color:rgb(21,68,94);");
// 顶部标题
lab_title_ = PvManager::label(p, w_main_, title, PvRect(1, 1, w - 2, 48), "border-radius:10;background-color:rgb(49,132,149);font:bold 20px;color:rgb(255,255,255);");
pvSetAlignment(p, lab_title_, AlignCenter);
// 用户工作区
w_worksace_ = PvManager::widget(p, w_main_, PvRect(1, 48, w - 2, (h - 120)));
// 关闭按钮
PvManager::button_image(p, w_main_, "resource/pv.btn.close.png", "", "", PvRect(w - 40, 10, 29, 29), [=](string s)
{
this->set_status("");
this->hide();
});
static string style_btn_ok =
"QPushButton{background-color:rgb(38,220,172);font:bold 20px;color:white;border-radius:8;}"
"QPushButton:hover{border:2px solid red;}"
"QPushButton:pressed{border-width:3px 0 0 3px;border-style:inset;}"
"QPushButton:disabled{color:rgb(150,150,150);}";
static string style_btn_cannel =
"QPushButton{background-color:rgb(200,207,216);font:bold 20px;color:black;border-radius:8;}"
"QPushButton:hover{border:2px solid red;}"
"QPushButton:pressed{border-width:3px 0 0 3px;border-style:inset;}"
"QPushButton:disabled{color:rgb(150,150,150);}";
int btn_ok = PvManager::button(p, w_main_, "确定", PvRect(w * 0.5 - 120 - 20, h - 60, 120, 40), style_btn_ok);
int btn_cancel = PvManager::button(p, w_main_, "取消", PvRect(w * 0.5 + 20, h - 60, 120, 40), style_btn_cannel);
PvInstance::bind_event(p, PvEvent::BUTTON_EVENT, btn_ok, [=](string e)
{
this->on_click_ok();
if (cb_pop_)
{
cb_pop_(p, 1);
}
else
{
this->hide();
}
});
PvInstance::bind_event(p, PvEvent::BUTTON_EVENT, btn_cancel, [=](string e)
{
this->set_status("");
this->hide();
});
// 错误提示栏
id_err_ = PvManager::label(p, w_main_, "", PvRect(50, h - 60 - 30, 700, 30), "background-color:rgba(50,50,50,30);color:red;font:bold 16px;");
// 默认不显示
this->set_visible(false);
w_param_ = make_shared<PanelParam>(p, w_worksace_, PvRect(0, 0, w, h));
w_param_->set_line_callback([=](shared_ptr<PvLineParam> line, string old_v, string v)
{
string id = line->get_id();
data_.set(id, v);
if (cb_line_changed_)
{
cb_line_changed_(line);
}
});
}
void PvPop::set_data(DataFields& data)
{
for (auto& it : data.fields())
{
this->set_data(it.first, it.second);
}
};
void PvPop::set_data(string id, string v)
{
data_.set(id, v);
if (w_param_)
{
w_param_->set_line_data(id, v);
}
}
string PvPop::get_data(string k)
{
return data_.get_str(k);
}
DataFields& PvPop::get_data()
{
return data_;
};
void PvPop::set_line_callback(function<void(shared_ptr<PvLineParam>)> cb)
{
cb_line_changed_ = cb;
}
void PvPop::on_show()
{
}
void PvPop::on_hide()
{
//w_param_->clear();
this->do_cleanup();
}
void PvPop::do_cleanup()
{
};
void PvPop::foreach_line(std::function<void(string k, string v)> cb)
{
for (auto& item : data_.fields())
{
if (cb)
{
cb(item.first, item.second);
}
}
}
void PvPop::clear()
{
data_.clear();
w_param_->clear();
}
PvPopConfirm::PvPopConfirm(PARAM* p, int parent, string title, int w, int h)
: PvPop(p, parent, title, w, h)
{
std::string style = "color:rgb(255,255,255);font:bold 20px;padding-left:10;";
PvManager::label(p, w_worksace_, "是否确定操作?", PvRect(20, 30, w, 40), style);
}

87
src/pv/#/PvPop.h Normal file
View File

@@ -0,0 +1,87 @@
#pragma once
#include <functional>
#include "DataFields.h"
#include "PvApp.h"
struct PopLineInfo
{
string title; // 控件名称标题
string type; // 控件类型
int pvid; // 值的控件ID
//PopLineInfo(string s, string t, int id) : title(s), type(t), pvid(id) {}
};
class PvPop : public PvObject
{
public:
PvPop(PARAM* p, int parent, string title, int w = 900, int h = 700);
void set_data(DataFields& data);
void set_data(string k, string v);
string get_data(string k);
DataFields& get_data();
void set_line_callback(function<void(shared_ptr<PvLineParam>)> cb);
void on_show() override;
void on_hide() override;
virtual void do_cleanup();
void foreach_line(std::function<void(string k, string v)>);
void set_status(string status);
string status();
void update(PARAM* p);
void set_pop_cb(std::function<void(PARAM* p, int status)> cb);
void set_err(PARAM* p, string err);
string err();
void reset(PARAM* p);
void set_enabled(string title, bool b);
virtual void on_click_ok();
void clear();
public:
static string POP_DEFAULT;
protected:
shared_ptr<PanelParam> w_param_;
// 原始数据
DataFields data_;
function<void(shared_ptr<PvLineParam>)> cb_line_changed_ = nullptr;
int w_main_;
int w_worksace_;
int lab_title_;
int id_err_;
int width_;
int height_;
PvRect rt_pop_;
string title_;
string status_;
string err_;
// 弹出窗口的行控件信息
unordered_map<string, PopLineInfo> map_w_;
std::function<void(PARAM* p, int status)> cb_pop_ = nullptr;
};
class PvPopConfirm : public PvPop
{
public:
PvPopConfirm(PARAM* p, int parent, string title, int w = 900, int h = 700);
int lab_text_ = PV_INVALID_ID;
};

119
src/pv/MaskMain.cpp Normal file
View File

@@ -0,0 +1,119 @@
#include "MaskMain.h"
#include "pv/pages/MaskPageHome.h"
#include "pv/pages/MaskPageRunning.h"
#include "pv/pages/MaskPageForecast.h"
#include "pv/pages/MaskPageStat.h"
#include "pv/pages/MaskPageSysmgr.h"
#include "common/DataFields.h"
#include <iostream>
#include <iomanip>
#include <ctime>
#include <sstream>
std::string GetDateTimeWeekday()
{
static std::vector<std::string> wdaysStr = {"星期天", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
std::time_t now = std::time(nullptr);
std::tm tm = *std::localtime(&now);
std::stringstream ss;
ss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S ") << wdaysStr[tm.tm_wday];
return ss.str();
}
int MaskMain::initUI()
{
pvSetStyleSheet(p, PV_ID_MAIN, "color: white; font: normal 14px \"微软雅黑\";");
ui.bkg = PvApp::pvid(p);
PvApp::image(p, 0, 0, 0, 1920, 1080, "bkg.png");
ui.datetime = PvApp::label(p, PV_ID_MAIN, 10, 30, 420, 30, GetDateTimeWeekday(), "font: bold 18px;");
pvSetAlignment(p, ui.datetime, AlignCenter);
int idStationTitle = PvApp::label(p, 0, 620, 0, 660, 90, "能源站监控与运行管理系统", "font:bold 48px \"Microsoft YaHei\"; color:white");
pvSetAlignment(p, idStationTitle, AlignCenter);
// 初始化子页面
int menuActive = PV_ID_NUL;
switch (pvcode_)
{
case EPvCode::NUL:
case EPvCode::MASK_LOGIN: {}
case EPvCode::MASK_HOME: { maskPage_ = std::make_shared<MaskPageHome>(p); }
break;
case EPvCode::MASK_RUNNING: { maskPage_ = std::make_shared<MaskPageRunning>(p); }
break;
case EPvCode::MASK_FORECAST_STORAGE:
case EPvCode::MASK_FORECAST_SOLAR:
case EPvCode::MASK_FORECAST_CHARGE:
case EPvCode::MASK_FORECAST: { maskPage_ = std::make_shared<MaskPageForecast>(p); }
break;
case EPvCode::MASK_STAT_STORAGE:
case EPvCode::MASK_STAT_SOLAR:
case EPvCode::MASK_STAT_CHARGE:
case EPvCode::MASK_STAT: { maskPage_ = std::make_shared<MaskPageStat>(p); }
break;
case EPvCode::MASK_SYSMGR:
case EPvCode::MASK_MGR_USER:
case EPvCode::MASK_MGR_ROLE:
case EPvCode::MASK_MGR_PERMISSION:
case EPvCode::MASK_MGR_STATION:
case EPvCode::MASK_MGR_DEVICE:
case EPvCode::MASK_MGR_POLICY:
case EPvCode::MASK_MGR_SYSLOG:
case EPvCode::MASK_MGR_ALERTLOG: { maskPage_ = std::make_shared<MaskPageSysmgr>(p); }
break;
default: { maskPage_ = std::make_shared<MaskPageHome>(p); }
break;
}
if (maskPage_)
{
maskPage_->initUI(pvcode_);
}
std::vector<std::string> vecMenuItems = {"系统总览", "运行监控", "预测管理", "统计分析", "系统管理"};
{
int n = vecMenuItems.size();
int y=980, w = 100, margin = 10, h=40;
int x0 = (1920 - (w*n + margin*(n-1))) * 0.5;
for (int i = 0; i<vecMenuItems.size(); ++i)
{
std::string& title = vecMenuItems[i];
EPvCode statusTmp = PvApp::getPvCode(title);
int x = x0 + (w+margin)*i;
int id = PvApp::button(p, 0, x, y, w, h, title, (statusTmp == pvcode_) ? STYLE_BTN_ACTIVE : STYLE_BTN);
mapMenuInfo_[id] = std::make_pair(title, statusTmp);
}
}
return 0;
}
std::shared_ptr<PvMask> MaskMain::createSubPage(int pvid)
{
return nullptr;
}
EPvCode MaskMain::onEventNull(int pvid)
{
pvSetText(p, ui.datetime, GetDateTimeWeekday().c_str());
return EPvCode::NUL;
}
EPvCode MaskMain::onEventButton(int pvid)
{
EPvCode pvcode = EPvCode::NUL;
auto iter = mapMenuInfo_.find(pvid);
if (iter != mapMenuInfo_.end())
{
return iter->second.second;
}
else
{
if (maskPage_)
{
pvcode = maskPage_->onEventButton(pvid);
}
}
return pvcode;
}

34
src/pv/MaskMain.h Normal file
View File

@@ -0,0 +1,34 @@
#pragma once
#include "pvapp.h"
static const std::string STYLE_BKG =
"border-width:1 1 1 1px;"
"border-style:outset solid;"
"border-color:rgba(180,180,180,255);"
"background-color:rgba(80,80,80,80);";
class MaskMain : public PvMask
{
public:
struct {
int bkg = PV_ID_NUL;
int widgetMenu = PV_ID_NUL;
int datetime = PV_ID_NUL;
} ui;
std::map<int, std::pair<std::string, EPvCode>> mapMenuInfo_;
EPvCode pvcode_;
std::shared_ptr<PvMask> maskPage_;
MaskMain(PARAM* p, EPvCode pvcode) : PvMask(p), pvcode_(pvcode) {}
int initUI();
std::shared_ptr<PvMask> createSubPage(int pvid);
EPvCode onEventNull(int pvid) override;
EPvCode onEventButton(int pvid) override;
};

226
src/pv/PvApp.cpp Normal file
View File

@@ -0,0 +1,226 @@
#include "PvApp.h"
#include "PvUser.h"
#include "TimeUtils.h"
#define FONT_NAME "微软雅黑"
void PvApp::setPvUser(PARAM* p, PvUser* pvuser)
{
p->user = pvuser;
}
PvUser* PvApp::getPvUser(PARAM* p)
{
return static_cast<PvUser*>(p->user);
}
int PvApp::pvid(PARAM* p)
{
auto pvuser = PvApp::getPvUser(p);
return (++pvuser->pvidIndex);
}
void PvApp::reset(PARAM* p)
{
// 清理event回调
auto pvuser = PvApp::getPvUser(p);
pvuser->mapEventCallback.clear();
pvuser->pvidIndex = 0;
}
EPvCode PvApp::getPvCode(std::string name)
{
static std::map<std::string, EPvCode> mapPvCode =
{
{"系统总览", EPvCode::MASK_HOME},
{"运行监控", EPvCode::MASK_RUNNING},
{"预测管理", EPvCode::MASK_FORECAST},
{"储能充放电预测", EPvCode::MASK_FORECAST_STORAGE},
{"光伏发电预测", EPvCode::MASK_FORECAST_SOLAR},
{"充电负荷预测", EPvCode::MASK_FORECAST_CHARGE},
{"统计分析", EPvCode::MASK_STAT},
{"储能设备运行分析", EPvCode::MASK_STAT_STORAGE},
{"光伏设备运行分析", EPvCode::MASK_STAT_SOLAR},
{"充电设备运行分析", EPvCode::MASK_STAT_CHARGE},
{"系统管理", EPvCode::MASK_SYSMGR},
{"用户管理", EPvCode::MASK_MGR_USER},
{"角色管理", EPvCode::MASK_MGR_ROLE},
{"权限管理", EPvCode::MASK_MGR_PERMISSION},
{"场站管理", EPvCode::MASK_MGR_STATION},
{"设备管理", EPvCode::MASK_MGR_DEVICE},
{"策略管理", EPvCode::MASK_MGR_POLICY},
{"系统日志", EPvCode::MASK_MGR_SYSLOG},
{"告警日志", EPvCode::MASK_MGR_ALERTLOG},
};
auto iter = mapPvCode.find(name);
if (iter != mapPvCode.end())
{
return iter->second;
}
else
{
return EPvCode::NUL;
}
}
void PvApp::bind(PARAM* p, PvEvent eid, int pvid, std::function<void(std::string text)> cb)
{
auto pvuser = PvApp::getPvUser(p);
if (pvuser)
{
std::string event = std::to_string((int)eid) + ":" + std::to_string((int)pvid);
pvuser->mapEventCallback[event] = cb;
}
}
void PvApp::dispatch(PARAM* p, PvEvent eid, int pvid, std::string text)
{
auto pvuser = PvApp::getPvUser(p);
if (pvuser)
{
std::string event = std::to_string((int)eid) + ":" + std::to_string((int)pvid);
auto iter = pvuser->mapEventCallback.find(event);
if (iter != pvuser->mapEventCallback.end())
{
(iter->second)(text);
}
}
}
int PvApp::widget(PARAM* p, int parent, int x, int y, int w, int h)
{
int id = PvApp::pvid(p);
pvQWidget(p, id, parent);
pvSetGeometry(p, id, x, y, w, h);
return id;
}
int PvApp::label(PARAM* p, int parent, int x, int y, int w, int h, std::string text, std::string qss)
{
int id = PvApp::pvid(p);
pvQLabel(p, id, parent);
pvSetGeometry(p, id, x, y, w, h);
pvSetFont(p, id, FONT_NAME, 14, 1, 0, 0, 0);
pvSetFontColor(p, id, 255, 255, 255);
if (!text.empty()) { pvSetText(p, id, text.c_str()); }
if (!qss.empty()) { pvSetStyleSheet(p, id, qss.c_str()); }
return id;
}
int PvApp::labelAlignCenter(PARAM* p, int parent, int x, int y, int w, int h, std::string text, std::string qss)
{
int id = PvApp::label(p, parent, x, y, w, h, text, qss);
pvSetAlignment(p, id, AlignCenter);
return id;
}
int PvApp::button(PARAM* p, int parent, int x, int y, int w, int h, std::string text, std::string qss)
{
int id = PvApp::pvid(p);
pvQPushButton(p, id, parent);
pvSetGeometry(p, id, x, y, w, h);
pvSetFont(p, id, FONT_NAME, 14, 1, 0, 0, 0);
pvSetFontColor(p, id, 255, 255, 255);
if (!text.empty()) { pvSetText(p, id, text.c_str()); }
if (!qss.empty()) { pvSetStyleSheet(p, id, qss.c_str()); }
return id;
}
int PvApp::image(PARAM* p, int parent, int x, int y, int w, int h, const char* filename)
{
int id = PvApp::pvid(p);
pvQImage(p, id, parent, filename);
pvSetGeometry(p, id, x, y, w, h);
//if (!qss.empty()) { pvSetStyleSheet(p, id, qss.c_str()); }
return id;
}
int PvApp::combox(PARAM* p, int parent, int x, int y, int w, int h, const std::vector<std::string>& vecItems)
{
int id = PvApp::pvid(p);
pvQComboBox(p, id, parent, 0, 0);
pvSetGeometry(p, id, x, y, w, h);
pvSetStyleSheet(p, id, QSS_COMBOX_14.c_str());
for (int i=0; i<vecItems.size(); ++i)
{
pvInsertItem(p, id, i, NULL, vecItems[i].c_str());
}
PvApp::image(p, id, w-h, 0, h, h, "downFill.png");
return id;
}
int PvApp::lineEdit(PARAM* p, int parent, int x, int y, int w, int h, std::string text, std::string qss)
{
int id = PvApp::pvid(p);
pvQLineEdit(p, id, parent);
pvSetGeometry(p, id, x, y, w, h);
pvSetStyleSheet(p, id, QSS_LINEEDIT.c_str());
if (!text.empty()) { pvSetText(p, id, text.c_str()); }
return id;
}
PvRect::PvRect()
{
}
PvRect::PvRect(int ix, int iy, int width, int height)
: x(ix), y(iy), w(width), h(height)
{
}
PvRect& PvRect::set(int ix, int iy, int width, int height)
{
x = ix;
y = iy;
w = width;
h = height;
return *this;
}
PvRect& PvRect::set_x(int ix)
{
x = ix;
return *this;
}
PvRect& PvRect::set_y(int iy)
{
y = iy;
return *this;
}
PvRect& PvRect::set_w(int iw)
{
w = iw;
return *this;
}
PvRect& PvRect::set_h(int ih)
{
h = ih;
return *this;
}
PvRect& PvRect::shift_x(int ix)
{
x += ix;
return *this;
}
PvRect& PvRect::shift_y(int iy)
{
y += iy;
return *this;
}
PvRect& PvRect::shift_w(int iw)
{
w += iw;
return *this;
}
PvRect& PvRect::shift_h(int ih)
{
h += ih;
return *this;
}

151
src/pv/PvApp.h Normal file
View File

@@ -0,0 +1,151 @@
#pragma once
#include "processviewserver.h"
#include "rleventlogserver.h"
#include "rltime.h"
#include "rlcutil.h"
#include "rlsvganimator.h"
#include <math.h>
#include <map>
#include <unordered_map>
#include <vector>
#include <functional>
#include "PvStyle.h"
#include "common/Logger.h"
// 主窗口的 id 是 0其它创建的控件都是主窗口的子控件在创建子控件时id 从 1 开始累加
#define PV_ID_MAIN 0
#define PV_ID_NUL -999
enum class EPvMaskID
{
NUL = 0,
LOGIN,
HOME,
Running,
SysManage,
Statistics,
};
enum class EPvCode
{
NUL = 0,
MASK_LOGIN,
MASK_HOME,
MASK_RUNNING,
MASK_FORECAST,
MASK_FORECAST_STORAGE,
MASK_FORECAST_SOLAR,
MASK_FORECAST_CHARGE,
MASK_STAT,
MASK_STAT_STORAGE,
MASK_STAT_SOLAR,
MASK_STAT_CHARGE,
MASK_SYSMGR,
MASK_MGR_USER,
MASK_MGR_ROLE,
MASK_MGR_PERMISSION,
MASK_MGR_STATION,
MASK_MGR_DEVICE,
MASK_MGR_POLICY,
MASK_MGR_SYSLOG,
MASK_MGR_ALERTLOG,
};
class PvObject
{
public:
PARAM* p = NULL;
int pvid = PV_ID_NUL;
PvObject(PARAM* p) : p(p) {}
void show(bool v) { if (p && pvid != PV_ID_NUL) { v ? pvShow(p, pvid) : pvHide(p, pvid); } }
virtual EPvCode onEventNull(int pvid) { return EPvCode::NUL; }
virtual EPvCode onEventButton(int pvid) { return EPvCode::NUL; }
virtual EPvCode onEventText() { return EPvCode::NUL; }
};
class PvMask : public PvObject
{
public:
PvMask(PARAM* p) : PvObject(p) {};
virtual int initUI(EPvCode pvcode) { return 0; };
};
class PvUser;
class PvApp
{
public:
static void setPvUser(PARAM* p, PvUser* pvuser);
static PvUser* getPvUser(PARAM* p);
static int pvid(PARAM* p);
static void reset(PARAM* p);
static EPvCode getPvCode(std::string name);
static void bind(PARAM* p, PvEvent eid, int pvid, std::function<void(std::string text)> cb);
static void dispatch(PARAM* p, PvEvent eid, int pvid, std::string text);
static int widget(PARAM* p, int parent, int x, int y, int w, int h);
static int label(PARAM* p, int parent, int x, int y, int w, int h, std::string text, std::string qss = "");
static int labelAlignCenter(PARAM* p, int parent, int x, int y, int w, int h, std::string text, std::string qss = "");
static int button(PARAM* p, int parent, int x, int y, int w, int h, std::string text, std::string qss = "");
static int image(PARAM* p, int parent, int x, int y, int w, int h, const char* filename);
static int combox(PARAM* p, int parent, int x, int y, int w, int h, const std::vector<std::string>& vecItems);
static int lineEdit(PARAM* p, int parent, int x, int y, int w, int h, std::string text, std::string qss = "");
};
///////////////////////////////////////////////////////////////////////////////////////////////////
/// === PvRect
class PvRect
{
public:
PvRect();
PvRect(int ix, int iy, int width, int height);
PvRect& set(int ix, int iy, int width, int height);
PvRect& set_x(int ix);
PvRect& set_y(int iy);
PvRect& set_w(int iw);
PvRect& set_h(int ih);
PvRect& shift_x(int ix);
PvRect& shift_y(int iy);
PvRect& shift_w(int iw);
PvRect& shift_h(int ih);
public:
int x = 0;
int y = 0;
int w = 100;
int h = 32;
};
struct PvColor
{
int r;
int g;
int b;
int a;
PvColor(int r, int g, int b, int a = 255) : r(r), g(g), b(b), a(a) {}
std::string rgb()
{
return "rgb(" + std::to_string(r) + "," + std::to_string(g) + "," + std::to_string(b) + ")";
}
std::string rgba()
{
return "rgb(" + std::to_string(r) + "," + std::to_string(g) + "," + std::to_string(b) + "," + std::to_string(a) + ")";
}
};

409
src/pv/PvChart.cpp Normal file
View File

@@ -0,0 +1,409 @@
#include "PvChart.h"
#include "TimeUtils.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
/// === PvChartBar
PvChartBar::PvChartBar(PARAM* p, int parentId, int x, int y, int w, int h)
: PvObject(p)
{
pvid_ = PvApp::label(p, parentId, x, y, w, h, "", "border: none; border-radius: 0px; background-color: transparent; color:white; font: normal 12px;");
boxBar_.x = 40;
boxBar_.y = 56;
boxBar_.w = w - boxBar_.x * 2;
boxBar_.h = h - boxBar_.y - 38;
boxBar_.id = PvApp::label(p, pvid_, boxBar_.x, boxBar_.y, boxBar_.w+1, boxBar_.h+1, "", "border: none; background-color: transparent;");
int hMargin = 5;
{
// 左侧X轴
yAxisLeft_.widget = PvApp::label(p, pvid_, 0, 0, boxBar_.x, h, "", "");
PvApp::label(p, yAxisLeft_.widget, boxBar_.x-1, boxBar_.y, 1, boxBar_.h, "", "border: 1px solid white;");
}
{
yAxisRight_.widget = PvApp::label(p, pvid_, boxBar_.x + boxBar_.w, 0, boxBar_.x, h, "", "");
PvApp::label(p, yAxisRight_.widget, 0, boxBar_.y, 1, boxBar_.h, "", "border: 1px solid white;");
}
yLabelLeft_ = PvApp::label(p, pvid_, 0, 0, boxBar_.x*2, boxBar_.y-10, "", "font: bold 14px; color: white;");
pvSetAlignment(p, yLabelLeft_, AlignHCenter | AlignBottom);
yLabelRight_ = PvApp::label(p, pvid_, boxBar_.w, 0, boxBar_.x*2, boxBar_.y-5, "", "font: bold 14px; color: white;");
pvSetAlignment(p, yLabelRight_, AlignHCenter | AlignBottom);
// Y轴分成5段
setTickX(7);
setTickY(5);
}
void PvChartBar::setTickX(int n)
{
vecTickX_.resize(n);
// 每一段的长度:
int len = int(float(boxBar_.w)/float(n));
int y = boxBar_.y + boxBar_.h;
// X轴分成 n 段,共有 n 条刻度线
for (int i = 0; i<n; ++i)
{
int x = boxBar_.x + float(boxBar_.w) * float(i)/float(n);
PvApp::label(p, pvid_, x+len*0.5, y+5, 1, 10, "", "border: 1px solid white;");
int id = PvApp::label(p, pvid_, x, y+12, len, 28, std::to_string(n-i+1), "font: normal 12px;");
pvSetAlignment(p, id, AlignCenter);
vecTickX_[i] = id;
}
PvApp::label(p, pvid_, boxBar_.x, y+5, boxBar_.w, 1, "", "border: 1px solid white;");
}
void PvChartBar::setTickY(int n)
{
yAxisLeft_.vecTick_.resize(n+1);
yAxisRight_.vecTick_.resize(n+1);
// 每一段的长度:
int len = int(float(boxBar_.h)/float(n));
// 刻度文字label的高度
int hLabel = 20;
// 刻度线的长度
int wLine = 8;
// Y轴分成 n 段,共有 n+1 条刻度线
for (int i = 0; i<=n; ++i)
{
// 刻度线的位置
int y = boxBar_.y + float(boxBar_.h) * float(n-i)/float(n);
{
// 左侧刻度线
PvApp::label(p, yAxisLeft_.widget, boxBar_.x-wLine, y, wLine, 1, "", "border: 1px solid white;");
// 左侧刻度text
int id = PvApp::label(p, yAxisLeft_.widget, 0, y-hLabel*0.5, boxBar_.x-wLine-3, hLabel, std::to_string(i), "");
pvSetAlignment(p, id, AlignRight | AlignVCenter);
yAxisLeft_.vecTick_[i] = id;
}
{
// 右侧刻度线
PvApp::label(p, yAxisRight_.widget, 0, y, wLine, 1, "", "border: 1px solid white;");
// 右侧刻度text
int id = PvApp::label(p, yAxisRight_.widget, 8+3, y-hLabel*0.5, boxBar_.x-wLine-3, hLabel, std::to_string(i), "");
pvSetAlignment(p, id, AlignLeft | AlignVCenter);
yAxisRight_.vecTick_[i] = id;
}
// Y轴的网格线
PvApp::label(p, pvid_, boxBar_.x, y, boxBar_.w, 1, "", "border: none; border-top: 1px dashed white; background-color: rgb(80,80,80,0); color:white;");
}
setRangeY(0, n);
}
void PvChartBar::setRangeY(double vmin, double vmax)
{
int n = yAxisLeft_.vecTick_.size();
int step = std::ceil((vmax - vmin)/double(n-1));
yLeftMin = vmin;
yLeftMax = vmin + step * (n-1);
for (int i = 0; i<n; ++i)
{
pvSetText(p, yAxisLeft_.vecTick_[i], std::to_string(int(yLeftMin + i* step)).c_str());
}
}
void PvChartBar::setLabelYLeft(std::string name)
{
if (yLabelLeft_ != PV_ID_NUL) { pvSetText(p, yLabelLeft_, name.c_str()); }
}
void PvChartBar::setLabelYRight(std::string name)
{
if (yLabelRight_ != PV_ID_NUL) { pvSetText(p, yLabelRight_, name.c_str()); }
}
static std::vector<std::string> vecSeriesColor =
{
"rgb(84, 112, 198);", "rgb(169, 223, 150);", "rgb(246, 220, 125);", "rgb(84, 112, 198);", "rgb(169, 223, 150);", "rgb(246, 220, 125);"
};
void PvChartBar::insertItem(std::string text)
{
int index = vecItems_.size();
int nItem = index+1;
// 根据 X 轴的分段数量创建 bar
int n = vecTickX_.size();
// 每一段的绘制区域的长度,左右保留间距 5px, 计算每一个柱体的宽度
int wBar = (int(float(boxBar_.w)/float(n)) - margin) / nItem;
// 如果不是第一个序列项,需要重新计算前面序列的宽度
for (int i = 0; i<index; ++i)
{
auto& bar = vecItems_[i].bar_;
for (int pos = 0; pos<bar.size(); ++pos)
{
int h = 0; // bar 初始高度设置为0
int x = boxBar_.x + float(boxBar_.w) * float(pos)/float(n) + margin + wBar*i;
int y = boxBar_.y + boxBar_.h -h;
pvSetGeometry(p, bar[pos], x, y, wBar-5, h);
}
}
BarItem barItem;
barItem.name = text;
int labelWidth = text.size()*12*0.4 + 18;
barItem.idItem = PvApp::label(p, pvid_, itemLabelPos, 10, labelWidth, 18, text, "border: none; font: normal 12px; border-left: 18px solid " + vecSeriesColor[index]);
itemLabelPos += (labelWidth + 10);
barItem.bar_.resize(n);
for (int pos = 0; pos<n; ++pos)
{
int h = 0; // bar 初始高度设置为0
int x = boxBar_.x + float(boxBar_.w) * float(pos)/float(n) + margin + wBar*index;
int y = boxBar_.y + boxBar_.h -h;
barItem.bar_[pos] = PvApp::label(p, pvid_, x, y, wBar-5, h, "", "border:0px; background-color: " + vecSeriesColor[index]);
}
vecItems_.push_back(barItem);
}
void PvChartBar::updateItem(int index, std::vector<std::pair<std::string, double>> vd)
{
int nItems = vecItems_.size();
if (index >= nItems)
{
return;
}
double vmax = yLeftMax;
// 计算Y轴的范围 获取数据中的最大值 先不考虑最小值最小值使用0
for (int i = 0; i<vd.size(); ++i)
{
auto& val = vd[i].second;
if (val > vmax) { vmax = val; }
}
setRangeY(0, vmax);
// 计算和重绘 bar
// Y轴差值
double delta = double(yLeftMax - yLeftMin);
auto& bar = vecItems_[index].bar_;
// 根据 X 轴的分段数量创建 bar
int n = vecTickX_.size();
// 每一段的绘制区域的长度,左右保留间距 5px, 计算每一个柱体的宽度
int wBar = (int(float(boxBar_.w)/float(n)) - margin) / vecItems_.size();
for (int pos = 0; pos<bar.size(); ++pos)
{
if (pos < vd.size())
{
double v = vd[pos].second;
int h = double(boxBar_.h)*v/delta;
int x = boxBar_.x + float(boxBar_.w) * float(pos)/float(n) + margin + wBar*index;
int y = boxBar_.y + boxBar_.h -h;
pvSetGeometry(p, bar[pos], x, y, wBar-5, h);
// 设置X轴
pvSetText(p, vecTickX_[pos], vd[pos].first.c_str());
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
/// === PvChartCurve
PvChartCurve::PvChartCurve(PARAM* p, int parent, int x, int y, int w, int h) : PvObject(p)
{
rect_.set(x, y, w, h);
pvid_ = PvApp::label(p, parent, x, y, w, h, "", "border: none; border-radius: 0px; background-color: transparent;");
plotId_ = PvApp::pvid(p);
pvQwtPlotWidget(p, plotId_, pvid_, 100, 100);
pvSetGeometry(p, plotId_, 10, 50, w - 20, h - 58);
pvSetFont(p, plotId_, "微软雅黑", 16, 0, 0, 0, 0);
// 设置标题
//qpwSetTitle(p,id_,"30日曲线");
// 设置背景色
qpwSetCanvasBackground(p, plotId_, 64, 102, 124);
// 设置横轴网格线 // NoPen, SolidLine, DashLine, DotLine, DashDotLine, DashDotDotLine, MPenStyle
qpwSetGridMajPen(p, plotId_, 175, 196, 211, DotLine);
qpwEnableOutline(p, plotId_, 1);
qpwSetOutlinePen(p, plotId_, 255, 0, 0);
// 设置坐标轴位置:左侧,底部 // yLeft,yRight,xBottom,xTop,axisCnt
//qpwEnableAxis(p, plot_id_, yLeft);
//qpwSetAxisTitle(p, plot_id_, yLeft, "Y轴说明");
//qpwEnableAxis(p, plot_id_, yRight);
//qpwSetAxisTitle(p, plot_id_, yRight, "Y轴说明");
qpwEnableAxis(p, plotId_, xBottom);
//qpwSetAxisTitle(p, id_, xBottom, "X轴说明");
//
qpwSetAxisScale(p, plotId_, xBottom, 0, 15 * 60 * 7, 15 * 60);
qpwSetAxisScale(p, plotId_, yLeft, 0, 100, 20);
// 不知道效果
qpwSetAxisOptions(p, plotId_, xBottom, Inverted); // pvNone = 0,IncludeRef = 1,Symmetric = 2,Floating = 4,Logarithmic = 8,InvertedInverted = 16
//this->mark(p, 0, 10, 100);
//this->mark(p, 1, 11, 100);
yLabelLeft = PvApp::label(p, pvid_, 10, 24, 100, 20, "", "border:none;color:white;font-size:14px;font-weight:bold;");
pvSetAlignment(p, yLabelLeft, AlignLeft);
yLabelRight = PvApp::label(p, pvid_, rect_.w - 100 - 10, 24, 100, 20, "", "border:none;color:white;font-size:14px;font-weight:bold;");
pvSetAlignment(p, yLabelRight, AlignRight);
}
void PvChartCurve::setBackground(PARAM* p, int r, int g, int b)
{
qpwSetCanvasBackground(p, plotId_, r, g, b);
std::string qss = "border: none; border-radius: 0px; background-color: " + PvColor(r, g, b).rgb() + ";";
pvSetStyleSheet(p, pvid_, qss.c_str());
}
void PvChartCurve::setLabelYLeft(std::string title, float min, float max, float step)
{
yLeftMin_ = min;
yLeftMax_ = max;
yLeftStep_ = step;
qpwSetAxisScale(p, plotId_, yLeft, min, max, step);
qpwEnableAxis(p, plotId_, yLeft);
//qpwSetAxisTitle(p, id_, yLeft, "Y轴说明");
pvSetText(p, yLabelLeft, title.c_str());
}
void PvChartCurve::setLabelYRight(std::string title, float min, float max, float step)
{
yRightMin_ = min;
yRightMax_ = max;
yRightStep_ = step;
qpwSetAxisScale(p, plotId_, yRight, min, max, step);
qpwEnableAxis(p, plotId_, yRight);
//qpwSetAxisTitle(p, id_, yRight, "Y轴说明");
pvSetText(p, yLabelRight, title.c_str());
}
void PvChartCurve::setXBottom(float min, float max, float step)
{
qpwSetAxisScale(p, plotId_, xBottom, min, max, step);
qpwReplot(p, plotId_);
}
void PvChartCurve::setXBottomFormat(std::string fmt)
{
// 设置时间轴 "yyyy-MM-dd hh:mm:ss"
qpwSetAxisScaleDraw(p, plotId_, xBottom, fmt.c_str());
}
static std::vector<PvColor> g_mapSeriesColor =
{
PvColor(84, 112, 198), PvColor(169, 223, 150), PvColor(246, 220, 125), PvColor(84, 112, 198), PvColor(169, 223, 150), PvColor(246, 220, 125)
};
int PvChartCurve::insertItem(int pos, std::string name, int x, int y, int w)
{
int index = vecItems_.size();
auto& color = g_mapSeriesColor[index];
PvApp::label(p, pvid_, x, y + 3, 14, 14, "", "background-color:" + color.rgb() + ";border-radius:7px;");
PvApp::label(p, pvid_, x + 16, y, w - 16, 18, name, "color:white;font:normal 12px;border:0px solid #ff0000;");
// 添加数据曲线
qpwInsertCurve(p, plotId_, index, name.c_str());
// 设置曲线颜色和类型 // NoPen,SolidLine,DashLine,DotLine,DashDotLine,DashDotDotLine,MPenStyle = 0x0f
qpwSetCurvePen(p, plotId_, index, color.r, color.g, color.b, 2, SolidLine);
//qpwSetCurveSymbol(p_, plot_id_, index, MarkerNone, col.r, col.g, col.b, WHITE, 4, 4);
// 设置数据对应的Y轴LEFT或RIGHT
qpwSetCurveYAxis(p, plotId_, index, (pos == 0) ? yLeft : yRight);
CurveItem item;
item.name = name;
item.pos = pos;
vecItems_.push_back(item);
return index;
}
void PvChartCurve::updateItem(int index, const std::vector<std::pair<double, double>>& data)
{
if (index >= vecItems_.size())
{
return;
}
auto& item = vecItems_[index];
int64_t tCurrent = TimeUtils::now();
int yMax = 0;
int size = (int)data.size();
std::vector<double> vx(size, 0);
std::vector<double> vy(size, 0);
for (size_t i = 0; i < size; ++i)
{
vx[i] = data[i].first;
vy[i] = data[i].second;
if (yMax < data[i].second) { yMax = data[i].second; }
}
this->reviseYMax(item.pos, yMax);
qpwSetCurveData(p, plotId_, index, size, &vx[0], &vy[0]);
qpwReplot(p, plotId_);
}
int64_t PvChartCurve::set_range_x_days(int n)
{
this->setXBottomFormat("MM/dd");
int step = 3600 * 24;
auto t_end = TimeUtils::datetime2ts(TimeUtils::now_date() + " 08:00:00");
auto t_start = t_end - step * (n - 1);
this->setXBottom(t_start, t_end, step);
return t_start;
}
int64_t PvChartCurve::set_range_x_day_by_hour(int n)
{
this->setXBottomFormat("hh:mm");
auto t_start = TimeUtils::datetime2ts(TimeUtils::now_date() + " 00:00:00");
auto t_end = t_start + 86400;
this->setXBottom(t_start, t_end, 86400 / n);
return t_start;
}
void PvChartCurve::reviseYMax(int pos, double v)
{
bool isLeft = (pos == 0);
int min = (isLeft ? yLeftMin_ : yRightMin_);
int max = (isLeft ? yLeftMax_ : yRightMax_);
int step = (isLeft ? yLeftStep_ : yRightStep_);
while (v > max)
{
max += (max-min);
step *= 2;
}
qpwSetAxisScale(p, plotId_, yLeft, min, max, step);
qpwSetAxisScale(p, plotId_, yRight, min, max, step);
}
void PvChartCurve::update_days_test(int n)
{
int step = 3600*24/n;
auto t_start = TimeUtils::datetime2ts(TimeUtils::now_date() + " 00:00:00");
auto t_end = t_start+3600*24;
std::vector<std::pair<double, double>> vd(n);
double v = 100;
for (int index = 0; index < vecItems_.size(); ++index)
{
for (int i = 0; i < n; ++i)
{
vd[i].first = static_cast<double>(t_start + i * step);
vd[i].second = v += 2;// (v += (Utils::random(0, 10)-5));
};
this->updateItem(index, vd);
}
}
void PvChartCurve::mark(PARAM* p, int index, float x, float y, std::string text)
{
qpwInsertMarker(p, plotId_, 0);
qpwSetMarkerPos(p, plotId_, index, x, y);
//MarkerNone,MarkerEllipse,MarkerRect,MarkerDiamond,MarkerTriangle,MarkerDTriangle,MarkerUTriangle,MarkerLTriangle,MarkerRTriangle,MarkerCross,MarkerXCross,MarkerStyleCnt
qpwSetMarkerSymbol(p, plotId_, 0, MarkerEllipse, RED, GREEN, 10, 10);
qpwSetMarkerFont(p, plotId_, 0, "微软雅黑", 14, 1);
qpwSetMarkerLabel(p, plotId_, 0, text.c_str());
}

119
src/pv/PvChart.h Normal file
View File

@@ -0,0 +1,119 @@
#include "PvApp.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
/// === PvChartBar
class PvChartBar : public PvObject
{
public:
static std::shared_ptr<PvChartBar> create(PARAM* p, int parentId, int x, int y, int w, int h)
{
return std::make_shared<PvChartBar>(p, parentId, x, y, w, h);
}
PvChartBar(PARAM* p, int parentId, int x, int y, int w, int h);
void setTickX(int n);
void setTickY(int n);
void insertItem(std::string text);
void updateItem(int index, std::vector<std::pair<std::string, double>> vd);
void setRangeY(double vmin, double vmax);
void setLabelYLeft(std::string name);
void setLabelYRight(std::string name);
int pvid_;
struct {
int x;
int y;
int w;
int h;
int id;
} boxBar_;
struct AxisTick {
int widget = PV_ID_NUL;
std::vector<int> vecTick_;
};
AxisTick yAxisLeft_;
AxisTick yAxisRight_;
AxisTick xAxis_;
int yLabelLeft_ = PV_ID_NUL;
int yLabelRight_ = PV_ID_NUL;
std::vector<int> vecTickX_;
struct BarItem {
std::string name;
int idItem;
std::vector<int> bar_;
};
std::vector<BarItem> vecItems_;
int itemLabelPos = 100;
int yLeftMin = 0;
int yLeftMax = 0;
int yMinRight = 0;
int yMaxRight = 0;
// 绘制区域的间距、柱体的间距
int margin = 5;
};
//****************************************************************************************************
// 曲线图
//
class PvChartCurve : public PvObject
{
public:
PvChartCurve(PARAM* p, int parent, int x, int y, int w, int h);
void setBackground(PARAM* p, int r, int g, int b);
void setLabelYLeft(std::string name, float min = 0, float max = 100, float step = 20);
void setLabelYRight(std::string name, float min = 0, float max = 100, float step = 20);
void setXBottom(float min, float max, float step);
void setXBottomFormat(std::string fmt);
int insertItem(int pos, std::string name, int x, int y, int w);
void updateItem(int index, const std::vector<std::pair<double, double>>& data);
int64_t set_range_x_days(int n);
int64_t set_range_x_day_by_hour(int n);
void update_days_test(int n);
void mark(PARAM* p, int index, float x, float y, std::string text);
void reviseYMax(int pos, double v);
private:
PvRect rect_;
int pvid_;
int plotId_;
int start_num_ = 0;
int labTimeLine_;
std::vector<std::pair<int, int>> vecTimeAxisId_;
struct CurveItem
{
std::string name;
int pos;
};
std::vector<CurveItem> vecItems_;
int yLabelLeft = PV_ID_NUL;
int yLabelRight = PV_ID_NUL;
int yLeftMin_ = 0;
int yLeftMax_ = 0;
int yLeftStep_ = 0;
int yRightMin_ = 0;
int yRightMax_ = 0;
int yRightStep_ = 0;
};

113
src/pv/PvPopWidget.cpp Normal file
View File

@@ -0,0 +1,113 @@
#include "PvPopWidget.h"
PvPopWidget::PvPopWidget(PARAM* p, int width, int height) : PvObject(p), width(width), height(height)
{
pvid = PvApp::widget(p, PV_ID_MAIN, 0, 0, 1920, 1080);
PvApp::label(p, pvid, 0, 0, 1920, 1080, "", "background-color: rgba(30,30,30,180);");
int x = (1920-width) *0.5;
int y = (1080-height) *0.5;
ui.widget = PvApp::widget(p, pvid, x, y, width, height);
PvApp::label(p, ui.widget, 2, 2, width-4, height-4, "", "background-color: rgb(12,39,58); border: 1px solid rgb(31,145,156);");
PvApp::label(p, ui.widget, 0, 0, 60, height, "", "background-color: transparent; border: 0 solid rgb(42, 149, 245); border-width: 5px 0 5px 5px;");
PvApp::label(p, ui.widget, width-60, 0, 60, height, "", "background-color: transparent; border: 0 solid rgb(42, 149, 245); border-width: 5px 5px 5px 0;");
ui.title = PvApp::label(p, ui.widget, 20, 10, width-20, 30, "", "font: bold 20px;");
PvApp::label(p, ui.widget, 20, 40, width*0.5-20, 3, "", QSS_UNDERLINE);
{
int w = 100, h = 40, offset = 50;
int x = (width- w*2 - offset) *0.5;
int y = height - h - 40;
int btnOk = PvApp::button(p, ui.widget, x, y, w, h, "确定", QSS_BTN_CONFIRM);
PvApp::bind(p, PvEvent::BUTTON_EVENT, btnOk, [=](std::string) {
this->show(false);
if (callbackConfirm) { callbackConfirm(); }
});
int btnCancel = PvApp::button(p, ui.widget, x+w+offset, y, w, h, "取消", QSS_BTN_CANCEL);
PvApp::bind(p, PvEvent::BUTTON_EVENT, btnCancel, [=](std::string) {
this->show(false);
});
}
}
void PvPopWidget::addParamLineEdit(std::string key, std::string title, int x, int y, bool editable/*= true*/)
{
auto line = std::make_shared<ParamLine>("lineEdit", key);
PvApp::label(p, ui.widget, x, y, lineKeyWidth, lineHeight, title, "font: bold 14px;");
line->widget = PvApp::lineEdit(p, ui.widget, x+lineKeyWidth, y, lineValWidth, lineHeight, "");
if (!editable) { pvSetEnabled(p, line->widget, 0); }
mapLines[key] = line;
PvApp::bind(p, PvEvent::TEXT_EVENT, line->widget, [=](std::string text) {
line->val = text;
});
}
void PvPopWidget::addParamCombox(std::string key, std::string title, int x, int y, std::vector<std::string> items)
{
auto line = std::make_shared<ParamLine>("combox", key);
PvApp::label(p, ui.widget, x, y, lineKeyWidth, lineHeight, title, "font: bold 14px;");
line->widget = PvApp::combox(p, ui.widget, x+lineKeyWidth, y, lineValWidth, lineHeight, items);
mapLines[key] = line;
PvApp::bind(p, PvEvent::TEXT_EVENT, line->widget, [=](std::string text) {
line->val = text;
});
}
void PvPopWidget::setParamText(std::shared_ptr<ParamLine> line, std::string text)
{
if (line->type == "lineEdit")
{
pvSetText(p, line->widget, text.c_str());
}
else if (line->type == "combox")
{
int index = 0;
for (int i = 0; i<line->items.size(); ++i)
{
if (line->items[i] == text) { index = i; break; }
}
pvSetCurrentItem(p, line->widget, index);
}
}
void PvPopWidget::setParamText(std::string key, std::string text)
{
auto iter = mapLines.find(key);
if (iter != mapLines.end())
{
this->setParamText(iter->second, text);
}
}
void PvPopWidget::setTitle(std::string title)
{
pvSetText(p, ui.title, title.c_str());
}
void PvPopWidget::setData(DataFields fields)
{
for (auto iter = mapLines.begin(); iter != mapLines.end(); ++iter)
{
auto& line = iter->second;
this->setParamText(line, fields.getStr(line->key));
}
}
DataFields PvPopWidget::getData()
{
DataFields fields;
for (auto iter = mapLines.begin(); iter!=mapLines.end(); ++iter)
{
auto& line = iter->second;
fields.set(line->key, line->val);
}
return fields;
}
void PvPopWidget::setLineGeometry(int wKey, int wVal, int h)
{
lineKeyWidth = wKey;
lineValWidth = wVal;
lineHeight = h;
}

53
src/pv/PvPopWidget.h Normal file
View File

@@ -0,0 +1,53 @@
#pragma once
#include "PvApp.h"
#include "DataFields.h"
class PvPopWidget : public PvObject
{
public:
struct ParamLine
{
std::string key;
std::string type;
std::string val;
int widget;
std::vector<std::string> items;
ParamLine(std::string type, std::string key) : type(type), key(key) {}
};
PvPopWidget(PARAM* p, int width, int height);
void addParamLineEdit(std::string key, std::string title, int x, int y, bool editable=true);
void addParamCombox(std::string key, std::string title, int x, int y, std::vector<std::string> items);
void setParamText(std::shared_ptr<ParamLine> line, std::string text);
void setParamText(std::string key, std::string text);
void setCallbackConfirm(std::function<void()> callback) { callbackConfirm = callback; };
void setTitle(std::string title);
void setData(DataFields fields);
DataFields getData();
void setLineGeometry(int wKey, int wVal, int h);
int width {800};
int height {600};
int lineKeyWidth {70};
int lineValWidth {180};
int lineHeight {30};
struct {
int widget;
int title;
} ui;
std::map<std::string, std::shared_ptr<ParamLine>> mapLines;
DataFields data;
std::function<void()> callbackConfirm = nullptr;
};

136
src/pv/PvStyle.h Normal file
View File

@@ -0,0 +1,136 @@
#pragma once
#include <string>
static std::string QSS_BTN_CONFIRM =
"QPushButton { background-color:rgb(28, 145, 138); border-radius:10px; border: none; color:white; font:bold 18px \"Microsoft YaHei\";}"
"QPushButton:hover { background-color:rgb(10,125,215);border:2px solid rgb(1,239,255);color:rgb(1,239,255)}"
"QPushButton:pressed { border-width:3px 0 0 3px;background-color:rgb(150,150,150);border-style:inset;}"
"QPushButton:disabled { color:rgb(150,150,150);}";
static std::string QSS_BTN_CANCEL =
"QPushButton { background-color:rgb(200, 200, 200);border-radius:10px;border:0px solid rgb(10,120,215);color:white;font:bold 18px \"Microsoft YaHei\";}"
"QPushButton:hover { background-color:rgb(10,125,215);border:2px solid rgb(1,239,255);color:rgb(1,239,255)}"
"QPushButton:pressed { border-width:3px 0 0 3px;background-color:rgb(150,150,150);border-style:inset;}"
"QPushButton:disabled { color:rgb(150,150,150);}";
static std::string QSS_COMBOX =
"QComboBox {border: 1px solid rgb(18, 251, 255); background-color: rgb(5, 47, 77); border-radius: 5px; color:white; font: bold 16px;}"
"QComboBox QAbstractItemView { border: 1px solid gray; background-color: rgba(8, 54, 91); border-radius: 5px;}"
"QComboBox::drop-down { border-radius: 5px; width: 30px; }"
"QComboBox:disabled { color:rgb(150,150,150);}";
static std::string QSS_COMBOX_14 =
"QComboBox {border: 1px solid rgb(18, 251, 255); background-color: rgb(5, 47, 77); border-radius: 5px; color:white; font: bold 14px;}"
"QComboBox QAbstractItemView { border: 1px solid gray; background-color: rgba(8, 54, 91); border-radius: 5px;}"
"QComboBox::drop-down { border-radius: 5px; width: 30px; }"
"QComboBox:disabled { color:rgb(150,150,150);}";
static std::string QSS_LINEEDIT =
"QLineEdit { background-color: rgb(12, 39, 58); border: 1px solid rgb(18, 251, 255); border-radius: 5px; color:white; font: bold 14px;}"
"QLineEdit:disabled { border: 1px solid gray; color:rgb(150,150,150);}";
static std::string STYLE_BTN =
"QPushButton { background-color:rgb(4, 96, 142);border-radius:10px;border:0px solid rgb(10,120,215);color:white;font:bold 18px \"Microsoft YaHei\";}"
"QPushButton:hover { background-color:rgb(10,125,215);border:2px solid rgb(1,239,255);color:rgb(1,239,255)}"
"QPushButton:pressed { border-width:3px 0 0 3px;background-color:rgb(150,150,150);border-style:inset;}"
"QPushButton:disabled { color:rgb(150,150,150);}";
static std::string STYLE_BTN_ACTIVE =
"QPushButton { background-color:rgb(4, 96, 142);border-radius:10px;border:2px solid rgb(1,239,255);color:rgb(1,239,255);font:bold 18px \"Microsoft YaHei\";}"
"QPushButton:hover { background-color:rgb(10,125,215);border:2px solid rgb(1,239,255);color:rgb(1,239,255)}"
"QPushButton:pressed { border-width:3px 0 0 3px;background-color:rgb(150,150,150);border-style:inset;}"
"QPushButton:disabled { color:rgb(150,150,150);}";
static std::string QSS_LABEL_BKG_1 =
"QLabel { background-color:rgb(5, 47, 77); border:none; border-radius:5px; }"
"QLabel:disabled { color:rgb(150,150,150);}";
static std::string QSS_LABEL_BKG_2 =
"QLabel { background-color:rgb(8, 54, 91); border:none; border-radius:5px; }"
"QLabel:disabled { color:rgb(150,150,150);}";
static std::string QSS_BOX =
"QLabel { background-color:rgba(200,200,200,20); border:0px solid;border-color:rgb(5,255,255);border-radius:2px;font:bold 16px;color:white; }"
"QLabel:hover {border: 1px solid rgb(1, 183, 209);}"
"QLabel:disabled { color:rgb(150,150,150);}";
static std::string QSS_BOX_ACTIVE =
"QLabel { background-color:rgb(7, 72, 111); border:2px solid;border-color:rgb(1, 183, 209); border-radius:2px;font:bold 16px;color:white; }"
"QLabel:hover {border: 1px solid rgb(1, 183, 209);}"
"QLabel:disabled { color:rgb(150,150,150);}";
static std::string QSS_TITLE =
"QLabel { background:transparent; color: rgb(99, 196, 216); font: bold 16px; border: none; padding-top: 0px;}"
"QLabel:disabled { color:rgb(150,150,150);}";
static std::string STYLE_TITLE_ICON =
"padding-top: 0px;"
"background-color: qlineargradient(x1:0, y1:1, x2:0, y2:0, stop:0 rgba(0, 71, 105, 255),stop:1 rgba(0, 71, 105, 0));"
"border-radius: 0px; color:white; font: bold 16px; border-left: 8px solid rgba(33,255,210);";
static std::string QSS_UNDERLINE =
"background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0,stop:0 rgba(9,194,207,200),stop:1 rgba(9,194,207,0));";
const std::string QSS_BTN_MGR =
"QPushButton { background-color:rgb(10, 34, 63); border-radius:5px; border:1px solid rgb(33, 105, 195); color:white; font:bold 18px \"Microsoft YaHei\";}"
"QPushButton:hover { background-color:rgb(10,125,215); border:2px solid rgb(1,239,255); color:rgb(1,239,255)}"
//"QPushButton:pressed{border-width:3px 0 0 3px;background-color:rgb(150,150,150);border-style:inset;}"
"QPushButton:pressed { border-width:3px 0 0 3px;border-style:inset; }"
"QPushButton:disabled{color:rgb(150,150,150);}";
const std::string QSS_BTN_MGR_ACTIVE =
"QPushButton { background-color:rgb(33, 105, 195);border-radius:5px; border:1px solid rgb(68, 167, 252);color:white;font:bold 18px \"Microsoft YaHei\"; }"
"QPushButton:hover { background-color:rgb(10,125,215); border:2px solid rgb(1,239,255); color:rgb(1,239,255)}"
"QPushButton:pressed { border-width:3px 0 0 3px;border-style:inset; }"
"QPushButton:disabled { color:rgb(150,150,150); }";
//"background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 transparent, stop:%1 red, stop:1 blue);"
///////////////////////////////////////////////////////////////////////////////////////////////////
/// === 表格
static const std::string QSS_TABLE =
"border: 1px solid rgb(28, 121, 122); background-color:rgb(7, 46, 74);";
// 表头
static const std::string QSS_TABLE_HEAD =
"background-color: rgb(18, 93, 113); color:rgb(255, 255, 255); font:bold 16px;"
"border-width:1 1 1 1px; border-style:inset solid; border-color:rgb(120, 120, 120);";
// 单元格
static const std::string QSS_TABLE_CELL =
"background-color:transparent; color:rgb(255,255,255); font:bold 15px;padding-left:1;"
"border-width:0 0 0 0px; border-style:inset solid; border-color:rgba(180,180,180,200);";
static const std::string QSS_TABLE_BTN_VIEW =
"QPushButton { background-color: rgb(28, 145, 138); color:white; border-radius:2px; border:none; font:bold 14px;}"
"QPushButton:hover { border: 1px solid white;}"
"QPushButton:pressed { border-width:3px 0 0 3px;border-style:inset;}"
"QPushButton:disabled { color:rgb(150,150,150);}";
const std::string BTN_NEW = // 78, 149, 143
"QPushButton{background-color:rgb(38,233,233);color:white;border-radius:5px;border:2px solid rgb(10,120,215);font:bold 18px;}"
"QPushButton:hover{background-color:rgb(10,125,215);}"
"QPushButton:pressed{border-width:3px 0 0 3px;background-color:rgb(150,150,150);border-style:inset;}"
"QPushButton:disabled{color:rgb(150,150,150);}";
const std::string BTN_EDIT =
"QPushButton{background-color:rgb(248,147,45);color:white;border-radius:5px;border:2px solid rgb(10,120,215);font:bold 15px;}"
"QPushButton:hover{background-color:rgb(10,125,215);}"
"QPushButton:pressed{border-width:3px 0 0 3px;background-color:rgb(150,150,150);border-style:inset;}"
"QPushButton:disabled{color:rgb(150,150,150);}";
const std::string BTN_DELETE =
"QPushButton{background-color:rgb(252,83,83);color:white;border-radius:5px;border:2px solid rgb(10,120,215);font:bold 15px;}"
"QPushButton:hover{background-color:rgb(10,125,215);}"
"QPushButton:pressed{border-width:3px 0 0 3px;background-color:rgb(150,150,150);border-style:inset;}"
"QPushButton:disabled{color:rgb(150,150,150);}";
// 表格行的斑马色0
static const std::string QSS_TABLE_ROW_0 =
"background-color:rgb(7, 46, 74); border-width:0 0 1 0px; border-style:inset solid; border-color:rgba(120,120,120, 100);";
// 表格行的斑马色1
static const std::string QSS_TABLE_ROW_1 =
"background-color:rgb(7, 46, 74); border-width:0 0 1 0px; border-style:inset solid; border-color:rgba(120,120,120, 100);";
// 表格行的高亮显示
static const std::string QSS_TABLE_ROW_HIGHLIGHT =
"background-color:rgba(14,45,60,200);border:1px solid rgba(255,0,0,100);";

476
src/pv/PvTable.cpp Normal file
View File

@@ -0,0 +1,476 @@
#include "PvTable.h"
#include "PvStyle.h"
static const string STYLE_BKG =
"border-width:1 1 1 1px; border-style:outset solid; border-color:rgba(180,180,180,255);"
"background-color:rgba(80,80,80,0);";
//*********************************************************************************************************************
// PvTable
PvTable::PvTable(PARAM* p, int parent, int x, int y, int w, int row, Options& opts)
//: PvWidget(p, parent, PvRect(x, y, w, opts.item_height* row + (opts.show_header ? (opts.header_height) : 0))),
: PvObject(p), option_(opts), nRow_(row), nCol_(0)
{
// 计算表格的显示区域
int h = opts.row_height* row + (opts.show_header ? (opts.head_height) : 0);
rect_.set(x, y, w, h);
// 表格的主窗体QWidget设置样式无效
pvid_ = PvApp::widget(p, parent, x, y, w, h+1);
// 表格的背景色和边框样式
PvApp::label(p, pvid_, 0, 0, w, h+1, "", QSS_TABLE);
vecHead_.resize(0);
vecRows_.resize(nRow_);
vecData_.resize(nRow_);
// 创建行高亮显示背景
for (int row = 0; row < nRow_; row++)
{
int y = item_posy(row);
string qss = (row % 2 != 0) ? QSS_TABLE_ROW_0 : QSS_TABLE_ROW_1;
int rowBkg = PvApp::label(p, pvid_, 1, y, rect_.w-2, option_.row_height, "", qss);
pvHide(p, rowBkg);
vecRows_[row].bkg = rowBkg;
}
}
void PvTable::addHead(string id, string text, int width, vector<pair<string, string>> mapping)
{
vecHead_.push_back(Head(id, text, width, mapping));
nCol_ = vecHead_.size();
if (width <= -1)
{
width = rect_.w-1 - posCol_;
}
int col = nCol_ - 1;
// 创建表头的标签
if (option_.show_header)
{
vecHead_[col].pvid = PvApp::label(p, pvid_, posCol_, 0, width, option_.head_height, text, QSS_TABLE_HEAD);
}
// 创建列的单元格
for (int row = 0; row < nRow_; ++row)
{
int y = item_posy(row);
int pvid = PvApp::label(p, pvid_, posCol_, y, width, option_.row_height, "", QSS_TABLE_CELL);
vecRows_[row].vecCells.push_back(pvid);
PvApp::bind(p, MOUSE_OVER_EVENT, pvid, [=](string s) { highlight(row, (s == "1")); });
}
posCol_ += width;
}
void PvTable::addHead(vector<string> vec_text)
{
int colSize = vec_text.size();
int x = 0;
for (int i = 0; i < vec_text.size(); ++i)
{
int w = float(rect_.w-1) * float(i+1) / float(colSize);
string text = vec_text[i];
this->addHead(text, text, w-x);
x = w;
}
}
void PvTable::setRowVisible(int row, bool v)
{
if (row < 0 || row >= vecRows_.size())
{
return;
}
auto& rowItem = vecRows_[row];
if (rowItem.visible != v)
{
rowItem.visible = v;
rowItem.visible ? pvShow(p, rowItem.bkg) : pvHide(p, rowItem.bkg);
if (!v)
{
for (int col = 0; col<rowItem.vecCells.size(); ++col)
{
pvSetText(p, rowItem.vecCells[col], "");
}
}
}
}
void PvTable::highlight(int row, bool v)
{
string qss = ((row % 2 != 0) ? QSS_TABLE_ROW_0 : QSS_TABLE_ROW_1);
if (vecRows_.size() > 0 && row <= vecRows_.size())
{
if (v) { qss = "background-color:rgba(14,45,60,200);border:1px solid rgba(255,0,0,100);"; }
pvSetStyleSheet(p, vecRows_[row].bkg, qss.c_str());
}
}
void PvTable::addOperate(vector<string> vecOpt)
{
// 创建表头的标签
if (option_.show_header)
{
PvApp::label(p, pvid_, posCol_, 0, rect_.w - posCol_, option_.head_height, "操作", QSS_TABLE_HEAD);
}
for (int row = 0; row < nRow_; ++row)
{
int y = item_posy(row);
int btn_opt = PvApp::label(p, pvid_, posCol_, y, rect_.w - posCol_, option_.row_height, "", QSS_TABLE_CELL);
//PvInstance::bind_event(p, MOUSE_OVER_EVENT, btn_opt, [=](string s) { highlight(row, (s == "1")); });
vecOpt_.push_back({ btn_opt, vector<int>() });
auto& vec_opt_btn_ = vecOpt_.back().second;
int x = 5, w = 60;
for (int i = 0; i < vecOpt.size(); i++)
{
auto& title = vecOpt[i];
w = 20 + 15 * title.size() / 3;
int btn = PvApp::button(p, btn_opt, x, 4, w, 24, title, QSS_TABLE_BTN_VIEW);
PvApp::bind(p, PvEvent::BUTTON_EVENT, btn, [=](std::string) {
if (cbOperate_) { cbOperate_(row, 0, title); }
});
vec_opt_btn_.push_back(btn);
x += (w + 5);
}
pvHide(p, btn_opt);
}
}
void PvTable::setOperateCallback(CallbackTableOpt cb)
{
cbOperate_ = cb;
};
void PvTable::set_text(PARAM* p, int row, int col, string text, string style)
{
if (row < nRow_ && col < nCol_)
{
pvSetText(p, vecRows_[row].vecCells[col], text.c_str());
if (!style.empty())
{
int idx = row + 1;
if (idx % 2 != 0)
{
style = item_base_style_ + style;
}
else
{
style = item_base_style_ + style;
}
//style = "qproperty-alignment:AlignCenter;" + style + "}";
string s = "QLabel{" + style + "} QLabel:disabled{color:rgb(150,150,150)}";
pvSetStyleSheet(p, vecRows_[row].vecCells[col], s.c_str());
}
}
}
void PvTable::setRowData(int row, DataFields& d)
{
if (row >= nRow_) { return; }
vecData_[row] = d;
for (int col = 0; col < vecHead_.size(); ++col)
{
auto& head = vecHead_[col];
string text = d.getStr(head.id);
text = head.getMapping(text);
pvSetText(p, vecRows_[row].vecCells[col], text.c_str());
}
setRowVisible(row, true);
this->setOperateVisible(row, d.size() > 0);
}
void PvTable::setRowData(int row, std::vector<std::string> vd)
{
if (row >= nRow_) { return; }
pvShow(p, vecRows_[row].bkg);
for (int col = 0; col < vecHead_.size(); ++col)
{
if (col < vd.size()) {
auto& head = vecHead_[col];
string text = head.getMapping(vd[col]);
pvSetText(p, vecRows_[row].vecCells[col], text.c_str());
}
}
setRowVisible(row, true);
this->setOperateVisible(row, vd.size() > 0);
}
DataFields& PvTable::getRowdata(int row)
{
static DataFields tmp;
return (row >= 0 && row < vecData_.size()) ? vecData_[row] : tmp;
}
void PvTable::set_border_visible(PARAM* p, bool v)
{
v ? pvShow(p, border_id_) : pvHide(p, border_id_);
}
int PvTable::item_posy(int row)
{
return option_.show_header ? row * option_.row_height + option_.head_height : row * option_.row_height;
}
//void PvTable::set_item_btn_callback(CallbackTableOpt cb)
//{
// //cb_opt_ = cb;
//}
void PvTable::add_col_button(PARAM* p, int col, string title, PvRect& rt, string style)
{
if (col >= nCol_)
{
return;
}
for (int row = 0; row < nRow_; ++row)
{
int id = PvApp::button(p, vecRows_[row].vecCells[col], rt.x, rt.y, rt.w, rt.h, title, style);
pvHide(p, id);
vec_col_item_btn_[row].push_back(id);
//PvInstance::bind_event(p, PvEvent::BUTTON_EVENT, id, [=](string s)
//{
// if (cb_operate_)
// {
// cb_operate_(row, col, title);
// }
//});
}
}
string GetTableItemButtonStyle(string title)
{
static unordered_map<string, string> map_style =
{
//{PV::OPT_NEW, PvStyle::BTN_NEW},
//{PV::OPT_EDIT, PvStyle::BTN_EDIT},
//{PV::OPT_DEL, PvStyle::BTN_DELETE}
};
string style = map_style[title];
if (style.empty())
{
style = BTN_EDIT;
}
return style;
}
void PvTable::add_col_button(PARAM* p, int col, vector<string> vec_title)
{
int x = 5;
int w = 0;
for (int i = 0; i < vec_title.size(); i++)
{
auto& title = vec_title[i];
w = 20 + 20 * title.size() / 3;
this->add_col_button(p, col, title, PvRect(x, 3, w, 28), BTN_EDIT);
x += (w + 5);
}
}
void PvTable::setOperateVisible(int row, bool v, int id)
{
if (row < vecOpt_.size())
{
auto& vec_opt_btn = vecOpt_[row].second;
int pvid = id < 0 ? vecOpt_[row].first : ((id < vec_opt_btn.size()) ? vec_opt_btn[id] : PV_ID_NUL);
v ? pvShow(p, pvid) : pvHide(p, pvid);
}
}
int PvTable::border_id()
{
return border_id_;
}
int PvTable::rows()
{
return nRow_;
}
int PvTable::colums()
{
return nCol_;
}
vector<DataFields> PvTable::data()
{
return vecData_;
}
PvTable::Head& PvTable::header(int col)
{
return vecHead_[col];
}
static const string STYLE_NORMAL =
"QPushButton{background-color:rgba(255,255,255,30);border-radius:0px;font:bold 16px;color:white;border:1px solid #20a481;}"
"QPushButton:hover{background-color:rgba(32,164,128,255);color:white;}"
"QPushButton:pressed{border-width:3px 0 0 3px;border-style:inset;color:white;}";
static const string STYLE_ACTIVE =
"QPushButton{background-color:rgba(32,164,128,255);border-radius:0px;font:bold 16px;color:white;border:1px solid #20a481;}"
"QPushButton:hover{background-color:rgba(32,164,128,255);color:white;}"
"QPushButton:pressed{border-width:3px 0 0 3px;border-style:inset;color:white;}";
PvPagination::PvPagination(PARAM* p, int parent, const PvRect& rt)
: PvObject(p)
{
pvid_ = PvApp::label(p, parent, rt.x, rt.y, rt.w, rt.h, "", "");
// 分页控件
int x = 0;
int y = 0;
btn_prev_ = PvApp::button(p, pvid_, x, y, 30, 30, "<", STYLE_NORMAL);
for (int i = 1; i <= 7; i++)
{
int id = PvApp::button(p, pvid_, x += 32, y, 30, 30, std::to_string(i), STYLE_NORMAL);
vec_btn_page_.push_back({id, 0});
//PvInstance::bind_event(p, PvEvent::BUTTON_EVENT, id, [=](string s) { this->on_click_page(id); });
}
btn_next_ = PvApp::button(p, pvid_, x += 32, y, 30, 30, ">", STYLE_NORMAL);
//PvInstance::bind_event(p, PvEvent::BUTTON_EVENT, btn_prev_, [=](string e) { this->on_click_prev(); });
//PvInstance::bind_event(p, PvEvent::BUTTON_EVENT, btn_next_, [=](string e) { this->on_click_next(); });
this->set_page(0, 0);
}
void PvPagination::set_page(int page_id, int max_page)
{
this->active_page_button(p, page_id, page_id_);
page_id_ = page_id;
page_count_ = max_page;
int x = 32;
int y = 0;// table_->rect().h + 16;
for (int i = 0; i < vec_btn_page_.size(); ++i)
{
auto& btn_info = vec_btn_page_[i];
auto btnid = btn_info.first;
int idx = i + 1;
if (idx > page_count_)
{
pvHide(p, btnid);
}
else
{
pvShow(p, btnid);
if (page_count_ > 7)
{
if (idx == 4)
{
idx = 0;
}
else if (idx > 4)
{
idx = page_count_ - (7 - idx);
}
}
btn_info.second = idx;
string text = to_string(idx);
if (text.empty() || text == "0")
{
text = "...";
pvSetEnabled(p, btnid, false);
}
pvMove(p, btnid, x, y);
pvSetText(p, btnid, text.c_str());
if (page_id == idx)
{
this->active_page_button(p, page_id, page_id_);
page_id_ = page_id;
}
x += (32);
}
}
pvMove(p, btn_next_, x, y);
}
int PvPagination::pageid()
{
return page_id_;
};
void PvPagination::active_page_button(PARAM* p, int new_pageid, int old_pageid)
{
for (int i = 0; i < vec_btn_page_.size(); i++)
{
auto& item = vec_btn_page_[i];
if (item.second != 0)
{
if (item.second == old_pageid)
{
pvSetStyleSheet(p, item.first, STYLE_NORMAL.c_str());
}
if (item.second == new_pageid)
{
pvSetStyleSheet(p, item.first, STYLE_ACTIVE.c_str());
}
}
}
}
void PvPagination::set_goto_page_callback(std::function<void(int pageidx)> cb)
{
cb_goto_ = cb;
}
void PvPagination::on_click_page(int btnid)
{
for (int i = 0; i < vec_btn_page_.size(); ++i)
{
auto& item = vec_btn_page_[i];
if (btnid == item.first)
{
int page_id = item.second;
if (cb_goto_) { cb_goto_(page_id); }
return;
}
}
}
void PvPagination::on_click_prev()
{
if (page_id_ <= 1)
{
return;
}
if (cb_goto_)
{
cb_goto_(page_id_ - 1);
}
}
void PvPagination::on_click_next()
{
if (page_id_ >= page_count_)
{
return;
}
if (cb_goto_)
{
cb_goto_(page_id_ + 1);
}
}
PvPageTable::PvPageTable(PARAM* p, int parent, int x, int y, int w, int rows, PvTable::Options& opts)
//: PvWidget(p, parent, PvRect(x, y, w, opts.item_height* rows + (opts.show_header ? (opts.header_height) : 0)))
: PvObject(p)
{
int h = opts.row_height* rows + (opts.show_header ? (opts.head_height) : 0);
int pvid_ = PvApp::widget(p, parent, x, y, w, h);
table_ = make_shared<PvTable>(p, pvid_, 0, 0, w, rows, opts);
}
shared_ptr<PvTable> PvPageTable::getTable()
{
return table_;
}

174
src/pv/PvTable.h Normal file
View File

@@ -0,0 +1,174 @@
#ifndef _PvTable_H_
#define _PvTable_H_
#include <functional>
#include "PvApp.h"
#include "DataFields.h"
using CallbackTableOpt = std::function<void(int row, int col, std::string text)>;
class PvTable : public PvObject
{
public:
struct Options
{
bool show_header = true;
bool show_border = true;
int head_height = 40;
int row_height = 35;
int page_size = 10;
int width = 500;
};
struct Head
{
Head() {}
Head(std::string hid, std::string htext, int w, std::vector<std::pair<std::string, std::string>> mapping)
: id(hid), text(htext), width(w), vecMaping(mapping) {}
std::string id;
std::string text;
int width = 80;
int pvid = PV_ID_NUL;
vector<pair<string, string>> vecMaping;
std::string getMapping(std::string s)
{
std::string v = s;
for (auto& item : vecMaping)
{
if (v == item.first) { v = item.second; }
else if (v == item.second) { v = item.first; }
}
return v;
}
};
struct Row
{
bool visible = false;
int bkg {PV_ID_NUL};
std::vector<int> vecCells {};
};
public:
PvTable(PARAM* p, int parent, int x, int y, int w, int rows, Options& option);
void addHead(std::string id, std::string text, int width, std::vector<std::pair<std::string, std::string>> mapping = {});
void addHead(std::vector<std::string> vecText);
void setRowVisible(int row, bool v);
void highlight(int row, bool v);
void addOperate(std::vector<std::string> vecOpt);
void setOperateCallback(CallbackTableOpt cb);
void set_text(PARAM* p, int row, int col, std::string text, std::string style = "");
void setRowData(int row, DataFields& d);
void setRowData(int row, std::vector<std::string> vd);
DataFields& getRowdata(int row);
void set_border_visible(PARAM* p, bool v);
void add_col_button(PARAM* p, int col, std::string title, PvRect& rt, std::string style);
void add_col_button(PARAM* p, int col, std::vector<std::string> vec_title);
void setOperateVisible(int row, bool v, int id = -1);
int item_posy(int row);
int border_id();
int rows();
int colums();
std::vector<DataFields> data();
PvTable::Head& header(int col);
private:
int pvid_;
PvRect rect_;
int border_id_;
std::vector<PvTable::Head> vecHead_;
std::vector<PvTable::Row> vecRows_;
std::vector<DataFields> vecData_;
vector<pair<int, vector<int>>> vecOpt_;
int nRow_;
int nCol_;
string item_base_style_;
Options option_;
unordered_map<int, vector<int>> vec_col_item_btn_;
CallbackTableOpt cbOperate_ = nullptr;
int posCol_ = 0;
};
class PvPagination : public PvObject
{
public:
PvPagination(PARAM* p, int parent, const PvRect& rt);
void set_page(int page_id, int max_page);
int pageid();
void set_goto_page_callback(std::function<void(int pageid)> cb);
private:
void on_click_page(int btnid);
void on_click_prev();
void on_click_next();
void active_page_button(PARAM* p, int new_pageid, int old_pageid);
private:
int pvid_ = PV_ID_NUL;
// 当前显示的页码索引, 从1开始
int page_id_ = 1;
// 总页数
int page_count_ = 0;
// 上一页按钮
int btn_prev_ = PV_ID_NUL;
// 下一页按钮
int btn_next_ = PV_ID_NUL;
// 页面跳转按钮
int btn_gopage_ = PV_ID_NUL;
// 页码列表最多显示6个页码按钮,前3页和后3页 <按钮ID, 页码>
std::vector<pair<int, int>> vec_btn_page_;
function<void(int)> cb_goto_ = nullptr;
};
class PvPageTable :public PvObject
{
public:
PvPageTable(PARAM* p, int parent, int x, int y, int w, int rows, PvTable::Options& opts);
shared_ptr<PvTable> getTable();
private:
shared_ptr<PvTable> table_ = nullptr;
shared_ptr<PvPagination> page_ctrl_ = nullptr;
};
#endif // ! _PvTable_H_

11
src/pv/PvUser.h Normal file
View File

@@ -0,0 +1,11 @@
#pragma once
#include <map>
#include <string>
#include <functional>
class PvUser
{
public:
int pvidIndex = 0;
std::map<std::string, std::function<void(std::string text)>> mapEventCallback;
};

View File

@@ -0,0 +1,44 @@
#include "MaskPageForecast.h"
#include "pv/PvChart.h"
static int CreatePanel(PARAM* p, int parentId, int x, int y, int w, int h, std::string title)
{
int id = PvApp::label(p, parentId, x, y, w, h, "", QSS_LABEL_BKG_2);
PvApp::label(p, id, 10, 10, w, 20, title, STYLE_TITLE_ICON);
PvApp::label(p, id, 20, 30, w, 2, "", QSS_UNDERLINE);
return id;
}
MaskPageForecast::MaskPageForecast(PARAM* p) : PvMask(p)
{
}
int MaskPageForecast::initUI(EPvCode pvcode)
{
int x = 10, y = 100, w = 1900, h = 420;
int delta = 40;
{
int panel = CreatePanel(p, 0, x, y, w, h, "储能充放电预测");
chart1_ = std::make_shared<PvChartCurve>(p, panel, 0, delta, w, h-delta);
chart1_->setBackground(p, 8, 54, 91);
chart1_->setLabelYLeft("电量(kWh)", 0, 10, 2);
chart1_->insertItem(0, "充电电量", 120, 10, 80);
chart1_->insertItem(0, "放电电量", 200, 10, 80);
}
{
int panel = CreatePanel(p, 0, x, y+=(h+10), w = 945, h, "光伏发电预测");
chart2_ = std::make_shared<PvChartCurve>(p, panel, 0, delta, w, h-delta);
chart2_->setBackground(p, 8, 54, 91);
chart2_->setLabelYLeft("电量(kWh)", 0, 10, 2);
chart2_->insertItem(0, "发电电量", 120, 10, 80);
}
{
int panel = CreatePanel(p, 0, x+=(w+10), y, w, h, "充电负荷预测");
chart3_ = std::make_shared<PvChartCurve>(p, panel, 0, delta, w, h-delta);
chart3_->setBackground(p, 8, 54, 91);
chart3_->setLabelYLeft("电量(kWh)", 0, 10, 2);
chart3_->insertItem(0, "充电电量", 120, 10, 80);
}
return 0;
}

View File

@@ -0,0 +1,16 @@
#pragma once
#include "pv/PvApp.h"
class PvChartCurve;
class MaskPageForecast : public PvMask
{
public:
MaskPageForecast(PARAM* p);
int initUI(EPvCode pvcode);
std::shared_ptr<PvChartCurve> chart1_;
std::shared_ptr<PvChartCurve> chart2_;
std::shared_ptr<PvChartCurve> chart3_;
};

View File

@@ -0,0 +1,329 @@
#include "MaskPageHome.h"
#include "common/Utils.h"
#include "app/Application.h"
#include "app/Station.h"
#include "pv/PvTable.h"
#include "pv/PvChart.h"
static int CreatePanel(PARAM* p, int parent, int x, int y, int w, int h, std::string title)
{
int panelId = PvApp::label(p, parent, x, y, w, h, "", QSS_LABEL_BKG_1);
int titleId = PvApp::label(p, panelId, 10, 8, w, 22, title, STYLE_TITLE_ICON);
PvApp::label(p, panelId, 20, 28, w, 2, "", QSS_UNDERLINE);
return panelId;
}
static int CreatePanel1(PARAM* p, int parent, int x, int y, int w, int h, std::string title)
{
int panelId = PvApp::label(p, parent, x, y, w, h, "", "border: none; background-color: transparent;");
PvApp::image(p, panelId, 0, 13, 500, 17, "bkgBox.png");
PvApp::label(p, panelId, 20, 0, w-20, 30, title, "background-color: transparent; font: bold 18px;");
return panelId;
}
static int CreateCard1(PARAM* p, int parentId, int x, int y, int w, int h, std::string title, std::string val)
{
int id = PvApp::label(p, parentId, x, y, w, h, "", QSS_LABEL_BKG_1);
int idTitle = PvApp::label(p, id, 0, h*0.5, w, h*0.5, title, "background:transparent; font: bold 28px;");
int idVal = PvApp::label(p, id, 0, 0, w, h*0.5, val, "background:transparent; font: bold 28px; color:rgb(77,215,240);");
pvSetAlignment(p, idTitle, AlignCenter);
pvSetAlignment(p, idVal, AlignHCenter | AlignBottom);
return id;
}
static int CreateCard2(PARAM* p, int parent, int x, int y, int w, int h, std::string title, std::string val)
{
int id = PvApp::label(p, parent, x, y, w, h, "", QSS_LABEL_BKG_1);
int idTitle = PvApp::label(p, id, 0, 0, w, h*0.5, title, "background:transparent; font: bold 16px;");
int idVal = PvApp::label(p, id, 0, h*0.5, w, h*0.5, val, "background:transparent; font: bold 16px; color:rgb(77,215,240);");
pvSetAlignment(p, idTitle, AlignCenter);
pvSetAlignment(p, idVal, AlignCenter);
return id;
}
static int CreateBox(PARAM* p, int parent, int x, int y, int w, int h, std::string k, std::string val="")
{
int id = PvApp::label(p, parent, x, y, w, h, "", "border-radius:0px; background-color: rgb(7, 45, 66); border: 1px solid rgb(27, 88, 105);");
{
int len = 10;
std::string qss = "background-color: transparent; border: 1px solid rgb(0, 218, 216);";
PvApp::label(p, id, 0, 0, len, len, "", qss + "border-width: 2px 0 0 2px");
PvApp::label(p, id, w-len, 0, len, len, "", qss + "border-width: 2px 2px 0 0");
PvApp::label(p, id, w-len, h-len, len, len, "", qss + "border-width: 0 2px 2px 0");
PvApp::label(p, id, 0, h-len, len, len, "", qss + "border-width: 0 0 2px 2px");
}
int titleId = PvApp::label(p, id, 0, 0, w, h*0.5, k, "border:none; background-color: transparent; font: bold 14px; padding-bottom: 0px;");
int valId = PvApp::label(p, id, 0, h*0.5, w, h*0.5, val, "border:none; background-color: transparent; font: bold 16px; color:rgb(77, 215, 240);");
pvSetAlignment(p, titleId, AlignHCenter | AlignBottom);
pvSetAlignment(p, valId, AlignHCenter | AlignTop);
return valId;
}
static int CreateIconBox(PARAM* p, int parent, int x, int y, std::string k, std::string val, std::string icon)
{
//PvApp::label(p, parent, x, y, 50, 50, "", "background-color: rgb(2, 41, 60); border: 1px solid rgb(61, 254, 250);");
//PvApp::label(p, parent, x+3, y+3, 44, 44, "", "background-color: transparent; border: 2px solid rgb(31, 91, 139);");
PvApp::image(p, parent, x, y, 50, 50, icon.c_str());
PvApp::label(p, parent, x+52, y, 100, 20, k, "background-color: transparent; border: none; font: bold 14px;");
int valId = PvApp::label(p, parent, x+52, y+25, 100, 20, val, "background-color: transparent; border: none; font: bold 14px;");
return valId;
}
class PopStation
{
public:
PARAM* p {};
int widget = PV_ID_NUL;
int width = 1100;
int height = 800;
int labelTitle = PV_ID_NUL;
PopStation(PARAM* p) : p(p)
{
int wMask = 1920, hMask = 960;
widget = PvApp::widget(p, PV_ID_MAIN, 0, 0, wMask, hMask);
PvApp::label(p, widget, 0, 0, 1920, 960, "", "background-color: rgb(1, 32, 54);");
// 站名称Title
labelTitle = PvApp::label(p, widget, 0, 40, wMask, 30, "", "border: none; background-color: transparent; font: bold 20px;");
pvSetAlignment(p, labelTitle, AlignCenter);
{
int pid = PvApp::label(p, widget, (wMask-width)*0.5, (hMask-height)*0.5, width, height, "", "background-color: rgb(2, 58, 87); border: 1px solid rgb(31, 198, 255);");
PvApp::label(p, pid, 0, 0, 80, height, "", "background-color: transparent; border: 3px solid rgb(31, 198, 255); border-width: 3px 0 3px 3px;");
PvApp::label(p, pid, width-80, 0, 80, height, "", "background-color: transparent; border: 3px solid rgb(31, 198, 255); border-width: 3px 3px 3px 0;");
int btnClose = PvApp::button(p, pid, width-40, 5, 30, 30, "", "background-color: white; border: none; border-radius: 15px; color: rgb(2, 59, 87); font: bold 20px;");
PvApp::bind(p, PvEvent::BUTTON_EVENT, btnClose, [=](std::string) {
pvHide(p, widget);
});
int x = 20, y = 40, w = 500, h = 240;
{
int box = CreatePanel1(p, pid, x, y, w, h, "预制舱信息");
int x0 = 50, y0 = 40, w0 = 150, h0 = 60;
CreateBox(p, box, x0 = 60, y0, w0, h0, "电池类型", "磷酸铁锂电池");
CreateBox(p, box, w-x0-w0, y0, w0, h0, "冷却模式", "风冷");
CreateBox(p, box, x0 = 20, y0 += (h0+5), w0, h0, "运行模式", "最优经济化");
CreateBox(p, box, w-x0-w0, y0, w0, h0, "电池额定电压", "300 V");
CreateBox(p, box, x0 = 60, y0 += (h0+5), w0, h0, "PCS额定功率", "100 kW");
CreateBox(p, box, w-x0-w0, y0, w0, h0, "电池储能容量", "100 kWh");
}
{
y += (h+10);
int box = CreatePanel1(p, pid, x, y, w, h, "运行与统计信息");
CreateIconBox(p, box, 40, 40, "舱内温度", "20 ℃", "icon1.png");
CreateIconBox(p, box, 40+160, 40, "舱内湿度", "30 %", "icon2.png");
CreateIconBox(p, box, 40+160*2, 40, "电压", "200 V", "icon3.png");
CreateIconBox(p, box, 40, 90, "电流", "30 A", "icon4.png");
CreateIconBox(p, box, 40+160, 90, "功率", "6 kW", "icon5.png");
CreateIconBox(p, box, 40+160*2, 90, "功率因数", "1.0", "icon5.png");
int x0 = 10, y0 = 160, w0 = 90, h0 = 60;
CreateBox(p, box, x0, y0, w0, h0, "场站运行天数", "365 天");
CreateBox(p, box, x0 += (w0+10), y0, w0, h0, "储能充电电量", "22 kWh");
CreateBox(p, box, x0 += (w0+10), y0, w0, h0, "储能放电电量", "11 kWh");
CreateBox(p, box, x0 += (w0+10), y0, w0, h0, "场站累计收益", "123.12 元");
CreateBox(p, box, x0 += (w0+10), y0, w0, h0, "设备利用率", "10 %");
}
{
y += (h+10);
int box = CreatePanel1(p, pid, x, y, w, h, "环境信息");
CreateIconBox(p, box, 80, 50, "光照强度", "27.1 Lux", "icon1.png");
CreateIconBox(p, box, 80+200*1, 50, "风速", "1.2 m/s", "icon2.png");
CreateIconBox(p, box, 80, 120, "环境温度", "35.7 ℃", "icon1.png");
CreateIconBox(p, box, 80+200*1, 120, "环境湿度", "31.3 %", "icon2.png");
}
{
x = 580; y = 40; h = 240;
int box = CreatePanel1(p, pid, x, y, w, h, "储能充放电量");
PvChartCurve chart(p, box, 0, 40, 500, 200);
chart.setBackground(p, 2, 58, 87);
chart.setLabelYLeft("电量kWh");
chart.insertItem(0, "充电电量", 120, 10, 80);
chart.insertItem(0, "放电电量", 200, 10, 80);
}
{
y += (h+10);
int box = CreatePanel1(p, pid, x, y, w, h, "场站收益");
PvChartCurve chart(p, box, 0, 40, 500, 200);
chart.setBackground(p, 2, 58, 87);
chart.setLabelYLeft("电量(元)");
chart.insertItem(0, "收益", 120, 10, 80);
}
{
y += (h+10);
int box = CreatePanel1(p, pid, x, y, w, h, "设备利用率");
PvChartCurve chart(p, box, 0, 40, 500, 200);
chart.setBackground(p, 2, 58, 87);
chart.setLabelYLeft("利用率(%");
chart.insertItem(0, "利用率", 120, 10, 80);
}
}
}
void setTitle(std::string title)
{
pvSetText(p, labelTitle, title.c_str());
}
void show(int v)
{
v ? pvShow(p, widget) : pvHide(p, widget);
}
};
//background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0,stop:1 rgba(0,255,240,200),stop:0 rgba(0,255,240,0));
int MaskPageHome::initUI(EPvCode pvcode)
{
int x = 10, y = 100, w = 500, h1 = 260, h2=290, h3=290;
// 左侧区域: 上
{
int x0 = 0, y0=40, w0=160, h0=64;
int panel = CreatePanel(p, 0, x, y, w, h1, "运行总览");
ui.labelRunDays = CreateBox(p, panel, x0 = 50, y0, w0, h0, "运行时间");
ui.labelStationNum = CreateBox(p, panel, w-10-w0-x0, y0, w0, h0, "能源站数量");
ui.labelEnergyCapacity = CreateBox(p, panel, x0 = 10, y0+=(h0+10), w0, h0, "储能总容量");
ui.labelIncome = CreateBox(p, panel, w-10-w0-x0, y0, w0, h0, "累计收益");
ui.labelStorageIn = CreateBox(p, panel, x0 = 50, y0 += (h0+10), w0, h0, "累计储能充电量");
ui.labelStorageOut = CreateBox(p, panel, w-10-w0-x0, y0, w0, h0, "累计储能放电量");
}
// 左侧区域: 中
{
int panel = CreatePanel(p, 0, x, y+h1+10, w, h2, "储能设备");
int x1 = 10;
CreateCard2(p, panel, x1, 40, 235, 50, "今日充电电量", "0 kWh");
CreateCard2(p, panel, x1+=245, 40, 235, 50, "今日放电电量", "0 kWh");
auto bar = PvChartBar::create(p, panel, 10, 100, w-20, h2-100);
bar->setLabelYLeft("电量(kWh)");
bar->insertItem("日充电电量");
bar->insertItem("日放电电量");
bar->updateItem(0, {{"08/01", 10}, {"08/02", 20}, {"08/03", 30}, {"08/04", 40}, {"08/05", 50}, {"08/06", 60}, {"08/07", 70}});
bar->updateItem(1, {{"08/01", 1}, {"08/02", 2}, {"08/03", 3}, {"08/04", 4}, {"08/05", 5}, {"08/06", 6}, {"08/07", 7}});
}
// 左侧区域: 下
{
int panel = CreatePanel(p, 0, x, y+h1+h2+20, w, h3, "光伏设备");
int x1 = 10;
CreateCard2(p, panel, x1, 40, 235, 50, "今日发电电量", "0 kWh");
CreateCard2(p, panel, x1 += 245, 40, 235, 50, "今日入网电量", "0 kWh");
auto bar = PvChartBar::create(p, panel, 10, 100, w-20, h3-100);
bar->setLabelYLeft("电量(kWh)");
bar->insertItem("日发电电量");
bar->insertItem("日入网电量");
bar->updateItem(0, {{"08/01", 10}, {"08/02", 20}, {"08/03", 30}, {"08/04", 40}, {"08/05", 50}, {"08/06", 60}, {"08/07", 70}});
}
// 右侧区域:上
{
int panel = CreatePanel(p, 0, x += (880+10+500+10), y, w = 500, h1, "运行分析");
}
// 右侧区域:中
{
int panel = CreatePanel(p, 0, x, y+h1+10, w = 500, h2, "充电设备");
int x1 = 10;
CreateCard2(p, panel, x1, 40, 235, 50, "今日充电电量", "0 kWh");
CreateCard2(p, panel, x1 += 245, 40, 235, 50, "今日充电次数", "0 次");
auto bar = PvChartBar::create(p, panel, 10, 100, w-20, h2-100);
bar->setLabelYLeft("电量(kWh)");
bar->insertItem("日充电电量");
bar->insertItem("日充电次数");
bar->updateItem(0, {{"08/01", 10}, {"08/02", 20}, {"08/03", 30}, {"08/04", 40}, {"08/05", 50}, {"08/06", 60}, {"08/07", 70}});
}
// 右侧区域:下
{
int panel = CreatePanel(p, 0, x, y+h1+h2+20, w = 500, h3, "告警分析");
int x1 = 10;
CreateCard2(p, panel, x1, 40, 153, 50, "今日储能告警", "0 次");
CreateCard2(p, panel, x1 += 163, 40, 153, 50, "今日光伏告警", "0 次");
CreateCard2(p, panel, x1 += 163, 40, 153, 50, "今日充电告警", "0 次");
auto bar = PvChartBar::create(p, panel, 10, 100, w-20, h3-100);
bar->setLabelYLeft("次数");
bar->insertItem("日储能告警");
bar->insertItem("日光伏告警");
bar->insertItem("日充电告警");
bar->updateItem(0, {{"08/01", 10}, {"08/02", 20}, {"08/03", 30}, {"08/04", 40}, {"08/05", 50}, {"08/06", 60}, {"08/07", 70}});
}
// 中间区域
{
int panel = PvApp::label(p, 0, x = 10+500+10, y, w = 880, h1+h2+h3+20, "", QSS_LABEL_BKG_1);
////// 饼图
//int left = PvApp::widget(p, panel, 100, 100, 100, 100);
//pvSetStyleSheet(p, left, "border: 1px solid gray; border-radius:50px;");
//int id = PvApp::pvid();
//pvDownloadFile(p, "red.png");
//pvQImage(p, id, left, "");
//pvSetGeometry(p, id, 10, 10, 100, 100);
//pvSetImage(p, id, "red.png", 0);
////pvSetStyleSheet(p, id, "border-radius: 50px;");
//int right = PvApp::label(p, panel, 200, 100, 100, 200, "", "border: 1px solid gray;");
//std::string qss = "background: qlineargradient(x1:0, y1 : 0, x2 : 1, y2 : 1, stop : 0 red, stop:1 blue); border-radius: 50px;";
//std::string qss1 = "width: 200px;height: 200px;background: red;transform: rotate(45deg); border-radius: 100px;";
//PvApp::label(p, left, 0, 0, 200, 200, "", qss1);
PvApp::image(p, panel, 0, 20, 880, 800, "map.png");
auto popStation = new PopStation(p);
popStation->show(0);
int btn = PvApp::button(p, panel, 10, 20, 100, 30, "场站一", STYLE_BTN);
PvApp::bind(p, PvEvent::BUTTON_EVENT, btn, [=](std::string) {
popStation->setTitle("场站一");
pvShow(p, popStation->widget);
});
PvApp::image(p, panel, 500, 420, 50, 50, "mapMarker.png");
PvApp::image(p, panel, 420, 500, 50, 50, "mapMarker.png");
PvApp::image(p, panel, 520, 532, 50, 50, "mapMarker.png");
PvApp::image(p, panel, 363, 380, 50, 50, "mapMarker.png");
PvApp::image(p, panel, 545, 564, 50, 50, "mapMarker.png");
}
this->updateUI();
return 0;
}
void MaskPageHome::updateUI()
{
auto& appdata = Application::instance().getAppData();
int stationNum = appdata.mapStation.size(); // 场站数量
double energyCapacity {}; // 储能容量
double incomeTotal {}; // 累计收益
double electStorageIn {}; // 储能充电电量
double electStorageOut {}; // 储能放电电量
for (auto& item : appdata.mapStation)
{
auto& station = item.second;
energyCapacity += station->energyCapacity;
incomeTotal += station->incomeTotal;
}
pvSetText(p, ui.labelRunDays, "100 天");
pvSetText(p, ui.labelRunDays, (Utils::toStr(stationNum) + "").c_str());
pvSetText(p, ui.labelEnergyCapacity, (Utils::toStr(energyCapacity) + " kWh").c_str());
pvSetText(p, ui.labelIncome, (Utils::toStr(incomeTotal) + "").c_str());
pvSetText(p, ui.labelStorageIn, (Utils::toStr(electStorageIn) + " kWh").c_str());
pvSetText(p, ui.labelStorageOut, (Utils::toStr(electStorageOut) + " kWh").c_str());
}

View File

@@ -0,0 +1,37 @@
#pragma once
#include "pv/PvApp.h"
class MaskPageHome : public PvMask
{
public:
// 控件ID的数量需要在创建控件之前确定值和数量
int idCount = 100;
int iconView1;
int welcomeLabel;
int helpLabel;
int buttonRestroom;
public:
MaskPageHome(PARAM* p) : PvMask(p)
{
iconView1 = PvApp::pvid(p);
welcomeLabel = PvApp::pvid(p);
helpLabel = PvApp::pvid(p);
buttonRestroom = PvApp::pvid(p);
}
int initUI(EPvCode pvcode);
void updateUI();
struct {
int labelRunDays;
int labelStationNum;
int labelEnergyCapacity;
int labelIncome;
int labelStorageIn;
int labelStorageOut;
} ui;
};

View File

@@ -0,0 +1,253 @@
#include "MaskPageRunning.h"
#include "app/Application.h"
static std::string QSS_CARD_DEVICE =
"QLabel { background-color:rgb(8, 54, 91); border:0px solid rgb(120, 120, 120); border-radius:5px; font:bold 14px; color:white; }"
"QLabel:hover {border: 2px solid rgb(79, 129, 255); border-radius:2px;}"
"QLabel:disabled { color:rgb(150,150,150);}";
static std::string QSS_PARAM_K = "border:none; background-color: transparent; color: rgb(180,180,180); font: bold 13px;";
static std::string QSS_PARAM_V = "border:none; background-color: transparent; color: white; font: bold 14px;";
static int CreateParamLabel(PARAM* p, int parent, int x, int y, std::string k, std::string v)
{
PvApp::label(p, parent, x, y, 70, 30, k, QSS_PARAM_K);
return PvApp::label(p, parent, x += 70, y, 120, 30, v, QSS_PARAM_V);
}
class BoxCard : PvObject
{
public:
static std::shared_ptr<BoxCard> create(PARAM* p, int parent, int x, int y)
{
return std::make_shared<BoxCard>(p, parent, x, y);
}
BoxCard(PARAM* p, int parent, int x, int y) : PvObject(p)
{
card_ = PvApp::label(p, parent, x, y, 400, 250, "", QSS_CARD_DEVICE);
PvApp::label(p, card_, 10, 10, 60, 60, "", "border:none; background-color: rgb(39, 158, 145);");
ui.code = PvApp::label(p, card_, 80, 10, 100, 20, "", "border:none; background-color: transparent;");
ui.name = PvApp::label(p, card_, 80, 30, 100, 20, "", "border:none; background-color: transparent;");
ui.type = PvApp::label(p, card_, 80, 50, 100, 20, "", "border:none; background-color: transparent; color: rgb(8, 161, 249);");
int x1 = 190;
ui.online = PvApp::labelAlignCenter(p, card_, x1, 10, 70, 30, "在线", QSS_PARAM_V);
ui.running = PvApp::labelAlignCenter(p, card_, x1 += 70, 10, 70, 30, "空闲", QSS_PARAM_V);
ui.err = PvApp::labelAlignCenter(p, card_, x1 += 70, 10, 70, 30, "正常", QSS_PARAM_V);
PvApp::labelAlignCenter(p, card_, x1 = 190, 40, 70, 30, "在线状态", QSS_PARAM_K);
PvApp::labelAlignCenter(p, card_, x1 += 70, 40, 70, 30, "工作状态", QSS_PARAM_K);
PvApp::labelAlignCenter(p, card_, x1 += 70, 40, 70, 30, "故障状态", QSS_PARAM_K);
PvApp::label(p, card_, 10, 80, 80, 30, "运行分析:", QSS_PARAM_K);
PvApp::button(p, card_, 80, 83, 60, 24, "查看", "border:none; border-radius: 5px; background-color: rgb(28, 145, 138); color:white; font: bold 14px;");
// 默认创建 10 个参数标签:
int n = 10;
vecParamLabel_.resize(n);
for (int i = 0; i<n; i++)
{
int row = i/2;
int col = i%2;
int h = 25;
vecParamLabel_[i].first = PvApp::label(p, card_, 10 + 200*col, 115 + h*row, 70, h, "参数"+std::to_string(i) + ":", QSS_PARAM_K);
vecParamLabel_[i].second = PvApp::label(p, card_, 10 + 200*col + 70, 115 + h*row, 120, h, "---", QSS_PARAM_V);
}
}
void setCard(std::string type, std::string name, std::string code)
{
if (ui.code != PV_ID_NUL) { pvSetText(p, ui.code, code.c_str()); }
if (ui.name != PV_ID_NUL) { pvSetText(p, ui.name, name.c_str()); }
if (ui.type != PV_ID_NUL) { pvSetText(p, ui.type, type.c_str()); }
}
void setParamkeys(std::vector<std::string> vecKeys)
{
for (int i = 0; i<vecParamLabel_.size(); i++)
{
auto& labelKey = vecParamLabel_[i].first;
auto& labelVal = vecParamLabel_[i].second;
if (i<vecKeys.size())
{
std::string& title = vecKeys[i] + "";
pvSetText(p, labelKey, title.c_str());
pvSetText(p, labelVal, "---");
pvShow(p, labelKey);
pvShow(p, labelVal);
}
else
{
pvHide(p, labelKey);
pvHide(p, labelVal);
}
}
}
void show(int visible)
{
visible ? pvShow(p, card_) : pvHide(p, card_);
}
struct {
int code = PV_ID_NUL;
int name = PV_ID_NUL;
int type = PV_ID_NUL;
int online = PV_ID_NUL;
int running = PV_ID_NUL;
int err = PV_ID_NUL;
} ui;
int card_;
std::vector<std::pair<int, int>> vecParamLabel_;
std::map<std::string, int> mapParam_;
};
MaskPageRunning::MaskPageRunning(PARAM* p) : PvMask(p)
{
}
int MaskPageRunning::initUI(EPvCode pvcode)
{
PvApp::label(p, 0, 10, 100, 1900, 850, "", "background-color: rgb(5, 47, 77); border-radius: 10px;");
PvApp::label(p, 0, 10, 150, 220, 790, "", "background-color: rgb(8, 54, 91); border-radius: 10px;");
int workspace = PvApp::label(p, 0, 240, 150, 1670, 790, "", "background-color: rgba(8, 54, 91, 0); border-radius: 10px;");
std::vector<std::string> vecStationNames;
Application::instance().getAppData().getStationNames(vecStationNames);
PvApp::label(p, 0, 20, 110, 80, 30, "场站切换", "color:white; font: bold 16px;");
PvApp::combox(p, 0, 100, 110, 150, 30, vecStationNames);
if (vecStationNames.size() > 0)
{
station_ = Application::instance().getAppData().getStationByName(vecStationNames[0]);
}
PvApp::label(p, 0, 320, 110, 80, 30, "运行模式", "color:white; font: bold 16px;");
PvApp::combox(p, 0, 400, 110, 200, 30, {"最优经济化运行模式", "最优经济化运行模式", "自定义"});
PvApp::label(p, 0, 670, 110, 80, 30, "策略名称", "color:white; font: bold 16px;");
PvApp::combox(p, 0, 750, 110, 200, 30, {"峰谷套利策略", "需求响应策略", "自发自用上网策略"});
int x = 20, y = 160, w = 200, h = 180;
// 储能设备
{
ui.storage.name = "储能设备";
int pid = ui.storage.box = PvApp::label(p, 0, x, y, w, h, "", QSS_BOX);
PvApp::label(p, pid, 10, 0, w-10, 30, ui.storage.name, QSS_TITLE);
ui.storage.btn = PvApp::button(p, pid, 0, 0, w, h, "", "background-color: transparent;");
ui.storage.workspace = PvApp::widget(p, workspace, 0, 0, 1670, 790);
pvHide(p, ui.storage.workspace);
// 创建信息卡片
{
vecCard_.resize(12);
for (int i=0; i<vecCard_.size(); ++i)
{
vecCard_[i] = BoxCard::create(p, ui.storage.workspace, (i%4)*410, (i/4)*260);
}
}
}
// 光伏设备
{
ui.solar.name = "光伏设备";
int pid = ui.solar.box = PvApp::label(p, 0, x, y += (h+10), w, h, "", QSS_BOX);
PvApp::label(p, pid, 10, 0, w-10, 30, ui.solar.name, QSS_TITLE);
ui.solar.btn = PvApp::button(p, pid, 0, 0, w, h, "", "background-color: transparent;");
ui.solar.workspace = ui.storage.workspace;
pvHide(p, ui.solar.workspace);
}
// 充电设备
{
ui.charge.name = "充电设备";
int pid = ui.charge.box = PvApp::label(p, 0, x, y += (h+10), w, h, "", QSS_BOX);
PvApp::label(p, pid, 10, 0, w-10, 30, ui.solar.name, QSS_TITLE);
ui.charge.btn = PvApp::button(p, pid, 0, 0, w, h, "", "background-color: transparent;");
ui.charge.workspace = ui.storage.workspace;
pvHide(p, ui.charge.workspace);
}
// 环境与安防设备
{
ui.security.name = "环境与安防设备";
int pid = ui.security.box = PvApp::label(p, 0, x, y += (h+10), w, h, "", QSS_BOX);
PvApp::label(p, pid, 10, 0, w-10, 30, ui.security.name, QSS_TITLE);
ui.security.btn = PvApp::button(p, pid, 0, 0, w, h, "", "background-color: transparent;");
ui.security.workspace = PvApp::widget(p, workspace, 0, 0, 1670, 790);
pvHide(p, ui.security.workspace);
}
activeBoxPanel(ui.storage);
{
}
return 0;
}
EPvCode MaskPageRunning::onEventButton(int pvid)
{
if (pvid == ui.storage.btn) { activeBoxPanel(ui.storage); }
else if (pvid == ui.solar.btn) { activeBoxPanel(ui.solar); }
else if (pvid == ui.charge.btn) { activeBoxPanel(ui.charge); }
else if (pvid == ui.security.btn) { activeBoxPanel(ui.security); }
return EPvCode::NUL;
}
std::map<std::string, std::vector<std::string>> mapDeviceTypes =
{
{"储能设备", {"储能预制舱", "储能电池", "储能电池", "储能电池"}},
{"光伏设备", {"光伏板", "光伏板", "光伏板", "光伏板", "光伏板", "光伏板", "光伏板", "光伏板"}},
{"充电设备", {"充电桩", "充电桩", "充电桩", "充电桩", "充电桩", "充电桩", "充电桩", "充电桩", "充电桩", "充电桩"}},
};
std::map<std::string, std::vector<std::string>> mapDeviceParams =
{
{"储能预制舱", {"运行模式", "冷却方式", "实时电压", "额定电压", "实时电流", "额定电流", "实时功率", "额定功率", "功率因数", "电池容量"}},
{"储能电池", {"实时电压", "额定电压", "实时电流", "额定电流", "实时功率", "额定功率", "功率因数", "电池容量"}},
{"逆变器", {"实时电压", "额定电压", "实时电流", "额定电流", "实时功率", "额定功率", "功率因数"}},
{"光伏板", {"实时电压", "额定电压", "实时电流", "额定电流", "实时功率", "额定功率", "功率因数"}},
{"充电桩", {"实时电压", "额定电压", "实时电流", "额定电流", "实时功率", "额定功率", "功率因数"}},
};
void MaskPageRunning::activeBoxPanel(BoxPanel& panel)
{
static int activeBox = PV_ID_NUL;
static int activeWorkspace = PV_ID_NUL;
if (activeBox != PV_ID_NUL) { pvSetStyleSheet(p, activeBox, QSS_BOX.c_str()); }
if (activeBox = panel.box) { pvSetStyleSheet(p, activeBox, QSS_BOX_ACTIVE.c_str()); }
if (activeWorkspace != PV_ID_NUL) { pvHide(p, activeWorkspace); }
if (activeWorkspace = panel.workspace) { pvShow(p, activeWorkspace); }
if (panel.workspace != ui.security.workspace)
{
std::vector<std::string> vecDeviceInfo = mapDeviceTypes[panel.name];
// 读取储能设备信息
for (int i = 0; i<vecCard_.size(); ++i)
{
auto& card = vecCard_[i];
if (i < vecDeviceInfo.size())
{
std::string type = vecDeviceInfo[i];
card->show(1);
card->setCard(type, type+"-"+std::to_string(i), type+"-"+std::to_string(i));
card->setParamkeys(mapDeviceParams[type]);
}
else
{
card->show(0);
}
}
}
}

View File

@@ -0,0 +1,35 @@
#pragma once
#include "pv/PvApp.h"
class BoxCard;
class Station;
class MaskPageRunning : public PvMask
{
public:
struct BoxPanel {
std::string name;
int box;
int btn;
int workspace;
};
MaskPageRunning(PARAM* p);
int initUI(EPvCode pvcode);
virtual EPvCode onEventButton(int pvid);
void activeBoxPanel(BoxPanel& panel);
struct {
BoxPanel storage;
BoxPanel solar;
BoxPanel charge;
BoxPanel security;
} ui;
std::vector<std::shared_ptr<BoxCard>> vecCard_;
std::shared_ptr<Station> station_ = nullptr;
};

View File

@@ -0,0 +1,167 @@
#include "MaskPageStat.h"
#include "pv/PvTable.h"
#include "pv/PvChart.h"
static int CreatePanel(PARAM* p, int parentId, int x, int y, int w, int h, std::string title)
{
int id = PvApp::label(p, parentId, x, y, w, h, "", QSS_LABEL_BKG_2);
PvApp::label(p, id, 10, 10, w, 20, title, STYLE_TITLE_ICON);
PvApp::label(p, id, 20, 30, w, 2, "", QSS_UNDERLINE);
return id;
}
MaskPageStat::MaskPageStat(PARAM* p) : PvMask(p)
{
}
struct StatChartInfo
{
std::string name;
std::vector<std::string> vecAxis;
std::vector<std::pair<std::string, int>> vecItems;
StatChartInfo(std::string name, std::vector<std::string> axis, std::vector<std::pair<std::string, int>> items) : name(name), vecAxis(axis), vecItems(items) {};
};
using VecChartDef = std::vector<StatChartInfo>;
static VecChartDef chartStorageDef =
{
StatChartInfo {"充放电分析", {"电量(kWh)"}, {{"充电电量", 0}, { "放电电量", 0 }}},
StatChartInfo {"运行状态分析", {"时长(h)", "次数"}, {{"充电时长", 0}, {"放电时长", 0}, {"故障次数", 0}}},
StatChartInfo {"电压/电流分析", {"电压(V)", "电流(A)"}, {{"电压", 0}, {"电流", 0}}},
StatChartInfo {"功率分析", {"功率(kW)"}, {{"功率", 0}}}
};
static VecChartDef chartSolarDef =
{
StatChartInfo {"发电电量分析", {"电量(kWh)"}, {{"发电电量", 0}}},
StatChartInfo {"运行状态分析", {"时长(h)", "次数"}, {{"发电时长", 0}, {"故障次数", 0}}},
StatChartInfo {"电压/电流分析", {"电压(V)", "电流(A)"}, {{"电压", 0}, {"电流", 0}}},
StatChartInfo {"功率分析", {"功率(kW)"}, {{"功率", 0}}}
};
static VecChartDef chartChargeDef =
{
StatChartInfo {"充电分析", {"电量(kWh)"}, {{"充电电量", 0}}},
StatChartInfo {"运行状态分析", {"时长(h)", "次数"}, {{"充电时长", 0}, {"充电次数", 0}, {"故障次数", 0}}},
StatChartInfo {"电压/电流分析", {"电压(V)", "电流(A)"}, {{"电压", 0}, {"电流", 0}}},
StatChartInfo {"功率分析", {"功率(kW)"}, {{"功率", 0}}}
};
using VecStatDef = std::vector<std::pair<std::string, VecChartDef>>;
static VecStatDef statDef = {
{"储能设备运行分析", chartStorageDef},
{"光伏设备运行分析", chartSolarDef},
{"充电设备运行分析", chartChargeDef}
};
int MaskPageStat::initUI(EPvCode pvcode)
{
PvApp::label(p, PV_ID_MAIN, 10, 100, 1900, 850, "", QSS_LABEL_BKG_1);
if (pvcode == EPvCode::MASK_STAT) { pvcode = EPvCode::MASK_STAT_STORAGE; }
std::string curModuleName;
VecChartDef chartDef {};
for (int i = 0; i<statDef.size(); ++i)
{
std::string moduleName = statDef[i].first;
// 创建按钮
bool isActive = (PvApp::getPvCode(moduleName) == pvcode);
int pageBtn = PvApp::button(p, PV_ID_MAIN, 10+(i*190), 110, 180, 40, moduleName, isActive ? QSS_BTN_MGR_ACTIVE : QSS_BTN_MGR);
mapSubpage_[pageBtn] = moduleName;
if (isActive)
{
curModuleName = moduleName;
chartDef = statDef[i].second;
}
}
// 初始化统计图表
if (!chartDef.empty())
{
int x=10, y=160, w=467, h=300, delta = 32;
for (int i = 0; i<chartDef.size(); ++i)
{
auto& chartInfo = chartDef[i];
std::string title = chartInfo.name;
auto& vecAxis = chartInfo.vecAxis;
auto& vecItems = chartInfo.vecItems;
int panel = CreatePanel(p, 0, x+(w+10)*i, y, w, h, title);
if (i == 0)
{
chart1 = std::make_shared<PvChartBar>(p, panel, 0, delta, w, h-delta);
this->initChartBar(chart1, vecAxis, vecItems);
}
else if (i == 1)
{
chart2 = std::make_shared<PvChartBar>(p, panel, 0, delta, w, h-delta);
this->initChartBar(chart2, vecAxis, vecItems);
}
else if (i == 2)
{
chart3 = std::make_shared<PvChartCurve>(p, panel, 0, delta, w, h-delta);
chart3->setBackground(p, 8, 54, 91);
this->initChartCurve(chart3, vecAxis, vecItems);
}
else if (i == 3)
{
chart4 = std::make_shared<PvChartCurve>(p, panel, 0, delta, w, h-delta);
chart4->setBackground(p, 8, 54, 91);
this->initChartCurve(chart4, vecAxis, vecItems);
}
}
}
// 初始化表格
{
PvTable::Options option;
auto table = new PvTable(p, 0, 10, 480, 1900, 12, option);
table->addHead({"日期", "设备ID", "设备名称", "设备类型", "充电次数", "放电次数", "故障次数"});
}
//
{
}
return 0;
}
void MaskPageStat::initChartBar(std::shared_ptr<PvChartBar> chart, std::vector<std::string>& vecAxis, std::vector<std::pair<std::string, int>>& vecItems)
{
if (!chart) { return; }
for (int pos = 0; pos<vecAxis.size(); ++pos)
{
auto text = vecAxis[pos];
if (pos == 0) chart->setLabelYLeft(text);
if (pos == 1) chart->setLabelYRight(text);
}
for (int index = 0; index<vecItems.size(); ++index)
{
chart->insertItem(vecItems[index].first);
}
}
void MaskPageStat::initChartCurve(std::shared_ptr<PvChartCurve> chart, std::vector<std::string>& vecAxis, std::vector<std::pair<std::string, int>>& vecItems)
{
if (!chart) { return; }
for (int pos = 0; pos<vecAxis.size(); ++pos)
{
auto text = vecAxis[pos];
if (pos == 0) chart->setLabelYLeft(text);
if (pos == 1) chart->setLabelYRight(text);
}
for (int index = 0; index<vecItems.size(); ++index)
{
chart->insertItem(vecItems[index].second, vecItems[index].first, 100+80*index, 10, 80);
}
}
EPvCode MaskPageStat::onEventButton(int pvid)
{
std::string title;
auto iter = mapSubpage_.find(pvid);
if (iter != mapSubpage_.end())
{
title = iter->second;
return PvApp::getPvCode(title);
}
return EPvCode::NUL;
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include "pv/PvApp.h"
class PvChartBar;
class PvChartCurve;
class MaskPageStat : public PvMask
{
public:
MaskPageStat(PARAM* p);
int initUI(EPvCode pvcode);
void initChartBar(std::shared_ptr<PvChartBar> chart, std::vector<std::string>& vecAxis, std::vector<std::pair<std::string, int>>& vecItems);
void initChartCurve(std::shared_ptr<PvChartCurve> chart, std::vector<std::string>& vecAxis, std::vector<std::pair<std::string, int>>& vecItems);
EPvCode onEventButton(int pvid) override;
std::map<int, std::string> mapSubpage_;
std::shared_ptr<PvChartBar> chart1;
std::shared_ptr<PvChartBar> chart2;
std::shared_ptr<PvChartCurve> chart3;
std::shared_ptr<PvChartCurve> chart4;
};

View File

@@ -0,0 +1,355 @@
#include "MaskPageSysmgr.h"
#include "pv/PvTable.h"
#include <thread>
#include "database/Dao.h"
#include "pv/PvPopWidget.h"
static void createPvTable(PARAM* p)
{
int nRow = 20, nCol = 10;
int id = PvApp::pvid(p);
pvQTable(p, id, 0, nRow, nCol);
pvSetGeometry(p, id, 10, 200, 1900, 700);
//pvSetWhatsThis()
//pvWhatsThisPrintf()
//pvToolTip()
//pvSetGeometry()
//pvSetMinSize()
//pvSetMaxSize()
//pvMove();
//pvResize();
//pvHide();
//pvShow();
//pvSetStyleSheet()
//pvPrintfStyleSheet();
pvSetPaletteBackgroundColor(p, id, 1, 32, 54);
pvSetPaletteForegroundColor(p, id, 0, 255, 0);
//pvSetFontColor()
//pvSetFont()
//pvSetEnabled()
//pvSetFocus()
//pvCopyToClipboard()
//pvSaveAsBmp()
//pvTablePrintf()
//pvClear();
//pvSetColumnWidth()
//pvSetRowHeight()
//pvSetWordWrap()
//pvSetTablePixmap()
//pvEnsureCellVisible()
pvSetEditable(p, id, 0);
pvTableSetEnabled(p, id, 1, 0, 0);
// 设置垂直vertical方向表头不可 resize
for (int i = 0; i<nRow; ++i) { pvTableSetHeaderResizeEnabled(p, id, 0, 0, i); }
// 设置水平horizontal方向表头不可 resize
for (int i = 0; i<nCol; ++i) { pvTableSetHeaderResizeEnabled(p, id, 1, 0, i); }
pvSetTableText(p, id, -1, -1, "A");
//pvSetTableCheckBox()
//pvSetTableLabel()
pvSetTableButton(p, id, 1, 1, "HelloWorld");
pvSetTableComboBox(p, id, 1, 2, 1, "choice1,#choice2,choice3");
//pvSetNumRows()
//pvSetNumCols()
//pvInsertRows()
//pvInsertColumns()
//pvRemoveRow()
//pvRemoveColumn()
//pvSetTableTextAlignment();
//pvSave()
//pvMysqldump()
//pvCSVcreate()
//pvCSVdump()
//pvCSV()
}
class PageTable : public PvMask
{
public:
PageTable(PARAM* p) : PvMask(p)
{
table = std::make_shared<PvTable>(p, 0, 10, 160, 1900, 20, option);
table->setOperateCallback([=](int row, int col, std::string text) { this->onCallbackOperate(row, col, text); });
};
void setPage(int pageIndex, int pageSize, int count) {}
void updateDataFromDB()
{
std::vector<DataFields> result;
PageInfo pageInfo;
this->queryTable(pageInfo, result);
for (int i = 0; i<table->rows(); ++i)
{
if (i<result.size())
{
auto& fields = result[i];
table->setRowData(i, result[i]);
}
else
{
table->setRowVisible(i, false);
}
}
}
virtual void queryTable(PageInfo& pageInfo, std::vector<DataFields>& result) {}
virtual void onCallbackOperate(int row, int col, std::string text) {};
int pageIndex {0};
PvTable::Options option;
std::shared_ptr<PvTable> table;
std::shared_ptr<PvPopWidget> pop;
};
class PageUser : public PageTable
{
public:
PageUser(PARAM* p, EPvCode pvcode) :PageTable(p)
{
table->addHead(DMUser::USER_ID, "用户编号", 180, {});
table->addHead(DMUser::ACCOUNT, "用户名", 180, {});
table->addHead(DMUser::NAME, "姓名", 180, {});
table->addHead(DMUser::GENDER, "性别", 180, {{"1", ""}, {"0",""}});
table->addHead(DMUser::AGE, "年龄", 180, {});
table->addHead(DMUser::PHONE, "手机号", 180, {});
table->addHead(DMUser::EMAIL, "邮箱", 180, {});
table->addHead("role_id", "角色", 180, {});
table->addHead(DMUser::LOGINTIME, "上次登录时间", 200, {});
table->addOperate({"编辑"});
pop = std::make_shared<PvPopWidget>(p, 700, 500);
pop->show(0);
pop->setCallbackConfirm([=]() {
auto fields = pop->getData();
XLOGD() << fields.toStr();
// 保存数据:
DAO::updateUserById(fields);
});
int x = 50, y = 100, w=350, h=60;
pop->addParamLineEdit(DMUser::USER_ID, "用户编号", x, y, false);
pop->addParamCombox(DMRole::ROLE_ID, "角 色", x+w, y, {"系统管理员", "运营管理员", "运营人员"});
pop->addParamLineEdit(DMUser::ACCOUNT, "用 户 名", x, y+=h);
pop->addParamLineEdit(DMUser::NAME, "姓 名", x+w, y);
pop->addParamLineEdit(DMUser::GENDER, "性 别", x, y+=h);
pop->addParamLineEdit(DMUser::AGE, "年 龄", x+w, y);
pop->addParamLineEdit(DMUser::PHONE, "手 机 号", x, y += h);
pop->addParamLineEdit(DMUser::EMAIL, "邮 箱", x+w, y);
}
void queryTable(PageInfo& pageInfo, std::vector<DataFields>& result)
{
DAO::queryUserList(pageInfo, result);
}
void onCallbackOperate(int row, int col, std::string text)
{
if (text == "编辑")
{
pop->show(1);
pop->setTitle("编辑用户信息");
DataFields fields = table->getRowdata(row);
pop->setData(fields);
}
};
};
class PageRole : public PageTable
{
public:
PageRole(PARAM* p, EPvCode pvcode) : PageTable(p)
{
table->addHead(DMRole::ROLE_ID, "角色编号", 200, {});
table->addHead(DMRole::NAME, "角色名称", 200, {});
table->addHead(DMRole::DESCRIBE, "角色描述", 900, {});
table->addHead(DMRole::IS_OPEN, "是否启用", 200, {{"1", ""}, {"0", ""}});
table->addOperate({"编辑", "设置权限"});
}
void queryTable(PageInfo& pageInfo, std::vector<DataFields>& result)
{
DAO::queryRoleList(pageInfo, result);
}
};
class PagePermission : public PageTable
{
public:
PagePermission(PARAM* p, EPvCode pvcode) : PageTable(p)
{
table->addHead(DMPermission::PERMISSION_ID,"权限编号", 200, {});
table->addHead(DMPermission::NAME, "权限名称", 200, {});
table->addHead(DMPermission::DESCRIBE, "权限描述", 900, {});
table->addHead(DMPermission::IS_OPEN, "是否启用", 200, {{"1", ""}, {"0", ""}});
table->addOperate({"查看"});
}
void queryTable(PageInfo& pageInfo, std::vector<DataFields>& result)
{
DAO::queryPermissionList(pageInfo, result);
}
};
class PageStation : public PageTable
{
public:
PageStation(PARAM* p, EPvCode pvcode) : PageTable(p)
{
table->addHead(DMStation::STATION_ID, "场站编号", 200, {});
table->addHead(DMStation::NAME, "场站名称", 200, {});
table->addHead(DMStation::ADDRESS, "地址", 200, {});
table->addHead(DMStation::LONGITUDE, "经度", 200, {});
table->addHead(DMStation::LATITUDE, "维度", 200, {});
table->addHead(DMStation::TEL, "电话", 200, {});
table->addHead(DMStation::STATUS, "状态", 200, {{"0","未启用"}, {"1", "启用"}});
table->addOperate({"查看", "编辑"});
}
void queryTable(PageInfo& pageInfo, std::vector<DataFields>& result)
{
DAO::queryStationList(pageInfo, result);
}
};
class PageDevice: public PageTable
{
public:
PageDevice(PARAM* p, EPvCode pvcode) : PageTable(p)
{
table->addHead(DMDevice::DEVICE_ID, "设备编号", 120, {});
table->addHead(DMDevice::TYPE, "设备类型", 120, {});
table->addHead(DMDevice::NAME, "设备名称", 180, {});
table->addHead(DMDevice::CODE, "设备编码", 160, {});
table->addHead(DMDevice::MODEL, "设备型号", 160, {});
table->addHead(DMDevice::FACTORY, "厂家", 160, {});
table->addHead(DMDevice::TEL, "厂家电话", 160, {});
table->addHead(DMDevice::ATTRS, "设备参数", 460, {});
table->addHead(DMDevice::IS_OPEN, "是否启用", 120, {{"1", ""}, {"0", ""}});
table->addOperate({"查看", "编辑"});
}
void queryTable(PageInfo& pageInfo, std::vector<DataFields>& result)
{
DAO::queryDeviceList(pageInfo, result);
}
};
class PagePolicy : public PageTable
{
public:
PagePolicy(PARAM* p, EPvCode pvcode) : PageTable(p)
{
table->addHead(DMPolicy::POLICY_ID, "策略编号", 200, {});
table->addHead(DMPolicy::TYPE, "策略类型", 200, {});
table->addHead(DMPolicy::NAME, "策略名称", 200, {});
table->addHead(DMPolicy::DESCRIBE, "策略描述", 400, {});
table->addHead(DMPolicy::VALUE, "策略参数", 400, {});
table->addHead(DMPolicy::IS_OPEN, "是否启用", 200, {{"1", ""}, {"0", ""}});
table->addOperate({"查看", "编辑"});
}
void queryTable(PageInfo& pageInfo, std::vector<DataFields>& result)
{
DAO::queryPolicyList(pageInfo, result);
}
};
class PageSyslog : public PageTable
{
public:
PageSyslog(PARAM* p, EPvCode pvcode) : PageTable(p)
{
table->addHead(DMSystemLog::LOG_ID, "日志编号", 160, {});
table->addHead(DMSystemLog::TYPE, "日志类型", 160, {});
table->addHead(DMSystemLog::USER_ACCOUNT, "用户", 160, {});
table->addHead(DMSystemLog::CONTENT, "日志详情", 800, {});
table->addHead(DMSystemLog::STATUS, "状态", 160, {});
table->addHead(DMSystemLog::CREATE_TIME, "记录时间", 200, {});
table->addOperate({"查看"});
}
void queryTable(PageInfo& pageInfo, std::vector<DataFields>& result)
{
DAO::querySystemLogList(pageInfo, result);
}
};
class PageAlertlog : public PageTable
{
public:
PageAlertlog(PARAM* p, EPvCode pvcode) : PageTable(p)
{
table->addHead(DMAlertLog::LOG_ID, "日志编号", 160, {});
table->addHead(DMAlertLog::TYPE, "日志类型", 160, {});
table->addHead(DMAlertLog::DEVICE_ID, "设备ID", 160, {});
table->addHead(DMAlertLog::CONTENT, "日志详情", 800, {});
table->addHead(DMAlertLog::STATUS, "状态", 160, {});
table->addHead(DMAlertLog::CREATE_TIME, "记录时间", 200, {});
table->addOperate({"查看"});
}
void queryTable(PageInfo& pageInfo, std::vector<DataFields>& result)
{
//DAO::queryAlertLogList(pageInfo, result);
}
};
MaskPageSysmgr::MaskPageSysmgr(PARAM* p) : PvMask(p)
{
}
int MaskPageSysmgr::initUI(EPvCode pvcode)
{
PvApp::label(p, PV_ID_MAIN, 10, 100, 1900, 850, "", QSS_LABEL_BKG_1);
if (pvcode == EPvCode::MASK_SYSMGR) { pvcode = EPvCode::MASK_MGR_USER; }
static std::vector<std::string> vecPageNames = {"用户管理", "角色管理", "权限管理", "场站管理", "设备管理", "策略管理", "系统日志", "告警日志"};
for (int i = 0; i<vecPageNames.size(); ++i)
{
std::string& title = vecPageNames[i];
bool isActive = (PvApp::getPvCode(title) == pvcode);
int idPageBtn = PvApp::button(p, PV_ID_MAIN, 10+(i*110), 110, 100, 40, title, isActive ? QSS_BTN_MGR_ACTIVE : QSS_BTN_MGR);
mapSubpage_[idPageBtn] = title;
}
switch (pvcode)
{
case EPvCode::MASK_MGR_USER: { subpage_ = std::make_shared<PageUser>(p, pvcode); }
break;
case EPvCode::MASK_MGR_ROLE: { subpage_ = std::make_shared<PageRole>(p, pvcode); }
break;
case EPvCode::MASK_MGR_PERMISSION: { subpage_ = std::make_shared<PagePermission>(p, pvcode); }
break;
case EPvCode::MASK_MGR_STATION: { subpage_ = std::make_shared<PageStation>(p, pvcode); }
break;
case EPvCode::MASK_MGR_DEVICE: { subpage_ = std::make_shared<PageDevice>(p, pvcode); }
break;
case EPvCode::MASK_MGR_POLICY: { subpage_ = std::make_shared<PagePolicy>(p, pvcode); }
break;
case EPvCode::MASK_MGR_SYSLOG: { subpage_ = std::make_shared<PageSyslog>(p, pvcode); }
break;
case EPvCode::MASK_MGR_ALERTLOG: { subpage_ = std::make_shared<PageAlertlog>(p, pvcode); }
break;
default:
{
subpage_ = std::make_shared<PageUser>(p, pvcode);
} break;
}
std::thread([=]() {
if (subpage_) { subpage_->updateDataFromDB(); }
}).detach();
return 0;
}
EPvCode MaskPageSysmgr::onEventButton(int pvid)
{
std::string title;
auto iter = mapSubpage_.find(pvid);
if (iter != mapSubpage_.end())
{
title = iter->second;
return PvApp::getPvCode(title);
}
return EPvCode::NUL;
}

View File

@@ -0,0 +1,30 @@
#pragma once
#include "pv/PvApp.h"
class PageTable;
class MaskPageSysmgr : public PvMask
{
public:
MaskPageSysmgr(PARAM* p);
int initUI(EPvCode pvcode);
virtual EPvCode onEventButton(int pvid) override;
struct {
int activePage = PV_ID_NUL;
int pageUser = PV_ID_NUL;
int pageRole = PV_ID_NUL;
int pagePermission = PV_ID_NUL;
int pageStation = PV_ID_NUL;
int pageDevice = PV_ID_NUL;
int pagePolicy = PV_ID_NUL;
int pageSyslog = PV_ID_NUL;
int pageAlertlog = PV_ID_NUL;
} ui;
std::shared_ptr<PageTable> subpage_;
std::map<int, std::string> mapSubpage_;
};

112
src/pv/pvmain.cpp Normal file
View File

@@ -0,0 +1,112 @@
#include "pv/PvApp.h"
#include "pv/PvUser.h"
#include "pv/MaskMain.h"
void pvInitResource(PARAM* p)
{
pvDownloadFile(p, "assets/pv/bkg.png");
pvDownloadFile(p, "assets/pv/icon1.png");
pvDownloadFile(p, "assets/pv/icon2.png");
pvDownloadFile(p, "assets/pv/icon3.png");
pvDownloadFile(p, "assets/pv/icon4.png");
pvDownloadFile(p, "assets/pv/icon5.png");
pvDownloadFile(p, "assets/pv/bkgBox.png");
pvDownloadFile(p, "assets/pv/map.png");
pvDownloadFile(p, "assets/pv/mapMarker.png");
pvDownloadFile(p, "assets/pv/downFill.png");
}
int onPvThreadCleanup(void* ptr)
{
PARAM* p = (PARAM*)ptr;
return 0;
}
int pvMain(PARAM* p)
{
p->sleep = 100;
// 管理客户端的连接信息
PvUser pvuser;
PvApp::setPvUser(p, &pvuser);
// 客户端断开时回调
pvSetCleanup(p, onPvThreadCleanup, p);
pvInitResource(p);
EPvCode pvcode = EPvCode::NUL;
while (1)
{
// 重置控件的ID值
PvApp::reset(p);
std::shared_ptr<MaskMain> mask;
mask = std::make_shared<MaskMain>(p, pvcode);
// 超过设定数量值的控件将不会显示
pvStartDefinition(p, 1024);
if (mask)
{
mask->initUI();
}
pvQLayoutVbox(p, 0, -1);
pvEndDefinition(p);
XLOGD() << "===>>> pvidIndex=" << PvApp::pvid(p);
char event[MAX_EVENT_LENGTH];
char text[MAX_EVENT_LENGTH];
int pvid;
pvClearMessageQueue(p);
while (1)
{
pvcode = EPvCode::NUL;
pvPollEvent(p, event);
PvEvent pvEventId = static_cast<PvEvent>(pvParseEvent(event, &pvid, text));
if (mask)
{
PvApp::dispatch(p, pvEventId, pvid, text);
switch (pvEventId)
{
case NULL_EVENT: {
pvcode = mask->onEventNull(pvid);
} break;
case BUTTON_EVENT: {
pvcode = mask->onEventButton(pvid); std::cout << "EVENT: BUTTON\n";
} break;
case TEXT_EVENT: {} break;
case SLIDER_EVENT: {} break;
case CHECKBOX_EVENT: {} break;
case RADIOBUTTON_EVENT: {} break;
case GL_IDLE_EVENT: {} break;
case GL_PAINT_EVENT: {} break;
case GL_INITIALIZE_EVENT: {} break;
case GL_RESIZE_EVENT: {} break;
case TAB_EVENT: {} break;
case TABLE_CLICKED_EVENT: {} break;
case TABLE_TEXT_EVENT: {} break;
case SELECTION_EVENT: {} break;
case CLIPBOARD_EVENT: {} break;
case BUTTON_PRESSED_EVENT: {} break;
case BUTTON_RELEASED_EVENT: {} break;
case RIGHT_MOUSE_EVENT: {} break;
case KEYBOARD_EVENT: {} break;
case PLOT_MOUSE_MOVED_EVENT: {} break;
case PLOT_MOUSE_PRESSED_EVENT: {} break;
case PLOT_MOUSE_RELEASED_EVENT: {} break;
case USER_EVENT: {} break;
case MOUSE_OVER_EVENT: {} break;
default: {} break;
}
}
if (pvcode != EPvCode::NUL)
{
std::cout << "mask status: " << (int)pvcode << std::endl;
break;
}
}
}
}

View File

@@ -122,7 +122,7 @@ int MyWebHandler::insertUser(QVariantMap params)
JSgetReqParam("phone", params, fields);
JSgetReqParam("email", params, fields);
JSgetReqParam("is_open", params, fields);
XLOGD() << "[cppNative] insertUser: params=" << fields.to_str();
XLOGD() << "[cppNative] insertUser: params=" << fields.toStr();
bool ret = DAO::insertUser(fields);

View File

@@ -114,10 +114,10 @@ void QUI::combox(QComboBox& comb, QWidget* parent, int x, int y, int w, int h, s
}
}
void QUI::lineedit(QLineEdit& line, QWidget* parent, int x, int y, int w, int h)
void QUI::lineedit(QLineEdit& lineEdit, QWidget* parent, int x, int y, int w, int h)
{
line.setParent(parent);
line.setGeometry(x, y, w, h);
lineEdit.setParent(parent);
lineEdit.setGeometry(x, y, w, h);
}
void QUI::setBackground(QWidget* w, std::string name, QColor color, std::string border)

View File

@@ -56,7 +56,7 @@ public:
static void combox(QComboBox& comb, QWidget* parent, int x, int y, int w, int h, std::vector<std::string> items, std::string v="");
static void lineedit(QLineEdit& line, QWidget* parent, int x, int y, int w, int h);
static void lineedit(QLineEdit& lineEdit, QWidget* parent, int x, int y, int w, int h);
static void setBackground(QWidget* w, std::string name, QColor color=QColor(29, 54, 102), std::string border="border-radius:5px;");
};