#include "GeneralInterface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // libqrencode 相关头文件 #include #include #include #include "Globals.h" #include "ConfigManager.h" #include "LoggerHelper.h" #include "OtherSettings/log/FormLogView.h" #include "VersionManager.h" template T GeneralInterface::toInt(const QByteArray &data, int offset, bool networkOrder) { if ((offset + sizeof(T)) > static_cast(data.size())) { return 0; } T value; memcpy(&value, data.constData() + offset, sizeof(T)); if (networkOrder) { if constexpr (sizeof(T) == 2) { return qFromBigEndian(value); } else if constexpr (sizeof(T) == 4) { return qFromBigEndian(value); } else if constexpr (sizeof(T) == 8) { return qFromBigEndian(value); } } return value; } /** * @brief 将QByteArray中的数据转换为uint8_t类型 * * @param data QByteArray对象,包含要转换的数据 * @param offset 数据在QByteArray中的偏移量 * @return uint8_t 转换后的uint8_t类型数据 */ uint8_t GeneralInterface::toUInt8(const QByteArray &data, int offset) { return toInt(data, offset); } /** * @brief 将QByteArray中的数据转换为uint16_t类型 * * @param data QByteArray对象,包含要转换的数据 * @param offset 数据在QByteArray中的偏移量 * @param networkOrder 是否使用网络字节序 * @return uint16_t 转换后的uint16_t值 */ uint16_t GeneralInterface::toUInt16(const QByteArray &data, int offset, bool networkOrder) { return toInt(data, offset, networkOrder); } /** * @brief 将QByteArray中的数据转换为uint32_t类型 * * @param data QByteArray对象,包含要转换的数据 * @param offset 数据在QByteArray中的偏移量 * @param networkOrder 是否使用网络字节序 * @return uint32_t 转换后的uint32_t值 */ uint32_t GeneralInterface::toUInt32(const QByteArray &data, int offset, bool networkOrder) { return toInt(data, offset, networkOrder); } /** * @brief 将QByteArray转换为uint64_t类型 * * @param data QByteArray对象,包含要转换的数据 * @param offset 数据在QByteArray中的偏移量 * @param networkOrder 是否使用网络字节序 * * @return uint64_t 转换后的uint64_t类型数据 */ uint64_t GeneralInterface::toUInt64(const QByteArray &data, int offset, bool networkOrder) { return toInt(data, offset, networkOrder); } /** * @brief GeneralInterface::networkStructToByteArray 将MSG_HEAD结构体转换为网络字节序的字节数组 * @param data MSG_HEAD结构体 * @return QByteArray */ QByteArray GeneralInterface::networkStructToByteArray(const MSG_HEAD &data) { // 将MSG_HEAD结构体转换为网络字节序的字节数组 MSG_HEAD networkData; networkData.trans_id = qToBigEndian(data.trans_id); networkData.proto_id = qToBigEndian(data.proto_id); networkData.length = qToBigEndian(data.length); networkData.unit_id = qToBigEndian(data.unit_id); networkData.cmd = qToBigEndian(data.cmd); // 返回转换后的字节数组 return QByteArray(reinterpret_cast(&networkData), sizeof(MSG_HEAD)); } /** * @brief GeneralInterface::encodeValue 将数据编码为指定长度的字节数组 * @param data 要编码的数据 * @param length 数据的长度 * @return 编码后的字节数组 */ template QByteArray GeneralInterface::encodeValue(T &data, const uint8_t &length) { QByteArray result; auto tempData = qToBigEndian(data); result.append(0x80 + sizeof(data)); result.append(length); result.append(QByteArray(reinterpret_cast(&tempData), sizeof(T))); return result; } /** * @brief GeneralInterface::decodeValue 将字节数组解码为指定类型的数据 * @param data 字节数组 * @return 解码后的数据 */ template T GeneralInterface::decodeValue(const QByteArray &data) { return toInt(data); } /** * @brief GeneralInterface::encodeStartChargeGunInfo 将START_CHARGE_CMD结构体编码为字节数组 * @param data START_CHARGE_CMD结构体 * @return 编码后的字节数组 */ QByteArray GeneralInterface::encodeStartChargeGunInfo(const START_CHARGE_CMD &data) { QByteArray ctl_array; ctl_array.append(encodeValue(data.GUNID, 1)); ctl_array.append(0x81); ctl_array.append(sizeof(data.EMID)); ctl_array.append(QByteArray(reinterpret_cast(&data.EMID), sizeof(data.EMID))); ctl_array.append(encodeValue(data.requestKWs, 1)); ctl_array.append(encodeValue(data.requestMinutes, 1)); ctl_array.append(encodeValue(data.requestSOC, 1)); ctl_array.append(encodeValue(data.authorizationResult, 1)); return ctl_array; } /** * @brief GeneralInterface::encodeStopChargeGunInfo 将STOP_CHARGE_CMD结构体编码为字节数组 * @param data STOP_CHARGE_CMD结构体 * @return 编码后的字节数组 */ QByteArray GeneralInterface::encodeStopChargeGunInfo(const STOP_CHARGE_CMD &data) { QByteArray ctl_array; ctl_array.append(encodeValue(data.GUNID, 1)); ctl_array.append(0x81); ctl_array.append(sizeof(data.EMID)); ctl_array.append(QByteArray(reinterpret_cast(&data.EMID), sizeof(data.EMID))); ctl_array.append(encodeValue(data.authorizated, 1)); return ctl_array; } /** * @brief GeneralInterface::decodeChargeBasicInfo 将字节数组解码为charge_gun_basic_info_t结构体 * @param data 字节数组 * @return charge_gun_basic_info_t结构体 */ charge_gun_basic_info_t GeneralInterface::decodeChargeBasicInfo(const QByteArray &data) { charge_gun_basic_info_t basicInfo; int start_index = 2; basicInfo.GUNID = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); basicInfo.GUN_Error = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); basicInfo.GUN_State = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); basicInfo.GUN_Type = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); basicInfo.BOX_temp = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); basicInfo.OCPP_online = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); return basicInfo; } /** * @brief GeneralInterface::decodeChargeInfo 将字节数组解码为charge_gun_charge_info_t结构体 * @param data 字节数组 * @return charge_gun_charge_info_t结构体 */ charge_gun_charge_info_t GeneralInterface::decodeChargeInfo(const QByteArray &data) { charge_gun_charge_info_t charge_info; int start_index = 2; charge_info.GUNID = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); charge_info.transactionId = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); charge_info.SOC = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); charge_info.startUTC = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); charge_info.now_UTC = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); charge_info.startKW = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); charge_info.now_KW = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); charge_info.GUNLine_temp0 = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); charge_info.GUNLine_temp1 = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); charge_info.powerKW = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); charge_info.voltage = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); charge_info.current = decodeValue(data.mid(start_index, sizeof(uint32_t))); start_index += (sizeof(uint32_t) + 2); charge_info.rts = decodeValue(data.mid(start_index, sizeof(uint32_t))); return charge_info; } /** * @brief GeneralInterface::decodeReplyInfo 将字节数组解码为REPLY_INFO结构体 * @param data 字节数组 * @return REPLY_INFO结构体 */ REPLY_INFO GeneralInterface::decodeReplyInfo(const QByteArray& data) { REPLY_INFO info; int start_index = 2; info.result = decodeValue(data.mid(start_index, sizeof(uint32_t))); return info; } /** * @brief GeneralInterface::encodeReplyInfo 将REPLY_INFO结构体编码为字节数组 * @param info REPLY_INFO结构体 * @return 编码后的字节数组 */ QByteArray GeneralInterface::encodeReplyInfo(const REPLY_INFO& info) { QByteArray result; result.append(encodeValue(info.result, 1)); return result; } /** * @brief GeneralInterface::setCheckBoxChecked 设置widget下所有QCheckBox的checked属性 * @param widget * @param checked */ void GeneralInterface::setCheckBoxChecked(const QWidget *widget, const bool &checked) { QList checkBoxes = widget->findChildren(); if (checkBoxes.isEmpty()) return; foreach (QCheckBox* checkBox, checkBoxes) { checkBox->setChecked(checked); } } /** * @brief GeneralInterface::setTouchScroller 设置QScrollArea的滚动方式为触摸滚动 * @param area */ void GeneralInterface::setTouchScroller(QScrollArea *area) { QScroller::grabGesture(area, QScroller::TouchGesture); QScroller::grabGesture(area->viewport(), QScroller::LeftMouseButtonGesture); } /** * @brief GeneralInterface::setTouchScroller 设置QListWidget的滚动方式为触摸滚动 * @param listWidget */ void GeneralInterface::setTouchScroller(QListWidget *listWidget) { QScroller::grabGesture(listWidget, QScroller::TouchGesture); QScroller::grabGesture(listWidget->viewport(), QScroller::LeftMouseButtonGesture); } /** * @brief GeneralInterface::setTouchScroller 设置QTableWidget的滚动方式为触摸滚动 * @param tableWidget */ void GeneralInterface::setTouchScroller(QTableWidget *tableWidget) { QScroller::grabGesture(tableWidget, QScroller::TouchGesture); QScroller::grabGesture(tableWidget->viewport(), QScroller::LeftMouseButtonGesture); } /** * @brief GeneralInterface::setTouchScroller 设置QTreeWidget的滚动方式为触摸滚动 * @param treeWidget */ void GeneralInterface::setTouchScroller(QTreeWidget *treeWidget) { QScroller::grabGesture(treeWidget, QScroller::TouchGesture); QScroller::grabGesture(treeWidget->viewport(), QScroller::LeftMouseButtonGesture); } /** * @brief GeneralInterface::setTouchScroller 设置QTextEdit的滚动方式为触摸滚动 * @param textEdit */ void GeneralInterface::setTouchScroller(QTextEdit *textEdit) { QScroller::grabGesture(textEdit, QScroller::TouchGesture); QScroller::grabGesture(textEdit->viewport(), QScroller::LeftMouseButtonGesture); } /** * @brief GeneralInterface::setTouchScroller 设置QAbstractScrollArea的滚动方式为触摸滚动 * @param scrollArea */ void GeneralInterface::setTouchScroller(QAbstractScrollArea *scrollArea) { QScroller::grabGesture(scrollArea, QScroller::TouchGesture); QScroller::grabGesture(scrollArea->viewport(), QScroller::LeftMouseButtonGesture); } void GeneralInterface::setControlEnabled(QWidget *control, QWidget *label, const int &userType) { // 管理员和根用户都有控制权 const bool hasControl = (/*userType == General::UserType::ADMIN || */userType == General::UserType::ROOT); if (control) { control->setEnabled(hasControl); } if (label) { label->setEnabled(hasControl); } } void GeneralInterface::setControlVisiabled(QWidget *control, QWidget *label, const int &userType) { // 管理员和根用户都有控制权 const bool hasControl = (userType == General::UserType::ROOT); if (control) { control->setVisible(hasControl); } if (label) { label->setVisible(hasControl); } } /** * @brief GeneralInterface::setAdvancedTouchScroller 设置高级触摸滚动配置 * @param scrollArea 滚动区域 * @param properties 滚动属性 */ void GeneralInterface::setAdvancedTouchScroller(QAbstractScrollArea *scrollArea, const QScrollerProperties &properties) { // 设置基本手势 QScroller::grabGesture(scrollArea, QScroller::TouchGesture); QScroller::grabGesture(scrollArea->viewport(), QScroller::LeftMouseButtonGesture); // 应用自定义属性 QScroller *scroller = QScroller::scroller(scrollArea); if (scroller) { scroller->setScrollerProperties(properties); } } /** * @brief GeneralInterface::setTouchScrollerWithProperties 设置带属性的触摸滚动 * @param scrollArea 滚动区域 * @param enableOvershoot 是否启用过度滚动 * @param enableFlick 是否启用轻弹 * @param decelerationFactor 减速度因子 */ void GeneralInterface::setTouchScrollerWithProperties(QAbstractScrollArea *scrollArea, bool enableOvershoot, bool enableFlick, qreal decelerationFactor) { // 设置基本手势 QScroller::grabGesture(scrollArea, QScroller::TouchGesture); QScroller::grabGesture(scrollArea->viewport(), QScroller::LeftMouseButtonGesture); // 配置滚动属性 QScroller *scroller = QScroller::scroller(scrollArea); if (scroller) { QScrollerProperties properties = scroller->scrollerProperties(); // 设置过度滚动 properties.setScrollMetric(QScrollerProperties::OvershootDragResistanceFactor, enableOvershoot ? 0.5 : 0.0); properties.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, enableOvershoot ? 0.5 : 0.0); // 设置轻弹 properties.setScrollMetric(QScrollerProperties::DecelerationFactor, enableFlick ? decelerationFactor : 0.0); // 设置其他优化参数 properties.setScrollMetric(QScrollerProperties::DragVelocitySmoothingFactor, 0.5); properties.setScrollMetric(QScrollerProperties::AxisLockThreshold, 0.5); properties.setScrollMetric(QScrollerProperties::ScrollingCurve, QEasingCurve::OutCubic); scroller->setScrollerProperties(properties); } } /** * @brief GeneralInterface::setTouchScrollerForChildren 为父控件的所有子控件设置触摸滚动 * @param parent 父控件 * @param className 指定类名(可选) */ void GeneralInterface::setTouchScrollerForChildren(QWidget *parent, const QString &className) { if (!parent) return; // 查找所有可滚动的子控件 QList scrollAreas = parent->findChildren(); foreach (QAbstractScrollArea* area, scrollAreas) { if (className.isEmpty() || area->metaObject()->className() == className) { setTouchScroller(area); } } // 查找所有ListWidget QList listWidgets = parent->findChildren(); foreach (QListWidget* listWidget, listWidgets) { if (className.isEmpty() || listWidget->metaObject()->className() == className) { setTouchScroller(listWidget); } } // 查找所有TableWidget QList tableWidgets = parent->findChildren(); foreach (QTableWidget* tableWidget, tableWidgets) { if (className.isEmpty() || tableWidget->metaObject()->className() == className) { setTouchScroller(tableWidget); } } // 查找所有TreeWidget QList treeWidgets = parent->findChildren(); foreach (QTreeWidget* treeWidget, treeWidgets) { if (className.isEmpty() || treeWidget->metaObject()->className() == className) { setTouchScroller(treeWidget); } } // 查找所有TextEdit QList textEdits = parent->findChildren(); foreach (QTextEdit* textEdit, textEdits) { if (className.isEmpty() || textEdit->metaObject()->className() == className) { setTouchScroller(textEdit); } } } /** * @brief GeneralInterface::getWindowTitle 获取窗口标题 * @param type 窗口类型 * @return 窗口标题 */ QString GeneralInterface::getWindowTitle(const int &type) { switch (type) { case FormItem::GENERAL: return QCoreApplication::translate("GeneralInterface", "General Log"); case FormItem::OCPP16: return QCoreApplication::translate("GeneralInterface", "OCPP 1.6 Log"); case FormItem::SLAVE: return QCoreApplication::translate("GeneralInterface", "Lower machine log"); default: return QString(""); } } /** * @brief GeneralInterface::getLogFilePath 获取日志文件路径 * @param type 窗口类型 * @return 日志文件路径 */ QString GeneralInterface::getLogFilePath(const int &type) { switch (type) { case FormItem::GENERAL: return ConfigManager::instance()->tcu_cfg().log_config.general_log_path; case FormItem::OCPP16: return ConfigManager::instance()->tcu_cfg().log_config.ocpp16_log_path; case FormItem::SLAVE: return ConfigManager::instance()->tcu_cfg().log_config.slave_log_path; default: return QString(""); } } /** * @brief GeneralInterface::getStateDescriptions 获取状态描述 * @param code 状态码 * @return 状态描述 */ QString GeneralInterface::getStateDescriptions(uint32_t code) { switch (code) { case GUNSTATE_IDLE: return QCoreApplication::translate("GeneralInterface", "Stand By"); case GUN_PLUGOUT: return QCoreApplication::translate("GeneralInterface", "Plug Out"); case GUN_PLUGIN: return QCoreApplication::translate("GeneralInterface", "Plug In"); case GUN_STARTUP: return QCoreApplication::translate("GeneralInterface", "Start Up"); case GUN_ELOCKED: return QCoreApplication::translate("GeneralInterface", "Elocked"); case GUN_AUTHORIZED: return QCoreApplication::translate("GeneralInterface", "Authorized"); case GUN_PARAMETERS: return QCoreApplication::translate("GeneralInterface", "Parameters"); case GUN_INSULATION: return QCoreApplication::translate("GeneralInterface", "Insulation"); case GUN_PRECHARGE: return QCoreApplication::translate("GeneralInterface", "Precharge"); case GUN_CHARGING: return QCoreApplication::translate("GeneralInterface", "Charging"); case GUN_SUSPEND: return QCoreApplication::translate("GeneralInterface", "Suspend"); case GUN_STOPING: return QCoreApplication::translate("GeneralInterface", "Stop"); case GUN_UNKNOW: return QCoreApplication::translate("GeneralInterface", "Unknow"); case GUN_UNAVAILABLE: return QCoreApplication::translate("GeneralInterface", "UnAvailable"); case GUN_INOPERAVIVE: return QCoreApplication::translate("GeneralInterface", "Inoperative"); case GUN_RESERVED: return QCoreApplication::translate("GeneralInterface", "Reserved"); default: return QCoreApplication::translate("GeneralInterface", "NULL"); } } /** * @brief GeneralInterface::getGuiState 根据表单类型和状态码获取GUI状态 * @param form 表单类型 * @param state 状态码 * @return GUI状态 */ gui_state_enum GeneralInterface::getGuiState(const charge_prepare_enum &form, const int &state) { switch (form) { case PLUG_OUT: return CHARGE_GUN_AVAILABLE; case AUTHENTICATION: { if (state == 0) { int authState = getAuthShowType(ConfigManager::instance()->tcu_cfg().auth_type_enable.qr, ConfigManager::instance()->tcu_cfg().auth_type_enable.nfc, ConfigManager::instance()->tcu_cfg().auth_type_enable.pos); return getGuiStateAuthForm(authState); } else { return getGuiStateAuthForm(state); } } case CHARGE_PREPARE: return getGuiStatePrepareForm(state); case CHARGING_FORM: return getGuiStateChargingForm(state); case FINISH_FORM: return RETURN_GUN; case ERROR_FORM: return getGuiStateErrorForm(); default: return UNKOWN_STATE; } } /** * @brief GeneralInterface::getGuiStateAuthForm 根据状态码获取授权表单的GUI状态 * @param state 状态码 * @return 授权表单的GUI状态 */ gui_state_enum GeneralInterface::getGuiStateAuthForm(const int &state) { switch (state) { case AuthType::QR: return AUTHORIZATION_QR; case AuthType::CARD: return AUTHORIZATION_ID_CARD; case AuthType::QR_OR_CARD: return AUTHORIZATION_QR_ID_CARD; case AuthType::NO_CARD: return WAITING_NO_ID_CARD; case AuthType::NO_QR_OR_CARD: return WAITING_NO_ID_CARD_QR; default: return UNKOWN_STATE; } } int GeneralInterface::getAuthShowType(const bool &qrEnable, const bool &cardEnable, const bool &posEnable) { if (qrEnable && !cardEnable && !posEnable) { return AuthType::QR; } else if (qrEnable && cardEnable && !posEnable) { return AuthType::QR_OR_CARD; } else if (!qrEnable && cardEnable && !posEnable) { return AuthType::CARD; } else if (!qrEnable && !cardEnable && posEnable) { return AuthType::POS; } return -1; } /** * @brief GeneralInterface::getGuiStatePrepareForm 根据状态码获取准备表单的GUI状态 * @param state 状态码 * @return 准备表单的GUI状态 */ gui_state_enum GeneralInterface::getGuiStatePrepareForm(const int &state) { switch (state) { case 0: return RETRY_OR_RETURN_GUN; case 1: return SCAN_QR_OR_RETURN_GUN; case 2: return CARD_AUTHORIZATION_FAILED; case 3: return CHANGE_CARD; case 4: return BALANCE_NO_START; case 6: return TOP_UP_RETRY; case 7: return PREPARE_HANDSHAKING; case 8: return HANDSHAKING; case 9: return CONFIGUING; case 10: return START_CHARGING; case 11: return UNPLUG_RETRY_OR_CHANGE; default: return UNKOWN_STATE; } } /** * @brief GeneralInterface::getGuiStateChargingForm 根据状态码获取充电表单的GUI状态 * @param state 状态码 * @return 充电表单的GUI状态 */ gui_state_enum GeneralInterface::getGuiStateChargingForm(const int &state) { switch (state) { case 0: return RETURN_GUN; default: return UNKOWN_STATE; } } /** * @brief GeneralInterface::getGuiStateFinishForm 根据状态码获取完成表单的GUI状态 * @param state 状态码 * @return 完成表单的GUI状态 */ gui_state_enum GeneralInterface::getGuiStateFinishForm(const int &state) { switch (state) { case 0: return RETURN_GUN; default: return UNKOWN_STATE; } } /** * @brief GeneralInterface::getGuiStateErrorForm 根据状态码获取错误表单的GUI状态 * @param state 状态码 * @return 错误表单的GUI状态 */ gui_state_enum GeneralInterface::getGuiStateErrorForm(const int &state) { switch (state) { case 0: return RETURN_GUN; case 1: return CHARGE_GUN_UNAVILABLE; case 2: return OUT_OF_SERVICE; case 3: return MAX_POWER_LIMIT; default: return UNKOWN_STATE; } } /** * @brief GeneralInterface::getChargeProgress 根据表单类型获取充电进度 * @param form 表单类型 * @return 充电进度 */ charge_progress_enum GeneralInterface::getChargeProgress(const charge_prepare_enum &form) { switch (form) { case PLUG_OUT: return PLUG_IN_PROGRESS; case CHARGING_FORM: return CHARGING_PROGRESS; case CHARGE_PREPARE: return AUTHORIZATION_PROGRESS; case AUTHENTICATION: return AUTHORIZATION_PROGRESS; case FINISH_FORM: return FINISHED_PROGRESS; default: return UNKNOWN_PROGRESS; } } /** * @brief GeneralInterface::getChangedGunID 根据枪ID获取另一把枪的ID * @param gun_id 枪ID * @return 另一把枪的ID */ int GeneralInterface::getChangedGunID(const int &gun_id) { switch (gun_id) { case CHARGE_GUN_DEFAULT_1: return CHARGE_GUN_DEFAULT_2; case CHARGE_GUN_DEFAULT_2: return CHARGE_GUN_DEFAULT_1; default: return 0; } } /** * @brief GeneralInterface::getMaxHeight 获取widget的最大高度 * @param widget * @return widget的最大高度 */ int GeneralInterface::getMaxHeight(QWidget *widget) { if (!widget) return 0; // 获取widget在全局的底部y坐标 QPoint globalBottomLeft = widget->mapToGlobal(QPoint(0, widget->height())); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) // Qt5.10及以上推荐用QScreen QScreen *screen = widget->screen(); if (!screen) screen = QApplication::primaryScreen(); QRect available = screen->availableGeometry(); #else // Qt5.9及以下用QDesktopWidget QRect available = QApplication::desktop()->availableGeometry(widget); #endif int maxHeight = available.bottom() - globalBottomLeft.y(); return maxHeight > 0 ? maxHeight : 0; } /** * @brief GeneralInterface::initTimezoneComboBox 初始化时区下拉框 * @param combox */ void GeneralInterface::initTimezoneComboBox(CustomComboBox *combox) { QStringList timezone_list; timezone_list << "Asia/Shanghai" << "Asia/Tokyo" << "Asia/Seoul" << "Asia/Kolkata" << "Asia/Singapore" << "Asia/Bangkok" << "Asia/Kuala_Lumpur" << "Europe/London" << "Europe/Berlin" << "Europe/Paris" << "Europe/Rome" << "Europe/Madrid" << "Europe/Moscow" << "America/New_York" << "America/Los_Angeles" << "America/Toronto" << "America/Sao_Paulo" << "America/Mexico_City"; combox->addItems(timezone_list); } /** * @brief GeneralInterface::initLanguageComboBox 初始化语言下拉框 * @param combox */ void GeneralInterface::initLanguageComboBox(CustomComboBox *combox) { QStringList timezone_list; timezone_list << "Chinese (Simplified)" << "English (US)"; combox->addItems(timezone_list); } void GeneralInterface::initCurrencySymbolMap() { QMap currencySymbol; currencySymbol.insert("USD", "$"); currencySymbol.insert("EUR", "€"); currencySymbol.insert("GBP", "£"); currencySymbol.insert("JPY", "¥"); currencySymbol.insert("CNY", "¥"); currencySymbol.insert("AUD", "A$"); currencySymbol.insert("CAD", "C$"); currencySymbol.insert("CHF", "Fr"); currencySymbol.insert("HKD", "HK$"); currencySymbol.insert("SGD", "S$"); currencySymbol.insert("INR", "₹"); currencySymbol.insert("BRL", "R$"); currencySymbol.insert("RUB", "₽"); currencySymbol.insert("KRW", "₩"); currencySymbol.insert("MXN", "Mex$"); currencySymbol.insert("NZD", "NZ$"); currencySymbol.insert("SEK", "kr"); currencySymbol.insert("DKK", "kr"); currencySymbol.insert("NOK", "kr"); currencySymbol.insert("SAR", "﷼"); currencySymbol.insert("AED", "د.إ"); currencySymbol.insert("ZAR", "R"); currencySymbol.insert("TRY", "₺"); currencySymbol.insert("CHF", "Fr"); GLOBALS->setValue(Global::Keys::CURRENCY_SYMBOL_MAP, QVariant::fromValue(currencySymbol)); } QString GeneralInterface::getCurrencySymbol(const QString &str) { auto map = GLOBALS->getValue(Global::Keys::CURRENCY_SYMBOL_MAP).value>(); return QString("%1(%2)").arg(str, map.value(str)); } QString GeneralInterface::getCurrencySymbol(CustomComboBox *combox) { auto str = combox->currentText(); auto list = str.split('('); return list[0]; } void GeneralInterface::initCurrencySymbolComboBox(CustomComboBox *combox) { QStringList currencySymbol; auto map = GLOBALS->getValue(Global::Keys::CURRENCY_SYMBOL_MAP).value>(); foreach (const auto& key, map.keys()) { currencySymbol.append(QString("%1(%2)").arg(key, map.value(key))); } combox->addItems(currencySymbol); } /** * @brief GeneralInterface::clearGridLayoutChiledren 清空QGridLayout的子控件 * @param layout */ void GeneralInterface::clearGridLayoutChiledren(QGridLayout *layout) { if (!layout) return; // 删除所有子控件(widget) while (QLayoutItem* item = layout->takeAt(0)) { if (QWidget* widget = item->widget()) { layout->removeWidget(widget); widget->deleteLater(); // 安全删除 widget } else if (QLayout* childLayout = item->layout()) { clearGridLayoutChiledren(qobject_cast(childLayout)); // 递归删除子 layout } layout->removeItem(item); delete item; // 删除 QLayoutItem 本身 } for(int row = 0; row < layout->rowCount(); row ++) { layout->setRowStretch(row, 0); } layout->update(); } /** * @brief GeneralInterface::gridLayoutAddWidget 向QGridLayout中添加控件 * @param layout * @param widget */ void GeneralInterface::gridLayoutAddWidget(QGridLayout *layout, QWidget *widget, int row, int col) { if (row == -1) row = layout->rowCount(); layout->addWidget(widget, row, col); layout->setRowStretch(row, col); } /** * @brief GeneralInterface::gridLayoutAddStrtch 向QGridLayout中添加弹簧 * @param layout */ void GeneralInterface::gridLayoutAddStrtch(QGridLayout *layout) { int row = layout->rowCount(); // 在最后一行添加弹簧(spacer),让控件靠上 QSpacerItem* vSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); layout->addItem(vSpacer, row, 0, 1, layout->columnCount()); layout->setRowStretch(row, 1); } /** * @brief GeneralInterface::getChargingProfilePurposeIndex 根据充电配置目的获取索引 * @param purpose 充电配置目的 * @return */ int GeneralInterface::getChargingProfilePurposeIndex(const QString &purpose) { if (purpose == ChargingProfiles::Purpose::CHARGE_POINT_MAX_PROFILE) { return Workspace::Settings::Ocpp::ChargingProfiles::CHARGE_POINT_MAX_PROFILE; } else if (purpose == ChargingProfiles::Purpose::TX_DEFAULT_PROFILE) { return Workspace::Settings::Ocpp::ChargingProfiles::TX_DEFAULT_PROFILE; } else if (purpose == ChargingProfiles::Purpose::TX_PROFILE) { return Workspace::Settings::Ocpp::ChargingProfiles::TX_PROFILE; } return 0; } /** * @brief GeneralInterface::getChargingProfileByPurpose 根据充电配置目的获取充电配置 * @param data * @param dataList * @param purpose * @return */ bool GeneralInterface::getChargingProfileByPurpose(charging_profiles_detail_info_t &data, const QList& dataList, const QString &purpose) { QMap> stats; for (const auto& item : std::as_const(dataList)) { if (stats.contains(item.chargingProfilePurpose)) { auto& tempData = stats[item.chargingProfilePurpose]; tempData.first++; if (tempData.second.stackLevel < item.stackLevel) { tempData.second = item; } } else { stats.insert(item.chargingProfilePurpose, {1, item}); } } if (stats.contains(purpose)) { data = stats.value(purpose).second; } return stats.contains(purpose); } /** * @brief GeneralInterface::chargingProfileToMap 充电配置转map * @param dataList * @return */ QVARIANTMAP_INT_MAP GeneralInterface::chargingProfileToMap(const QList &dataList) { QVARIANTMAP_INT_MAP result; if (dataList.isEmpty()) return result; result = chargingProfileToMapByConnectorId(dataList); return result; } /** * @brief GeneralInterface::chargingProfileMapToList map转充电配置 * @param dataMap * @return */ QList GeneralInterface::chargingProfileMapToList(const QMap &dataMap) { QList result; if (dataMap.isEmpty()) return result; result = chargingProfileMapToListByConnectorId(dataMap); return result; } /** * @brief GeneralInterface::chargingProfileToMapByConnectorId 根据充电配置ID转map * @param dataList * @return */ QMap GeneralInterface::chargingProfileToMapByConnectorId(const QList &dataList) { QMap result; foreach (const auto& data, dataList) { auto map = chargingProfileToMapByPurpose(dataList, data.connectorId); result.insert(data.connectorId, QVariant::fromValue(map)); } return result; } /** * @brief GeneralInterface::chargingProfileMapToListByConnectorId map转充电配置ID * @param dataMap * @return */ QList GeneralInterface::chargingProfileMapToListByConnectorId(const QMap &dataMap) { QList result; foreach (const auto& data, dataMap) { result.append(chargingProfileMapToListByPurpose(data.value>(), dataMap.key(data))); } return result; } /** * @brief GeneralInterface::chargingProfileToMapByPurpose 根据充电配置目的转map * @param dataList * @param connectorId 充电配置ID * @return */ QMap GeneralInterface::chargingProfileToMapByPurpose(const QList &dataList, const int &connectorId) { QMap result; foreach (const auto& data, dataList) { if (data.connectorId == connectorId) { auto map = chargingProfileToMapByKind(dataList, data.connectorId, data.chargingProfilePurpose); result.insert(data.chargingProfilePurpose, QVariant::fromValue(map)); } } return result; } /** * @brief GeneralInterface::chargingProfileMapToListByPurpose map转充电配置目的 * @param dataMap * @param connectorId * @return */ QList GeneralInterface::chargingProfileMapToListByPurpose(const QMap &dataMap, const int &connectorId) { QList result; foreach (const auto& data, dataMap) { result.append(chargingProfileMapToListByKind(data.value>(), connectorId, dataMap.key(data))); } return result; } /** * @brief GeneralInterface::chargingProfileToMapByKind 根据充电配置类型转map * @param dataList * @param connectorId * @param purpose * @return */ QMap GeneralInterface::chargingProfileToMapByKind(const QList &dataList, const int &connectorId, const QString &purpose) { QMap result; foreach (const auto& data, dataList) { if (data.connectorId == connectorId && data.chargingProfilePurpose == purpose) { auto map = chargingProfileToMapByRecurrencyKind(dataList, data.connectorId, data.chargingProfilePurpose, data.chargingProfileKind); result.insert(data.chargingProfileKind, QVariant::fromValue(map)); } } return result; } /** * @brief GeneralInterface::chargingProfileMapToListByKind map转充电配置类型 * @param dataMap * @param connectorId * @param purpose * @return */ QList GeneralInterface::chargingProfileMapToListByKind(const QMap &dataMap, const int &connectorId, const QString &purpose) { QList result; foreach (const auto& data, dataMap) { result.append(chargingProfileMapToListRecurrencyKind(data.value>(), connectorId, purpose, dataMap.key(data))); } return result; } /** * @brief GeneralInterface::chargingProfileToMapByRecurrencyKind 根据充电配置周期转map * @param dataList * @param connectorId * @param purpose * @param kind * @return */ QMap GeneralInterface::chargingProfileToMapByRecurrencyKind(const QList &dataList, const int &connectorId, const QString &purpose, const QString &kind) { QMap result; foreach (const auto& data, dataList) { if (data.connectorId == connectorId && data.chargingProfilePurpose == purpose && data.chargingProfileKind == kind) { auto variantList = chargingProfileToMapByRecurrencyKind(dataList, data.connectorId, data.chargingProfilePurpose, data.chargingProfileKind, data.recurrencyKind); result.insert(data.recurrencyKind, QVariant::fromValue(variantList)); } } return result; } /** * @brief GeneralInterface::chargingProfileMapToListRecurrencyKind map转充电配置周期 * @param dataMap * @param connectorId * @param purpose * @param kind * @return */ QList GeneralInterface::chargingProfileMapToListRecurrencyKind(const QMap &dataMap, const int &connectorId, const QString &purpose, const QString &kind) { QList result; foreach (const auto& data, dataMap) { result.append(chargingProfileMapToListRecurrencyKind(data.value>(), connectorId, purpose, kind, dataMap.key(data))); } return result; } /** * @brief GeneralInterface::chargingProfileToMapByRecurrencyKind 根据充电配置栈级转map * @param dataList * @param connectorId * @param purpose * @param kind * @param recurrencyKind * @return */ QMap GeneralInterface::chargingProfileToMapByRecurrencyKind(const QList &dataList, const int &connectorId, const QString &purpose, const QString &kind, const QString &recurrencyKind) { QMap result; foreach (const auto& data, dataList) { if (data.connectorId == connectorId && data.chargingProfilePurpose == purpose && data.chargingProfileKind == kind && data.recurrencyKind == recurrencyKind) { auto map = chargingProfileToMapByStackLevel(dataList, data.connectorId, data.chargingProfilePurpose, data.chargingProfileKind, data.recurrencyKind, data.stackLevel); result.insert(data.stackLevel, QVariant::fromValue(map)); } } return result; } /** * @brief GeneralInterface::chargingProfileMapToListRecurrencyKind map转充电配置栈级 * @param dataMap * @param connectorId * @param purpose * @param kind * @param recurrencyKind * @return */ QList GeneralInterface::chargingProfileMapToListRecurrencyKind(const QMap &dataMap, const int &connectorId, const QString &purpose, const QString &kind, const QString &recurrencyKind) { QList result; foreach (const auto& data, dataMap) { result.append(chargingProfileMapToListByStackLevel(data.value(), connectorId, purpose, kind, recurrencyKind, dataMap.key(data))); } return result; } /** * @brief GeneralInterface::chargingProfileToMapByStackLevel 根据充电配置栈级转map * @param dataList * @param connectorId * @param purpose * @param kind * @param recurrencyKind * @param stackLevel * @return */ QVariantList GeneralInterface::chargingProfileToMapByStackLevel(const QList &dataList, const int &connectorId, const QString &purpose, const QString &kind, const QString &recurrencyKind, const int &stackLevel) { QVariantList result; foreach (const auto& data, dataList) { if (data.connectorId == connectorId && data.chargingProfilePurpose == purpose && data.chargingProfileKind == kind && data.recurrencyKind == recurrencyKind && data.stackLevel == stackLevel) { result.append(QVariant::fromValue(data)); } } return result; } /** * @brief GeneralInterface::chargingProfileMapToListByStackLevel map转充电配置栈级 * @param dataMap * @param connectorId * @param purpose * @param kind * @param recurrencyKind * @param stackLevel * @return */ QList GeneralInterface::chargingProfileMapToListByStackLevel(const QVariantList &dataMap, const int &connectorId, const QString &purpose, const QString &kind, const QString &recurrencyKind, const int &stackLevel) { QList result; foreach (const auto& data, dataMap) { if (data.value().connectorId == connectorId && data.value().stackLevel == stackLevel && data.value().chargingProfilePurpose == purpose && data.value().chargingProfileKind == kind && data.value().recurrencyKind == recurrencyKind) { result.append(data.value()); } } return result; } /** * @brief GeneralInterface::getMaxElecment 获取最大值 * @param list * @return list为空返回-1,否则返回最大值 */ int GeneralInterface::getMaxElecment(const QList &list) { int result = -1; if (list.isEmpty()) return result; for (const auto& num : list) { if (num > result) result = num; } return result; } uint32_t GeneralInterface::getMaxElecment(const QList &list) { uint32_t result = 0; if (list.isEmpty()) return result; for (const auto& num : list) { if (num > result) result = num; } return result; } uint32_t GeneralInterface::getMaxErrorCode(const ERROR_NOTE_LIST &info) { QList error_code_list; foreach (const auto& data, info) { error_code_list.append(data.error_code); } return getMaxElecment(error_code_list); } /** * @brief GeneralInterface::getCurrentDate 获取当前时间 * @param seconds 秒数 * @return 返回"HH:mm:ss"的字符串 */ QString GeneralInterface::getCurrentDate(const int &seconds) { QDate date = QDate::currentDate(); QTime time = QTime(0, 0, 0).addSecs(seconds); QDateTime dt(date, time); return dt.toString("HH:mm:ss"); } /** * @brief GeneralInterface::getCurrentDate 获取当前时间 * @param time 时间字符串 * @return 返回秒数 */ int GeneralInterface::getCurrentDate(const QString &time) { QTime dt = QTime::fromString(time, "HH:mm:ss"); return QTime(0, 0, 0).secsTo(dt); } /** * @brief GeneralInterface::getCurrentDateOfWeek 获取当前周时间 * @param startTime 开始时间 * @param seconds 秒数 * @return 返回"HH:mm:ss"的字符串 */ QString GeneralInterface::getCurrentDateOfWeek(const QString &startTime, const int &seconds) { QDateTime dt = QDateTime::fromString(startTime, Qt::ISODate); dt.setTimeSpec(Qt::UTC); dt = dt.addSecs(seconds); return dt.toString("HH:mm:ss"); } /** * @brief GeneralInterface::getCurrentDateSecondOfWeek 获取当前周时间 * @param startTime 开始时间 * @param seconds 秒数 * @return 返回秒数 */ int GeneralInterface::getCurrentDateSecondOfWeek(const QString &startTime, const int &seconds) { // 1. 解析开始时间 QDateTime startDt = QDateTime::fromString(startTime, Qt::ISODate); startDt.setTimeSpec(Qt::UTC); // 2. 计算目标时间 QDateTime targetDt = startDt.addSecs(seconds); // 3. 获取当天00:00:00到目标时间的秒数 int secondsOfDay = QTime(0, 0, 0).secsTo(targetDt.time()); return secondsOfDay; } /** * @brief GeneralInterface::getCurrentDateOfWeek 获取当前周时间 * @param startTime 开始时间 * @param seconds 秒数 * @param dayOfWeek 周几 * @return 返回秒数 */ int GeneralInterface::getCurrentDateOfWeek(const QString &startTime, const int &seconds, const int &dayOfWeek) { // 1. 解析开始时间 QDateTime startDt = QDateTime::fromString(startTime, Qt::ISODate); startDt.setTimeSpec(Qt::UTC); // 2. 获取开始时间所在周的周一 QDate startDate = startDt.date(); int startDayOfWeek = startDate.dayOfWeek(); // 1=周一 QDate monday = startDate.addDays(1 - startDayOfWeek); // 3. 目标日期 = 本周的 dayOfWeek QDate targetDate = monday.addDays(dayOfWeek - 1); // 4. 用秒数构造 QTime QTime targetTime = QTime(0, 0, 0).addSecs(seconds); // 5. 构造目标 QDateTime QDateTime targetDt(targetDate, targetTime, Qt::UTC); // 6. 计算秒数差 qint64 secondsDiff = startDt.secsTo(targetDt); return static_cast(secondsDiff); } /** * @brief GeneralInterface::getCurrentDateOfWeek 获取当前周时间 * @param startTime 开始时间 * @param time 时间字符串 * @param dayOfWeek 周几 * @return */ int GeneralInterface::getCurrentDateOfWeek(const QString &startTime, const QString &time, const int& dayOfWeek) { // 1. 解析开始时间 QDateTime startDt = QDateTime::fromString(startTime, Qt::ISODate); startDt.setTimeSpec(Qt::UTC); // 若有Z,确保为UTC // 2. 获取开始时间所在周的周一 QDate startDate = startDt.date(); int startDayOfWeek = startDate.dayOfWeek(); // 1=周一 QDate monday = startDate.addDays(1 - startDayOfWeek); // 3. 目标日期 = 本周的 dayOfWeek QDate targetDate = monday.addDays(dayOfWeek - 1); // 4. 解析当天时间 QTime targetTime = QTime::fromString(time, "HH:mm:ss"); // 5. 构造目标 QDateTime QDateTime targetDt(targetDate, targetTime, Qt::UTC); // 6. 计算秒数差 qint64 secondsDiff = startDt.secsTo(targetDt); return static_cast(secondsDiff); } /** * @brief GeneralInterface::getDayOfWeek 获取当前周几 * @param time 时间字符串 * @return 1=周一、2=周二、3=周三、4=周四、5=周五、6=周六、7=周日 */ int GeneralInterface::getDayOfWeek(const QString &time) { QDateTime dt = QDateTime::fromString(time, Qt::ISODate); dt.setTimeSpec(Qt::UTC); // 明确为UTC时间 QDate date = dt.date(); return date.dayOfWeek(); } /** * @brief GeneralInterface::sortByStartPeriod 按开始时间排序 * @param list */ void GeneralInterface::sortByStartPeriod(QList &list) { std::sort(list.begin(), list.end(), [](const charging_schedule_period_info_t &a, const charging_schedule_period_info_t &b) { return a.startPeriod < b.startPeriod; }); } /** * @brief GeneralInterface::sortByStackLevel 按优先级排序 * @param list */ void GeneralInterface::sortByStackLevel(QList &list) { std::sort(list.begin(), list.end(), [](const charging_profiles_detail_info_t &a, const charging_profiles_detail_info_t &b) { return a.stackLevel > b.stackLevel; }); } /** * @brief GeneralInterface::getMaxChargingProfileId 获取最大充电配置ID * @param list * @return list为空返回-1,否则返回最大值 */ int GeneralInterface::getMaxChargingProfileId(QList &list) { QList chargingProfileIdList; foreach (const auto& data, list) { chargingProfileIdList.append(data.chargingProfileId); } return getMaxElecment(chargingProfileIdList); } /** * @brief GeneralInterface::getVersion 获取版本号 * @param appName * @return * HW_NAME: 硬件版本号 * CCU_NAME: CCU版本号 * TCU_NAME: TCU版本号 * PCU_NAME: PCU版本号 */ QString GeneralInterface::getVersion(const QString &appName) { QString version; if (appName == AppName::HW_NAME) { version = VersionManager::getHardwareVersion(); } else if (appName == AppName::CCU_NAME || appName == AppName::TCU_NAME || appName == AppName::PCU_NAME) { AppVersionInfo info = VersionManager::getAppVersion(appName); if (info.success) { version = QString("V%1.%2.%3").arg(info.major).arg(info.minor).arg(info.build); } } return version; } static char app_version[128] = {0}; static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; char *GeneralInterface::formatAppVersion() { int i; int month = 0, mday, year, hour, min, sec; #define ASCII_CODE(x) ((x)==32? 0:((x)-'0')) mday = ASCII_CODE(__DATE__[4])*10 + ASCII_CODE(__DATE__[5]); year = ASCII_CODE(__DATE__[7])*1000 + ASCII_CODE(__DATE__[8])*100 + ASCII_CODE(__DATE__[9])*10 + ASCII_CODE(__DATE__[10]); hour = ASCII_CODE(__TIME__[0])*10 + ASCII_CODE(__TIME__[1]); min = ASCII_CODE(__TIME__[3])*10 + ASCII_CODE(__TIME__[4]); sec = ASCII_CODE(__TIME__[6])*10 + ASCII_CODE(__TIME__[7]); #undef ASCII_CODE for (i = 0; i < 12; i++) { if (strncmp(__DATE__, months[i], 3)==0) { month = i + 1; break; } } snprintf(app_version, 128, "app_TCU_V1R0B%04d%02d%02d %02d:%02d:%02d", year, month, mday, hour, min, sec); return app_version; } void GeneralInterface::initGlobals() { // 初始化充电枪基本信息 for (int i = CHARGE_GUN_DEFAULT_1; i <= CHARGE_GUN_DEFAULT_2; i++) { BASIC_INFO basic_info; GLOBALS->setValue(i == CHARGE_GUN_DEFAULT_1 ? Global::Keys::GUN_BASIC_INFO_1 : Global::Keys::GUN_BASIC_INFO_2, QVariant::fromValue(basic_info)); CHARGING_INFO charging_info; GLOBALS->setValue(i == CHARGE_GUN_DEFAULT_1 ? Global::Keys::CHARGING_INFO_1 : Global::Keys::CHARGING_INFO_2, QVariant::fromValue(charging_info)); UINT32_QSTRING_MAP gun_error; GLOBALS->setValue(i == CHARGE_GUN_DEFAULT_1 ? Global::Keys::GUN_ERROR_1 : Global::Keys::GUN_ERROR_2, QVariant::fromValue(gun_error)); UINT32_QSTRING_MAP gun_state; GLOBALS->setValue(i == CHARGE_GUN_DEFAULT_1 ? Global::Keys::GUN_STATE_1 : Global::Keys::GUN_STATE_2, QVariant::fromValue(gun_state)); UINT8_QSTRING_MAP gun_type; GLOBALS->setValue(i == CHARGE_GUN_DEFAULT_1 ? Global::Keys::GUN_TYPE_1 : Global::Keys::GUN_TYPE_2, QVariant::fromValue(gun_type)); } // 初始化其他全局变量 GLOBALS->setValue(Global::Keys::AUTH_LIST, QVariant::fromValue(AUTH_LIST())); GLOBALS->setValue(Global::Keys::CCU_CFG, QVariant::fromValue(CCU_CFG())); GLOBALS->setValue(Global::Keys::CHARGING_PROFILES, QVariant::fromValue(CHARGING_PROFILES())); GLOBALS->setValue(Global::Keys::CHARGING_PROFILES_MAP, QVariant::fromValue(QVARIANTMAP_INT_MAP())); GLOBALS->setValue(Global::Keys::CFG_KEY, QVariant::fromValue(CONFIG_KEY_INFO())); GLOBALS->setValue(Global::Keys::OFFLINE_CHARGING_RECORDS, QVariant::fromValue(CHARGING_RECORDS_INFO())); GLOBALS->setValue(Global::Keys::TCU_CFG, QVariant::fromValue(TCU_CFG())); GLOBALS->setValue(Global::Keys::PCU_CFG, QVariant::fromValue(PCU_CFG())); GLOBALS->setValue(Global::Keys::PRICEINFO, QVariant::fromValue(PRICE_INFO())); VERSION version_info; version_info.ccu_version = GeneralInterface::getVersion(AppName::CCU_NAME); version_info.tcu_version = GeneralInterface::getVersion(AppName::TCU_NAME); version_info.pcu_version = GeneralInterface::getVersion(AppName::PCU_NAME); version_info.hw_version = GeneralInterface::getVersion(AppName::HW_NAME); GLOBALS->setValue(Global::Keys::VERSION, QVariant::fromValue(version_info)); } int GeneralInterface::readLoadStyleFile(const QString &filapath) { QString styleSheetPath = filapath; // 检查资源文件是否存在 QFile file; file.setFileName(styleSheetPath); if (!file.exists()) { LOG_ERROR(QString("Style file not found: %1").arg(styleSheetPath)); return INIT_GUI_STYLE_LOAD_ERROR; } // 安全打开文件 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { LOG_ERROR(QString("Failed to open style file: %1, error: %2").arg( styleSheetPath, file.errorString())); return INIT_GUI_STYLE_LOAD_ERROR; } // 安全读取文件内容 QByteArray content; try { // 设置文件读取缓冲区大小限制,防止大文件导致内存问题 const qint64 maxSize = 2 * 1024 * 1024; // 2MB // 先检查文件大小 qint64 fileSize = file.size(); if (fileSize > maxSize) { LOG_WARNING(QString("Style file is large: %1 bytes, will limit to %2 bytes").arg( fileSize, maxSize)); } // 读取文件内容 content = file.read(maxSize); if (content.isEmpty() && !file.atEnd()) { LOG_ERROR("Failed to read style file content"); file.close(); return INIT_GUI_STYLE_LOAD_ERROR; } // 确保文件正确关闭 file.close(); // 检查内容是否有效 if (content.isEmpty()) { LOG_WARNING("Style file is empty, skipping"); return INIT_OK; // 允许程序继续运行 } // 安全地设置样式表 QString styleSheet = QString::fromUtf8(content); // 验证样式表语法是否有效(简单检查) if (styleSheet.count('{') != styleSheet.count('}')) { LOG_WARNING("Style sheet may have syntax errors, mismatched braces"); } // 应用样式表 - 确保qApp有效 if (qApp) { qApp->setStyleSheet(styleSheet); LOG_INFO("Style sheet loaded successfully"); } else { LOG_ERROR("QApplication instance not available when setting style sheet"); return INIT_GUI_STYLE_LOAD_ERROR; } } catch (const std::exception& e) { // 捕获可能的异常 LOG_ERROR(QString("Exception while loading style sheet: %1").arg(e.what())); file.close(); return INIT_GUI_STYLE_LOAD_ERROR; } catch (...) { LOG_ERROR("Unknown exception while loading style sheet"); file.close(); return INIT_GUI_STYLE_LOAD_ERROR; } return INIT_OK; } // ==================== 二维码生成相关方法 ==================== /** * @brief 使用 libqrencode 生成二维码 * @param text 要编码的文本内容(通常是URL) * @param size 二维码图片的尺寸(像素) * @param margin 二维码边距(模块数) * @param errorCorrectionLevel 错误纠正级别 (L=7%, M=15%, Q=25%, H=30%) * @return 生成的二维码图片,失败时返回空图片 */ QPixmap GeneralInterface::generateQRCode(const QString& text, int size, int margin, char errorCorrectionLevel) { if (text.isEmpty()) { return QPixmap(); } // 将 QString 转换为 UTF-8 字节数组 QByteArray textData = text.toUtf8(); // 设置错误纠正级别 QRecLevel level = QR_ECLEVEL_M; // 默认使用 M 级别 switch (errorCorrectionLevel) { case 'L': level = QR_ECLEVEL_L; break; case 'M': level = QR_ECLEVEL_M; break; case 'Q': level = QR_ECLEVEL_Q; break; case 'H': level = QR_ECLEVEL_H; break; default: level = QR_ECLEVEL_M; break; } // 生成二维码数据 QRcode* qrcode = QRcode_encodeString(textData.constData(), 0, level, QR_MODE_8, 1); if (!qrcode) { return QPixmap(); } // 计算实际尺寸(包含边距) int qrSize = qrcode->width; int totalSize = qrSize + 2 * margin; // 创建图像 QImage image(totalSize, totalSize, QImage::Format_ARGB32); image.fill(Qt::white); // 绘制二维码 QPainter painter(&image); painter.setPen(Qt::black); painter.setBrush(Qt::black); // 计算每个模块的像素大小 int moduleSize = size / totalSize; if (moduleSize < 1) moduleSize = 1; // 绘制二维码模块 for (int y = 0; y < qrSize; y++) { for (int x = 0; x < qrSize; x++) { if (qrcode->data[y * qrSize + x] & 1) { int pixelX = (x + margin) * moduleSize; int pixelY = (y + margin) * moduleSize; painter.fillRect(pixelX, pixelY, moduleSize, moduleSize, Qt::black); } } } // 释放二维码数据 QRcode_free(qrcode); // 转换为 QPixmap QPixmap pixmap = QPixmap::fromImage(image); // 如果指定尺寸与计算尺寸不同,进行缩放 if (pixmap.width() != size) { pixmap = pixmap.scaled(size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation); } return pixmap; } QPixmap GeneralInterface::generateQRCodeDefault(const QString &text, int size) { QRcode *qr = QRcode_encodeString(text.toUtf8().constData(), 0, QR_ECLEVEL_L, QR_MODE_8, 1); if (!qr) return QPixmap(); // 计算缩放比例(二维码实际大小可能小于请求的 size) int qrSize = qr->width > 0 ? qr->width : 1; int scale = size / qrSize; if (scale < 1) scale = 1; // 创建 QImage 并绘制二维码 QImage image(qrSize * scale, qrSize * scale, QImage::Format_ARGB32); image.fill(QColor(Qt::transparent)); QPainter painter(&image); painter.setPen(Qt::black); for (int y = 0; y < qrSize; y++) { for (int x = 0; x < qrSize; x++) { if (qr->data[y * qrSize + x] & 0x01) { painter.fillRect(x * scale, y * scale, scale, scale, Qt::black); } } } QRcode_free(qr); return QPixmap::fromImage(image); } QPixmap GeneralInterface::generateQRCodeWithIcon(const QString& text, int size, const QString& iconPath, int iconSize) { // 生成基础二维码 QPixmap qrCode = generateQRCode(text, size); if (qrCode.isNull()) { return QPixmap(); } // 如果未提供图标路径,直接返回基础二维码 if (iconPath.isEmpty()) { return qrCode; } // 加载图标 QPixmap icon(iconPath); if (icon.isNull()) { return qrCode; // 图标加载失败,返回基础二维码 } // 确定图标大小 int finalIconSize = iconSize; if (finalIconSize <= 0) { // 默认图标大小为二维码尺寸的1/4 finalIconSize = size / 4; } // 缩放图标 icon = icon.scaled(finalIconSize, finalIconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); // 在二维码中心绘制图标 QPainter painter(&qrCode); int iconX = (qrCode.width() - icon.width()) / 2; int iconY = (qrCode.height() - icon.height()) / 2; // 绘制一个白色背景,确保图标清晰可见 painter.fillRect(iconX - 2, iconY - 2, icon.width() + 4, icon.height() + 4, Qt::white); painter.drawPixmap(iconX, iconY, icon); return qrCode; } /** * @brief 生成二维码并保存到文件 * @param text 要编码的文本内容 * @param filePath 保存文件路径 * @param size 二维码图片的尺寸(像素) * @param margin 二维码边距(模块数) * @param errorCorrectionLevel 错误纠正级别 * @return 是否保存成功 */ bool GeneralInterface::generateQRCodeToFile(const QString& text, const QString& filePath, int size, int margin, char errorCorrectionLevel) { QPixmap qrCode = generateQRCode(text, size, margin, errorCorrectionLevel); if (qrCode.isNull()) { return false; } // 根据文件扩展名确定保存格式 QString extension = filePath.split('.').last().toLower(); QString format = "PNG"; // 默认格式 if (extension == "jpg" || extension == "jpeg") { format = "JPEG"; } else if (extension == "bmp") { format = "BMP"; } return qrCode.save(filePath, format.toUtf8().constData()); } /** * @brief 生成二维码的字节数组(用于网络传输) * @param text 要编码的文本内容 * @param size 二维码图片的尺寸(像素) * @param margin 二维码边距(模块数) * @param errorCorrectionLevel 错误纠正级别 * @return 二维码图片的字节数组 */ QByteArray GeneralInterface::generateQRCodeBytes(const QString& text, int size, int margin, char errorCorrectionLevel) { QPixmap qrCode = generateQRCode(text, size, margin, errorCorrectionLevel); if (qrCode.isNull()) { return QByteArray(); } // 将 QPixmap 转换为字节数组 QBuffer buffer; buffer.open(QIODevice::WriteOnly); if (qrCode.save(&buffer, "PNG")) { return buffer.data(); } return QByteArray(); } /** * @brief GeneralInterface::formatMillisecondDifference 格式化毫秒差 * @param ms1 * @param ms2 * @return */ QString GeneralInterface::formatMillisecondDifference(qint64 ms1, qint64 ms2) { qint64 diffMs = qAbs(ms1 - ms2); // 取绝对值 QTime time(0, 0); time = time.addMSecs(diffMs); return time.toString("HH:mm:ss"); } /** * @brief GeneralInterface::makeLabelJump 使标签跳一下 * @param label */ void GeneralInterface::makeLabelJump(QLabel* label) { // 创建动画组 QParallelAnimationGroup *group = new QParallelAnimationGroup(label); // 位置动画 QPropertyAnimation *posAnim = new QPropertyAnimation(label, "pos", group); posAnim->setDuration(800); posAnim->setEasingCurve(QEasingCurve::OutQuad); posAnim->setKeyValueAt(0, label->pos()); posAnim->setKeyValueAt(0.5, label->pos() + QPoint(0, -80)); posAnim->setKeyValueAt(1, label->pos()); // 缩放动画 QPropertyAnimation *scaleAnim = new QPropertyAnimation(label, "geometry", group); scaleAnim->setDuration(800); scaleAnim->setEasingCurve(QEasingCurve::OutQuad); QRect startRect = label->geometry(); scaleAnim->setKeyValueAt(0, startRect); scaleAnim->setKeyValueAt(0.5, QRect( startRect.x() - 10, startRect.y() - 80, startRect.width() + 20, startRect.height() + 20 )); scaleAnim->setKeyValueAt(1, startRect); // 启动动画 group->start(QAbstractAnimation::DeleteWhenStopped); } /** * @brief GeneralInterface::encodeAuthInfo 编码认证信息 * @param data * @return */ QByteArray GeneralInterface::encodeAuthInfo(const GUN_AUTH &data) { QByteArray result; result.append(encodeValue(data.GUNID, 1)); result.append(0x81); result.append(sizeof(data.EMID)); result.append(QByteArray(reinterpret_cast(&data.EMID), sizeof(data.EMID))); result.append(encodeValue(data.requestKWs, 1)); return result; } /** * @brief GeneralInterface::getGunIconUrl 获取枪口图标 * @param type 枪口类型 * @param isError 是否错误 * @return 返回枪口图标URL */ QString GeneralInterface::getGunIconUrl(const uint8_t &type, bool isError) { QString icon_url = ""; switch (type) { case GUNTYPE_NACS: icon_url = QString(":/icons/%1.png").arg("nacs" + QString("%1").arg(isError ? "_faild" : "")); break; case GUNTYPE_CCS1: icon_url = QString(":/icons/%1.png").arg("ccs1" + QString("%1").arg(isError ? "_faild" : "")); break; case GUNTYPE_CCS2: icon_url = QString(":/icons/%1.png").arg("ccs2" + QString("%1").arg(isError ? "_faild" : "")); break; case GUNTYPE_CHADEMO: icon_url = QString(":/icons/%1.png").arg("chademo" + QString("%1").arg(isError ? "_faild" : "")); break; case GUNTYPE_GBDC: icon_url = QString(":/icons/%1.png").arg("gbdc" + QString("%1").arg(isError ? "_faild" : "")); break; default: break; } return icon_url; } QByteArray GeneralInterface::encodeUpdateConfigInfo(const UPDATE_CONFIG &data) { QByteArray result; result.append(0x81); result.append(sizeof(data.config_filename)); result.append(QByteArray(reinterpret_cast(&data.config_filename), sizeof(data.config_filename))); return result; } /** * @brief GeneralInterface::isError 判断是否有错误 * @param error_list 错误列表 * @return 错误返回true,否则返回false */ bool GeneralInterface::isError(const QList &error_list) { return std::any_of(error_list.begin(), error_list.end(), [](uint32_t value) { return (value > CHARGE_GUN_ERROR_MAX_INDEX); }); } /** * @brief GeneralInterface::getMaxElement 获取最大值 * @param state_list 状态列表 * @return */ uint8_t GeneralInterface::getMaxElement(const QList &state_list) { if (state_list.isEmpty()) { return 0; // 或者抛出异常 } auto it = std::max_element(state_list.begin(), state_list.end()); return *it; } /** * @brief GeneralInterface::addRotationAnimation 添加旋转动画 * @param label */ void GeneralInterface::addRotationAnimation(QLabel *label) { // 创建图形场景和代理 QGraphicsScene *scene = new QGraphicsScene(label->parentWidget()); QGraphicsProxyWidget *proxy = scene->addWidget(label); // 设置旋转中心为标签中心 proxy->setTransformOriginPoint(proxy->boundingRect().center()); // 创建旋转动画 QPropertyAnimation *animation = new QPropertyAnimation(label, "rotation"); animation->setDuration(2000); // 动画持续时间(毫秒) animation->setStartValue(0); // 起始角度 animation->setEndValue(360); // 结束角度(360度) animation->setLoopCount(-1); // 无限循环 animation->start(); } /** * @brief GeneralInterface::getMaxElementWithQMax 获取最大值 * @param list * @return */ uint8_t GeneralInterface::getMaxElementWithQMax(const QList &list) { if (list.isEmpty()) { return 0; } int max = list.first(); for (int num : list) { max = qMax(max, num); } return max; } /** * @brief GeneralInterface::getTips 获取提示信息 * @param state 状态码 * @return */ QString GeneralInterface::getTips(const int &state) { switch (state) { case CHARGE_GUN_AVAILABLE: return QCoreApplication::translate("GeneralInterface", "Please Plug in"); case AUTHORIZATION_QR: return QCoreApplication::translate("GeneralInterface", "Please scan the QR code"); case AUTHORIZATION_ID_CARD: return QCoreApplication::translate("GeneralInterface", "Please swipe the card"); case AUTHORIZATION_QR_ID_CARD: return QCoreApplication::translate("GeneralInterface", "Please swipe the card or scan the QR code"); case WAITING_NO_ID_CARD: return QCoreApplication::translate("GeneralInterface", "Do not swipe card"); case WAITING_NO_ID_CARD_QR: return QCoreApplication::translate("GeneralInterface", "Do not swipe card; QR code scan works normally"); case WAITING_STANDBY: return QCoreApplication::translate("GeneralInterface", "waiting"); case AUTHORIZING_ONLINE: return QCoreApplication::translate("GeneralInterface", "Authorizing online"); case RETRY: return QCoreApplication::translate("GeneralInterface", "Retry"); case TIMEOUT: return QCoreApplication::translate("GeneralInterface", "Timeout"); case RETRY_OR_RETURN_GUN: return QCoreApplication::translate("GeneralInterface", "Please try again or return the connector"); case SCAN_QR_OR_RETURN_GUN: return QCoreApplication::translate("GeneralInterface", "Scan the QR code or return the connector"); case FAILED: return QCoreApplication::translate("GeneralInterface", "Failed"); case CARD_AUTHORIZATION_FAILED: return QCoreApplication::translate("GeneralInterface", "Charging authorization failed"); case CHANGE_CARD: return QCoreApplication::translate("GeneralInterface", "Please use another card or return the connector"); case BALANCE_NO_START: return QCoreApplication::translate("GeneralInterface", "Balance:$0x01, insufficient to start charging"); case TOP_UP_RETRY: return QCoreApplication::translate("GeneralInterface", "Please top up the funds then try again"); case PREPARE_HANDSHAKING: return QCoreApplication::translate("GeneralInterface", "Preparing Handshaking"); case HANDSHAKING: return QCoreApplication::translate("GeneralInterface", "Handshaking"); case CONFIGUING: return QCoreApplication::translate("GeneralInterface", "Configuring"); case START_CHARGING: return QCoreApplication::translate("GeneralInterface", "Start Charging"); case STARTUP_FAILED: return QCoreApplication::translate("GeneralInterface", "Startup Failed"); case UNPLUG_RETRY_OR_CHANGE: return QCoreApplication::translate("GeneralInterface", "Please try unplug the connector and reconnect it to the vehicle or use another connector"); case STOP_CHARGING: return QCoreApplication::translate("GeneralInterface", "STOP"); case TIME_REMAINING: return QCoreApplication::translate("GeneralInterface", "mins remaining"); case RETURN_GUN: return QCoreApplication::translate("GeneralInterface", "Please return the connector"); case RESTART_CHARGING: return QCoreApplication::translate("GeneralInterface", "Restart"); case CHARGING_STOP_ABNORMAL: return QCoreApplication::translate("GeneralInterface", ""); case UNAVAILABLE: return QCoreApplication::translate("GeneralInterface", "Unavailable"); case CHARGE_GUN_UNAVILABLE: return QCoreApplication::translate("GeneralInterface", "This connector is currently unavailable. Please use another one."); case OUT_OF_SERVICE: return QCoreApplication::translate("GeneralInterface", "This charging station is in out-of-service hours and unable to provide charging services."); case MAX_POWER_LIMIT: return QCoreApplication::translate("GeneralInterface", "This charging station has reached its maximum power limit and cannot be activated for charging."); default: return QCoreApplication::translate("GeneralInterface", ""); } } /** * @brief GeneralInterface::isCrossTime 判断时间是否交叉 * @param start 开始时间 * @param end 结束时间 * @return 交叉返回 true,否则返回 false */ bool GeneralInterface::isCrossTime(const uint32_t &start, const uint32_t &end) { return start > end; } /** * @brief GeneralInterface::getAuthResult 获取授权结果 * @param cardId 卡号 * @return 卡号存在且状态为无效且过期则返回 0,否则返回 1 */ int GeneralInterface::getAuthResult(const QByteArray &cardId) { auto auth_list = GLOBALS->getValue(Global::Keys::AUTH_LIST).value(); auto cfg_key = GLOBALS->getValue(Global::Keys::CFG_KEY).value(); int result = 1; if (cfg_key.CoreProfile.AuthorizationCacheEnabled) { // auth list cache foreach (const auto& value, auth_list.authorizationCache) { if (cardId.data() == value.idTag && value.status == "Invalid" && isTimeout(value.expiryDate)) { result = 0; } } } if (cfg_key.LocalAuthListManagementProfile.LocalAuthListEnabled) { // local auth list foreach (const auto& value, auth_list.localAuthorizationList) { if (cardId.data() == value.idTag && value.status == "Invalid" && isTimeout(value.expiryDate)) { result = 0; } } } return result; } /** * @brief GeneralInterface::isTimeout 判断时间是否过期 * @param date 时间字符串 * @return 过期返回 true,否则返回 false */ bool GeneralInterface::isTimeout(const QString &date) { // 将字符串转换为 QDateTime QDateTime targetTime = QDateTime::fromString(date, Qt::ISODate); if (!targetTime.isValid()) { return false; } // 转换为UTC时间以确保比较准确 targetTime = targetTime.toUTC(); QDateTime cur_time = QDateTime::currentDateTimeUtc(); return targetTime < cur_time; } QString GeneralInterface::calcTime(const uint32_t& startTime, const uint32_t& endTime) { QDateTime start = QDateTime::fromSecsSinceEpoch(startTime); QDateTime end = QDateTime::fromSecsSinceEpoch(endTime); return getCurrentDate(start.secsTo(end)); } bool GeneralInterface::isWidgetInLayout(QWidget* widget, QGridLayout* layout) { if (!widget || !layout) { return false; } // 遍历布局中的所有控件 for (int i = 0; i < layout->count(); ++i) { QLayoutItem* item = layout->itemAt(i); if (item && item->widget() == widget) { return true; } } return false; }