V20260507-2.5.3

丰富了实时翻译词库,在更多的界面提供了翻译支持。
增加了在后端可以使用临时账户登录的功能。
增加了自动删除超期的日志文件以及自动删除超期的存图路径数据库。
This commit is contained in:
2026-06-03 08:54:01 +08:00
parent cbf2756a16
commit ee97a4ab2c
40 changed files with 3427 additions and 1605 deletions
+9 -4
View File
@@ -11,20 +11,25 @@
#include <QDir>
#pragma execution_character_set("utf-8")
const std::string softwareVersion = "20260507-2.5.2";//版本号
const std::string softwareVersion = "20260507-2.5.3";//版本号
const std::string euiJsonVersion = "1.0.6";//json标准版本
#define GigaByte 1073741824//1GB字节大小
#define globalDatabasePth "./sqlDatabase/eui_db.db" //全局数据库路径
#define savePathToDataBase true //是否将存储路径存入到数据库中
#define saveImgEnable true//是否存储图片
#define switchToTongYongUi 1 //是否切换到通用UI界面
#define skipLogin 1 //是否跳过登录界面
#define jsonRpcEnable false //是否启用JSON RPC服务器
#define minimumWindowSize 1280, 720//最小窗口大小
#define histogramLabelTiltFontSize 8 //柱状图标签字体大小
#define windowMoveFreshRate 60//窗口移动每秒的刷新率
#define pathDataBaseSaveDate 90//存储存图路径的数据库其数据保留的日期
#define detDataBaseSaveDate 730//存储检测数据的数据库其数据保留的日期
#define pathDataBaseSaveDays 180//存储存图路径的数据库其数据保留的日期
#define detDataBaseSaveDays 730//存储检测数据的数据库其数据保留的日期
#define logSaveDays 365 //日志储存的日期
#define offlineMode 0//启用离线测试模式 不连接PLC 不与PLC交互 不连接相机 不对重复启动进行检测
#define dayShiftHour 7//白班小时
#define dayShiftMin 30//白班分钟
#define nightShiftHour 19//夜班小时
#define nightShiftMin 30//夜班分钟
#define sColor_Blue "51, 56, 69"
#define sColor_BlueWithWhite "64, 68, 81"
+4 -1
View File
@@ -96,8 +96,10 @@ signals:
void showMessageBox(const QString& message);
//备份及替换halcon算法文件
void backupAndReplaceAlgoFile(const std::string& path);
//重新加载所有相机为虚拟相机
void reloadAllCameraAsVirtual();
//更新用户权限 用户名 持续时间 等级(0访客,1管理员,2ROOT根管理员(一般不给予))
void updateUserPermission(const QString& userName, int duration, int level);
};
@@ -116,6 +118,7 @@ public slots:
virtual void getCameraNames(std::vector<std::string>* cameraNames) = 0;//获取所有相机的名称
virtual void getAllParams(std::vector<std::pair<QString, QVariant>>* params) = 0;//获取所有参数及值
virtual void editParam(QString group, QString name, QVariant value) = 0;//修改指定参数的值
virtual void launguageChanged(const std::string& language) = 0;//语言变更
};
extern "C" {
+1 -1
View File
@@ -51,7 +51,7 @@ static void drawArrow(cv::Mat& Paint, const cv::Point& start, const int& length,
}
}
void drawRotatedRect(cv::Mat& img, cv::RotatedRect rotateRect, cv::Scalar color, int thickness, int linType)
static void drawRotatedRect(cv::Mat& img, cv::RotatedRect rotateRect, cv::Scalar color, int thickness, int linType)
{
std::vector<cv::Point2f> boxPts(4);
rotateRect.points(boxPts.data());
+18 -15
View File
@@ -28,8 +28,8 @@ public:
}
{
QMutexLocker locker(&queueLock);
queue.enqueue(std::make_pair(snCode, data));
QMutexLocker locker(&dataQueueLock);
dataQueue.enqueue(std::make_pair(snCode, data));
}
if (!isRunning()) {
@@ -45,19 +45,22 @@ public:
protected:
void run() override {
while (true) {
std::pair<QString, std::map<QString, QVariant>> val;
QQueue<std::pair<QString, std::map<QString, QVariant>>> vals;
// 从队列获取数据
{
QMutexLocker locker(&queueLock);
if (queue.isEmpty()) {
QMutexLocker locker(&dataQueueLock);
if (dataQueue.isEmpty()) {
break;
}
val = queue.dequeue();
dataQueue.swap(vals);
}
try {
processData(val.first, val.second);
while (!vals.empty()) {
auto val = vals.dequeue();
processData(val.first, val.second);
}
}
catch (const std::exception& ex) {
qCritical() << "addData: Exception caught:" << ex.what();
@@ -70,8 +73,8 @@ protected:
private:
// 统一锁顺序:先队列锁,再数据库锁
QMutex queueLock;
QQueue<std::pair<QString, std::map<QString, QVariant>>> queue;
QMutex dataQueueLock;
QQueue<std::pair<QString, std::map<QString, QVariant>>> dataQueue;
std::mutex _mutex;
int m_intervalMs = 10000;
@@ -197,9 +200,9 @@ private:
int hour = currentDateTime.time().hour();
int minute = currentDateTime.time().minute();
//判断是否为白班
bool isDayShift = (hour > 8 || (hour == 8 && minute >= 30)) && (hour < 20 || (hour == 20 && minute <= 30));
//判断是否为前一天
bool isDayBefore = hour < 8 || (hour == 8 && minute <= 29);
bool isDayShift = (hour > dayShiftHour || (hour == dayShiftHour && minute >= dayShiftMin)) && (hour < nightShiftHour || (hour == nightShiftHour && minute <= nightShiftMin));
//判断是否为前一天(只有夜班可能是前一天的
bool isDayBefore = hour < dayShiftHour || (hour == dayShiftHour && minute < dayShiftMin);
QString dateStr;
if (isDayBefore) {
@@ -217,9 +220,9 @@ private:
int hour = currentDateTime.time().hour();
int minute = currentDateTime.time().minute();
//判断是否为白班
bool isDayShift = (hour > 8 || (hour == 8 && minute >= 30)) && (hour < 20 || (hour == 20 && minute <= 30));
//判断是否为前一天
bool isDayBefore = hour < 8 || (hour == 8 && minute <= 29);
bool isDayShift = (hour > dayShiftHour || (hour == dayShiftHour && minute >= dayShiftMin)) && (hour < nightShiftHour || (hour == nightShiftHour && minute <= nightShiftMin));
//判断是否为前一天(只有白班可能是前一天的
bool isDayBefore = hour < nightShiftHour || (hour == nightShiftHour && minute < nightShiftMin);
QDateTime lastShiftDateTime;
//如果当前为前一天的夜班 那就返回前一天的白班
+12 -6
View File
@@ -1,3 +1,5 @@
#pragma once
#include <qfile.h>
#include <string>
#include <unordered_map>
@@ -131,8 +133,14 @@ public:
bool dropTable(const QString& tableName) {
QMutexLocker lock(&db_mutex);
QString sql = QString("DROP TABLE IF EXISTS %1").arg(tableName);
return execute_unsafe(sql);
QString sql = QString("DROP TABLE IF EXISTS \"%1\"").arg(tableName);
QSqlQuery query(db);
if (!query.exec(sql)) {
qCritical() << "SQL error:" << query.lastError().text();
qCritical() << "Query:" << sql;
return false;
}
return true;
}
bool addColumn(const QString& tableName, const QString& columnName, const QString& columnType) {
@@ -443,15 +451,13 @@ private:
}
void disconnect() {
QSqlDatabase::removeDatabase(m_connectionName);
if (db.isOpen()) {
db.close();
qDebug() << "Database closed.";
}
QSqlDatabase::removeDatabase(m_connectionName);
}
QString m_connectionName;
QString dbPath;
};
#pragma once
};
File diff suppressed because it is too large Load Diff
+401
View File
@@ -0,0 +1,401 @@
#pragma once
#include <string>
#include <string_view>
#include <map>
#include <vector>
#include <fstream>
#include <iostream>
#include <nlohmannjson.hpp>
#include <QObject>
#include <QMutex>
#include <Windows.h>
#include <QMutexLocker>
#include <qelapsedtimer.h>
#include <qmap.h>
#include <tranlationBuiltInDict.h>
#define translationLRUCacheCapacity 3000 //LRU缓存容量,建议设置为1000-5000,根据实际情况调整
using json = nlohmann::json;
/**
* 单头文件翻译类 - 单例模式
* 支持可扩展的语言section
* 文本建议:建议翻译固定文本,像文本中带有数值之类会改变的建议拆分出固定文本进行翻译
*/
class translationLRUCache {
public:
explicit translationLRUCache(int capacity) : maxCapacity(capacity) {
if (capacity <= 0) {
throw std::invalid_argument("LRU缓存容量必须大于0");
}
}
// 检查字符串是否在缓存中
bool contains(const std::string& key, std::string& value) {
QMutexLocker locker(&mutex);
int index = cacheKeyList.indexOf(QString::fromStdString(key));
if (index != -1) {
// 移动到列表头部(最近使用)
cacheKeyList.move(index, 0);
value = cacheMap[key];
return true;
}
return false;
}
// 添加字符串到缓存
void put(const std::string& key, const std::string& value) {
QMutexLocker locker(&mutex);
std::pair<std::string, std::string>pairData = { key,value };
if (key.size() == 0 || value.size() == 0) {
return; // 不缓存空字符串
}
int index = cacheKeyList.indexOf(QString::fromStdString(key));
if (index != -1) {
// 已存在,移动到头部
cacheKeyList.move(index, 0);
cacheMap[key] = value; // 更新值
return;
}
// 不存在,需要添加
if (cacheKeyList.size() >= maxCapacity) {
// 缓存已满,移除最久未使用的元素(尾部)
cacheMap.remove(cacheKeyList.last().toStdString());
cacheKeyList.removeLast();
}
// 添加新元素到头部
cacheKeyList.prepend(QString::fromStdString(key));
cacheMap.insert(key, value);
}
// 移除指定元素
bool remove(const std::string& key) {
QMutexLocker locker(&mutex);
int index = cacheKeyList.indexOf(QString::fromStdString(key));
if (index != -1) {
cacheKeyList.removeAt(index);
cacheMap.remove(key);
return true;
}
return false;
}
// 获取缓存大小
int size() {
QMutexLocker locker(&mutex);
return cacheKeyList.size();
}
// 获取最大容量
int capacity() {
QMutexLocker locker(&mutex);
return maxCapacity;
}
// 检查是否为空
bool isEmpty() {
QMutexLocker locker(&mutex);
return cacheKeyList.isEmpty();
}
// 清空缓存
void clear() {
QMutexLocker locker(&mutex);
cacheKeyList.clear();
}
private:
QMutex mutex;
int maxCapacity;
QStringList cacheKeyList; // QStringList,头部是最近使用的,尾部是最久未使用的
QMap<std::string, std::string> cacheMap;
};
class Translation{
private:
translationLRUCache translatedCache{ translationLRUCacheCapacity };//缓存最近翻译的文本进行提速
std::vector<std::string> sections; // 可扩展的语言列表
std::vector<std::map<std::string, std::map<std::string, std::string>, std::less<>>> basicTranslations; //内置字典 [文本长度][文本][语言] std::less<> 启用透明查找,支持 string_view 零拷贝查询
std::string currentLanguage = "zh-CN"; // 默认语言
QMutex mutex;//线程安全锁
// 私有构造函数
Translation(){
QMutexLocker locker(&mutex);//线程安全锁
//语言选项
addSection_unsafe("zh-CN");
addSection_unsafe("en-US");
addSection_unsafe("ms-MY");
if (sections.size() > 0)//默认为第一个语言
currentLanguage = sections[0];
loadFromFile("./config/translation.json");
initBuiltInDict(basicTranslations);//初始化默认词库
}
// 禁用拷贝构造函数和赋值运算符
Translation(const Translation&) = delete;
Translation& operator=(const Translation&) = delete;
public:
// 获取单例实例
static Translation& getInstance() {
static Translation instance;
return instance;
}
// 从JSON文件加载配置
bool loadFromFile(const std::string& filePath) {
try {
std::ifstream file(filePath);
if (!file.is_open()) {
return false;
}
originalJson.clear();
file >> originalJson;
file.close();
return parseJson_unsafe(originalJson);
} catch (const std::exception& e) {
std::cerr << "Error loading translation file: " << e.what() << std::endl;
return false;
}
}
// 从JSON字符串加载配置
bool loadFromString(const std::string& jsonString) {
try {
json j = json::parse(jsonString);
return parseJson_unsafe(j);
} catch (const std::exception& e) {
std::cerr << "Error parsing JSON string: " << e.what() << std::endl;
return false;
}
}
// 从JSON对象加载配置
bool loadFromJson(const json& j) {
return parseJson_unsafe(j);
}
bool setLanguage(const std::string& lang) {
if (currentLanguage == lang)//
return false;
for (const auto& section : sections) {
if (section == lang) {
currentLanguage = lang;
return true;
}
}
// 如果语言不在sections中,也允许设置(可能是新添加的语言)
currentLanguage = lang;
return false;
}
// 获取当前语言
std::string getCurrentLanguage() const {
return currentLanguage;
}
// 获取翻译文本
std::string translate(const std::string& key) {
std::string cacheResult;
if (translatedCache.contains(key + currentLanguage, cacheResult))
return cacheResult;
QMutexLocker locker(&mutex);//线程安全锁
auto result = translate_unsafe(key);
translatedCache.put(key + currentLanguage, result);
return result; // 如果找不到翻译,返回原始key
}
// 获取指定语言的翻译
std::string translate(const std::string& key, const std::string& lang) {
QMutexLocker locker(&mutex);//线程安全锁
{//先从全字匹配字典中获取
if (key.length() <= basicTranslations.size()) {
auto it = basicTranslations.at(key.length() - 1).find(key);
if (it != basicTranslations.at(key.length() - 1).end()) {
auto langIt = it->second.find(lang);
if (langIt != it->second.end()) {
return langIt->second;
}
}
}
}
return key;
}
//获取所有语言的翻译结果
std::vector < std::pair < std::string, std::string>> getAllTranslateResult(const std::string& key) {
QMutexLocker locker(&mutex);//线程安全锁
std::vector < std::pair < std::string, std::string>> Result;
for (auto& section : sections) {
Result.push_back({ section,translate_unsafe(key,section) });
}
return Result;
}
// 添加语言section
void addSection(const std::string& lang) {
QMutexLocker locker(&mutex);//线程安全锁
if (lang == "us-EN") return;//老代码中大量存在的错误编码
for (const auto& section : sections) {
if (section == lang) return; // 已存在
}
sections.push_back(lang);
}
// 获取所有支持的语言
const std::vector<std::string>& getSections() const {
return sections;
}
// 检查是否有指定key的翻译
bool hasTranslation(const std::string& key) const {
return basicTranslations.at(key.length() - 1).find(key) != basicTranslations.at(key.length() - 1).end();
}
// 获取所有翻译key
std::vector<std::string> getAllKeys() {
QMutexLocker locker(&mutex);//线程安全锁
std::vector<std::string> keys;
for (const auto& vec : basicTranslations) {
for(const auto& pair : vec)
keys.push_back(pair.first);
}
return keys;
}
//保存到JSON文件
bool saveToFile(const std::string& filePath) {
QMutexLocker locker(&mutex);//线程安全锁
try {
originalJson["current_language"] = currentLanguage;
originalJson["section"] = sections;
std::ofstream file(filePath);
file << originalJson.dump(4); // 格式化输出,缩进4个空格
file.close();
return true;
} catch (const std::exception& e) {
std::cerr << "Error saving translation file: " << e.what() << std::endl;
return false;
}
}
private:
json originalJson;
// 使用nlohmann/json解析
bool parseJson_unsafe(const json& j) {
try {
// 解析current_language
if (j.contains("current_language") && j["current_language"].is_string()) {
currentLanguage = j["current_language"].get<std::string>();
}
// 解析sections
if (j.contains("section") && j["section"].is_array()) {
for (const auto& section : j["section"]) {
if (section.is_string()) {
addSection_unsafe(section.get<std::string>());
}
}
}
// 解析translations
if (j.contains("translate") && j["translate"].is_object()) {
for (const auto& [key, langObj] : j["translate"].items()) {
if (langObj.is_object()) {
for (const auto& [lang, text] : langObj.items()) {
if (text.is_string()) {
if (text.size() > basicTranslations.size())
basicTranslations.resize(text.size());
basicTranslations.at(text.size() - 1)[key][lang] = text.get<std::string>();
}
}
}
}
}
return true;
} catch (const std::exception& e) {
std::cerr << "Error parsing JSON: " << e.what() << std::endl;
return false;
}
}
std::string translate_unsafe(const std::string& key) {
if (key == "OK" || key == "NG")//部分简写短字不翻译
return key;
if (key.length() <= basicTranslations.size()){//先从全字匹配字典中获取
auto it = basicTranslations.at(key.length() - 1).find(key);
if (it != basicTranslations.at(key.length() - 1).end()) {
auto langIt = it->second.find(currentLanguage);
if (langIt != it->second.end()) {
return langIt->second;
}
}
}
//均无法找到 逐字翻译 仅适用于中文翻译到其他文字
for (int length = std::min(basicTranslations.size() - 1, key.length()); length > 1; length--) {
if (basicTranslations.at(length - 1).size() == 0)
continue;
auto& translationMap = basicTranslations.at(length - 1);
for (int startPos = 0; startPos <= key.length() - length; startPos++) {
{
std::string_view sv(key.data() + startPos, length);
auto it = translationMap.find(sv);
if (it != translationMap.end()) {
auto langIt = it->second.find(currentLanguage);
if (langIt != it->second.end()) {//成功翻译时
std::string result = langIt->second, frontStr, backStr;
if (startPos != 0) {//查询翻译成功之前的一段是否翻译成功
if (currentLanguage != "zh-CN")//不为中文时在词间添加空格
result = translate_unsafe(key.substr(0, startPos)) + " " + result;
else
result = translate_unsafe(key.substr(0, startPos)) + result;
}
if (startPos + length < key.length()) {//查询翻译成功之后的一段是否翻译成功
if (currentLanguage != "zh-CN")//不为中文时在词间添加空格
result += " " + translate_unsafe(key.substr(startPos + length));
else
result += translate_unsafe(key.substr(startPos + length));
}
return result;
}
}
}
}
}
return key; // 如果找不到翻译,返回原始key
}
std::string translate_unsafe(const std::string& key, const std::string& lang) {
{//先从全字匹配字典中获取
auto it = basicTranslations.at(key.length() - 1).find(key);
if (it != basicTranslations.at(key.length() - 1).end()) {
auto langIt = it->second.find(lang);
if (langIt != it->second.end()) {
return langIt->second;
}
}
}
return key;
}
// 添加语言section
void addSection_unsafe(const std::string& lang) {
if (lang == "us-EN") return;//老代码中大量存在的错误编码
for (const auto& section : sections) {
if (section == lang) return; // 已存在
}
sections.push_back(lang);
}
};
#define TR(key) QString::fromStdString(Translation::getInstance().translate(key))
+3
View File
@@ -106,6 +106,9 @@ public slots:
}
return;
}
void launguageChanged(const std::string& language){
_serviceManager.translateManager->setLanguage(language);
}
private:
bool initOnce = false;
QMutex backendJsonMutex;
+6 -6
View File
@@ -86,7 +86,7 @@ public:
continue;
}
#pragma region 注册地址写入模式的
{
if(callbacks.createCliBtn){
QString uuid = QUuid::createUuid().toString();;
auto commands = basicTriggerPresetGenerate(callbacks.triggerAddress, callbacks.triggerValue);
QString tips;
@@ -99,7 +99,7 @@ public:
}
#pragma endregion
#pragma region 注册直接触发模式的
{
if (callbacks.createCliBtn) {
QString uuid = QUuid::createUuid().toString();;
QStringList commands = { "DirectTrigger#"+ QString::fromStdString(callbacks.funcName)};
QString tips;
@@ -120,7 +120,7 @@ public:
continue;
}
#pragma region 注册地址写入模式的
{
if (callbacks.createCliBtn) {
QString uuid = QUuid::createUuid().toString();;
auto commands = basicUnionTriggerPresetGenerate(callbacks.triggerAddressAndValue);
QString tips;
@@ -133,7 +133,7 @@ public:
}
#pragma endregion
#pragma region 注册直接触发模式的
{
if (callbacks.createCliBtn) {
QString uuid = QUuid::createUuid().toString();;
QStringList commands = { "DirectTrigger#" + QString::fromStdString(callbacks.funcName) };
QString tips;
@@ -165,7 +165,7 @@ public:
QString setZeroTips;
for (auto& data : _serviceManager->_triggerManager->triggerCallbackFuncs) {
for (auto& callbacks : data.second) {
if (callbacks.funcName.find(avoid) != std::string::npos) {
if (callbacks.funcName.find(avoid) != std::string::npos && callbacks.createCliBtn == false) {
continue;
}
commands.append("fins#write_short_D" + QString::number(callbacks.triggerAddress) + "_" + QString::number(callbacks.triggerValue));
@@ -176,7 +176,7 @@ public:
}
for (auto& data : _serviceManager->_triggerManager->unionTriggerCallbackFuncs) {
for (auto& unionCallbacks : data.second) {
if (unionCallbacks.funcName.find(avoid) != std::string::npos) {
if (unionCallbacks.funcName.find(avoid) != std::string::npos && unionCallbacks.createCliBtn == false) {
continue;
}
for (auto& callbacks : unionCallbacks.triggerAddressAndValue) {
+13 -8
View File
@@ -176,14 +176,15 @@ void cameraManager::unloadAllCameras(){
module.destory_ptr = nullptr;
}
for (auto* lib : lib_list) {
if (lib != nullptr) {
if (lib->isLoaded())
lib->unload();
delete lib;
}
}
lib_list.clear();
//for (auto& lib : lib_list) {
// if (lib != nullptr) {
// if (lib->isLoaded())
// lib->unload();
// delete lib;
// lib = nullptr;
// }
//}
//lib_list.clear();
m_usedDllPath.clear();
onlineStatusMap.clear();
initFlag = false;
@@ -284,7 +285,11 @@ void cameraManager::reloadAllCameraAsVirtual(){
bool cameraManager::loadModule(cameraStruct& _cameraStruct){
try{
QString keyName = QString::fromStdString(_cameraStruct.key);
#if offlineMode == 0
QString dllPath = QString::fromStdString(_cameraStruct.dllPath);// 相机模块名
#else
QString dllPath = "Hd_VirtualCamera";
#endif
QString module_path = "./" + dllPath;
qDebug() << "[INFO] " << " module_path:" << module_path;
+62 -15
View File
@@ -1,3 +1,4 @@
#pragma once
#include <qthread.h>
#include <opencv.hpp>
#include <qqueue.h>
@@ -475,6 +476,13 @@ public:
}
}
}
// 每天检查一次,自动删除超出保留天数的旧表
QDate currentDate = QDate::currentDate();
if (lastCleanupDate.isNull() || lastCleanupDate != currentDate) {
lastCleanupDate = currentDate;
cleanupOldTables();
}
}
catch (std::exception& e) {
qCritical() << "[DB存储线程] 异常:" << e.what();
@@ -529,11 +537,50 @@ private:
return false;
}
}
// 自动删除超出保留天数的旧表
void cleanupOldTables() {
try {
int saveDays = pathDataBaseSaveDays;
if (saveDays <= 0) {
return;
}
auto tables = sqlDB.getTables();
QDate deadline = QDate::currentDate().addDays(-saveDays);
int deleteCount = 0;
for (const auto& tableName : tables) {
// 表名格式: yyyy-MM-dd-HH,提取日期部分
QStringList parts = tableName.split("-");
if (parts.size() >= 3) {
QDate tableDate(parts[0].toInt(), parts[1].toInt(), parts[2].toInt());
if (tableDate.isValid() && tableDate < deadline) {
qDebug() << "[DB存储线程] 删除旧表:" << tableName;
if (sqlDB.dropTable(tableName)) {
deleteCount++;
}
}
}
}
if (deleteCount > 0) {
qDebug() << "[DB存储线程] 清理完成,共删除" << deleteCount << "个旧表";
}
}
catch (std::exception& e) {
qCritical() << "[DB存储线程] 清理旧表异常:" << e.what();
}
catch (...) {
qCritical() << "[DB存储线程] 清理旧表未知异常";
}
}
SQLiteDB sqlDB;
QQueue<QString> pathQueue;
QMutex queueMutex;
std::atomic<int> pendingCount = 0;
QDate lastCleanupDate; // 上次清理日期,用于每天只清理一次
};
class imgSaveThread : public QThread {
@@ -592,12 +639,11 @@ public:
}
void addJob(std::shared_ptr<saveJob> job, type type_ = normal) {
QMutexLocker locker(&queueMutex);
for (auto& img : job->imgQueue) {
threadBufferSize += img.second->size();
}
QMutexLocker locker(&queueMutex);
switch (type_) {
case normal:
defaultQueue.append(job);
@@ -735,7 +781,7 @@ public:
QString resSavePath = _ImageSavePathFormatter->genPath(data->SNcode, data->cameraKeyName, data->saveObjectName,
"结果图", data->customPath, ngSummaryResult, data->customImageSaveTemplateParams);
if (saveRes == true && //保存结果图按钮为启用
_memUsage.load().usedMemPercent < notSaveAnyImgThreshold) {//内存占用不超过限制
notSaveAnyImg.load() == false) {//内存占用不超过限制
createDirIfNotExistsWithCache(resSavePath);//带LRUCache缓存校验的文件夹创建,并将保存路径加入数据库
@@ -750,11 +796,11 @@ public:
QString oriSavePath = _ImageSavePathFormatter->genPath(data->SNcode, data->cameraKeyName, data->saveObjectName,
"原图", data->customPath, ngSummaryResult ,data->customImageSaveTemplateParams);
if ((saveOri == true || saveSeg == true || saveCls == true) && //保存SEG图/CLS图/原图按钮为启用
_memUsage.load().usedMemPercent < notSaveOriImgThreshold) {//内存占用不超过限制
notSaveAnyOriImg.load() == false) {//内存占用不超过限制
createDirIfNotExistsWithCache(oriSavePath);//带LRUCache缓存校验的文件夹创建,并将保存路径加入数据库
#pragma region 原图存储及判断
if (_memUsage.load().usedMemPercent > notSaveOkOriImgThreshold && //当内存占用超过上限
if (notSaveOkImg.load() == true && //当内存占用超过上限
ngSummaryResult.aiDetOK == true && ngSummaryResult.halconOK == true) {//且结果为OK时不保存原图
cv::Mat blankImg(300, 300, CV_8UC1, cv::Scalar(0));
@@ -828,7 +874,6 @@ public:
private:
bSignalHub* bridge;
std::atomic<int> savingImgCount = 0;
std::atomic<memUsage> _memUsage;//内存占用
ngSummary* _ngSummary;//用于汇总NG存图结果
LRUCache createPathCache{ 100 };//用于缓存已经创建过的文件夹路径,防止重复创建
std::unique_ptr<dbSaveThread> dbThread; // 数据库存储线程
@@ -910,6 +955,7 @@ private:
}
//刷新存图参数
std::atomic<bool> notSaveAnyImg, notSaveAnyOriImg, notSaveOkImg;
void refreshParam() {
if (_paramSyncManager != nullptr) {
saveExtraNgImg = _paramSyncManager->get("通用设置", "额外存储NG图片", false).toBool();
@@ -922,19 +968,21 @@ private:
saveCls = _paramSyncManager->get("通用设置", "存储Cls图", true).toBool();
notSaveOkOriImgThreshold = _paramSyncManager->get("通用设置", "仅存储NG图的内存占用上限", 70.0).toDouble();
notSaveOriImgThreshold = _paramSyncManager->get("通用设置", "仅存储结果图的内存占用上限", 80.0).toDouble();
notSaveAnyImgThreshold = _paramSyncManager->get("通用设置", "不存储任何图片的内存占用上限", 90.0).toDouble();
notSaveAnyImgThreshold = _paramSyncManager->get("通用设置", "不存储任何图片的内存占用上限", 95.0).toDouble();
_ngSummary->setWaitTimeSec(_paramSyncManager->get("通用设置", "存图汇总NG结果的等待时间(秒)", 6.0).toDouble());
enableSummurayNgResult = _paramSyncManager->get("通用设置", "启用等待NG汇总存图", false).toBool();
_memUsage = getMemUsage();
auto mem = _memUsage.load();
if (mem.usedMemPercent > notSaveAnyImgThreshold) {
auto mem = getMemUsage();;
if (mem.usedMemPercent > notSaveAnyImgThreshold && mem.usedPhysPercent > notSaveAnyImgThreshold) {
qDebug() << "内存占用超过" << notSaveAnyImgThreshold << "%,将不储存任何图片";
notSaveAnyImg = true; notSaveAnyOriImg = true; notSaveOkImg = true;
}
else if (mem.usedMemPercent > notSaveOriImgThreshold) {
else if (mem.usedMemPercent > notSaveOriImgThreshold && mem.usedPhysPercent > notSaveOriImgThreshold) {
qDebug() << "内存占用超过" << notSaveOriImgThreshold << "%,将不储存原图";
notSaveAnyOriImg = true; notSaveOkImg = true;
}
else if (mem.usedMemPercent > notSaveOkOriImgThreshold) {
else if (mem.usedMemPercent > notSaveOkOriImgThreshold && mem.usedPhysPercent > notSaveOkOriImgThreshold) {
qDebug() << "内存占用超过" << notSaveOkOriImgThreshold << "%,将不储存OK原图";
notSaveOkImg = true;
}
}
}
@@ -953,4 +1001,3 @@ private:
}
};
#pragma once
+2
View File
@@ -7,6 +7,7 @@
#include <paramSyncManager.h>
#include <handleAiGpuThreadGuard.h>
#include <sql/dataRecorder.h>
#include <translation.h>
class ServiceManager {
public:
detDataManager* _detDataManager = nullptr;// 检测数据管理器 需要初始化
@@ -18,6 +19,7 @@ public:
missionResultHandle* _missionResultHandle = nullptr;//处理算法线程的数据采集
paramSyncManager* _paramSyncManager = nullptr;//前后端参数实时同步器
handleAiManager* _handleAiManager = nullptr;;//模型管理器
Translation* translateManager = &Translation::getInstance();
};
#pragma once
+8 -2
View File
@@ -60,6 +60,7 @@ bool triggerManager::connectHost(){
if (init == true)
return true;
status = NotInit;
#if offlineMode == 0
m_lib = new QLibrary(triggerFileName);
libLoadedflag = m_lib->load();
if (!m_lib->isLoaded()){
@@ -97,10 +98,15 @@ bool triggerManager::connectHost(){
return true;
}
}
#else
status = InitOK;
init = true;
emit bridge->loadingWindow_appendInitLog("离线模式创建成功", "green");
#endif
return false;
}
void triggerManager::receiveCommand() {
#if offlineMode == 0
for (auto& _struct : readAllCmds) {
QByteArray readByte = _struct.readAllCmd;
//接收数据
@@ -113,7 +119,7 @@ void triggerManager::receiveCommand() {
//解析数据
decodeCameraCMD(_struct,readByte);
}
#endif
}
void triggerManager::decodeCameraCMD(readAllStruct& _struct, QByteArray byte)
+22 -8
View File
@@ -9,7 +9,6 @@
#include <qelapsedtimer.h>
#include <thread>
#define debugTesting 0
using triggerCallback = std::function<void(int,int,QByteArray& byte)>;
using uniontriggerCallback = std::function<void(std::vector<std::pair<int,int>>, QByteArray& byte)>;
class cameraManager;
@@ -131,17 +130,21 @@ public:
#pragma region 发送数据
//直接发送数据到模块的方法
bool sendFinsMsg(const QString& cmdx){
#if offlineMode == 0
if (trigger == nullptr)
return false;
if (!trigger->writeData(vec_mat, cmdx.toUtf8()))
{
qCritical() << "[TCP Error] " << QString::fromUtf8("Fins数据发送失败!") << cmdx;
return false;
}
#endif
return true;
}
//直接发送数据到模块的方法
bool sendFinsMsg(const QByteArray& write_byte){
#if offlineMode == 0
if (trigger == nullptr)
return false;
QByteArray dataSend = write_byte;
@@ -150,6 +153,7 @@ public:
qCritical() << "[TCP Error] " << QString::fromUtf8("Fins数据发送失败!") << write_byte;
return false;
}
#endif
return true;
}
@@ -157,6 +161,7 @@ public:
//useage "D9200_1"
bool writeShort(const QByteArray& write_byte) {
#if offlineMode == 0
if (trigger == nullptr)
return false;
QByteArray array("write_short_" + write_byte);
@@ -165,10 +170,12 @@ public:
qCritical() << "[TCP Error] " << QString::fromUtf8("Fins数据发送失败!") << write_byte;
return false;
}
#endif
return true;
}
//useage {"D9200_1","D9201_1"}
bool writeShort(const std::vector<std::string>& write_byte) {
#if offlineMode == 0
if (trigger == nullptr || write_byte.size() == 0)
return false;
for (auto& byteArray : write_byte) {
@@ -178,11 +185,12 @@ public:
return false;
}
}
#endif
return true;
}
//useage {"D9200_1","D9201_1"}
bool writeShort(const std::vector<QByteArray>& write_byte) {
#if offlineMode == 0
if (trigger == nullptr || write_byte.size() == 0)
return false;
for (auto& byteArray : write_byte) {
@@ -192,12 +200,13 @@ public:
return false;
}
}
#endif
return true;
}
//useage "D9200_1"
bool writeFloat(const QByteArray& write_byte) {
#if offlineMode == 0
if (trigger == nullptr)
return false;
QByteArray array = "write_float_" + write_byte;
@@ -205,11 +214,12 @@ public:
qCritical() << "[TCP Error] " << QString::fromUtf8("Fins数据发送失败!") << array;
return false;
}
#endif
return true;
}
//useage {"D9200_1","D9201_1"}
bool writeFloat(const std::vector<std::string>& write_byte) {
#if offlineMode == 0
if (trigger == nullptr || write_byte.size() == 0)
return false;
for (auto& byteArray : write_byte) {
@@ -219,11 +229,12 @@ public:
return false;
}
}
#endif
return true;
}
//useage {"D9200_1","D9201_1"}
bool writeFloat(const std::vector<QByteArray>& write_byte) {
#if offlineMode == 0
if (trigger == nullptr || write_byte.size() == 0)
return false;
for (auto& byteArray : write_byte) {
@@ -233,7 +244,7 @@ public:
return false;
}
}
#endif
return true;
}
#pragma endregion
@@ -246,12 +257,14 @@ public:
int triggerAddress;//触发信号
int triggerValue;//触发的值
std::string funcName;//函数名
bool createCliBtn;//是否在故障排查界面创建按钮
triggerCallback func;//函数
};
struct unionTriggerCallbackStruct {
std::vector<std::pair<int, int>> triggerAddressAndValue;//联合触发用于判断的地址
conditionMode mode;//联合判断模式
std::string funcName;//函数名
bool createCliBtn;//是否在故障排查界面创建按钮
uniontriggerCallback func;//函数
};
@@ -306,7 +319,7 @@ public:
}
//单条件的触发器注册
bool registerCallback(int triggerAddress, int triggerValue, triggerCallback func, std::string funcName) {
bool registerCallback(int triggerAddress, int triggerValue, triggerCallback func, std::string funcName, bool createCliBtn = true) {
QMutexLocker locer(&mutex);
triggerCallbackStruct _triggerCallbackStruct;
_triggerCallbackStruct.triggerAddress = triggerAddress;
@@ -326,7 +339,7 @@ public:
std::map<QByteArray, std::vector<triggerCallbackStruct>> triggerCallbackFuncs;//单触发回调函数表
//联合条件的触发器注册
bool registerUnionCallback(std::vector<std::pair<int, int>> triggerAddressAndValue, conditionMode mode, uniontriggerCallback func, std::string funcName) {
bool registerUnionCallback(std::vector<std::pair<int, int>> triggerAddressAndValue, conditionMode mode, uniontriggerCallback func, std::string funcName, bool createCliBtn = true) {
QMutexLocker locer(&mutex);
if (triggerAddressAndValue.size() < 2) {
qCritical() << "联合触发器至少需要两个条件";
@@ -353,6 +366,7 @@ public:
cb.func = std::move(func);
cb.mode = mode;
cb.funcName = funcName.empty() ? QString("%1~%2联合触发").arg(cb.triggerAddressAndValue[0].first).arg(cb.triggerAddressAndValue[cb.triggerAddressAndValue.size() - 1].first).toStdString() : funcName;
cb.createCliBtn = createCliBtn;
unionTriggerCallbackFuncs[ownerStruct->readAllCmd].push_back(std::move(cb));
qInfo() << "注册联合触发器成功:" << ownerStruct->readAllCmd << ", 条件数=" << unionTriggerCallbackFuncs[ownerStruct->readAllCmd].back().triggerAddressAndValue.size();
return true;
-1
View File
@@ -25,7 +25,6 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/GUI/halconAlgo
${CMAKE_CURRENT_SOURCE_DIR}/GUI/uiModule
)
link_directories (
${ENV_LIB}
)
+5 -4
View File
@@ -26,6 +26,7 @@
#include <simpleTools/fileManager.h>
#include <uiModule/uiModule_modelManager.h>
#include <uiModule/uiModule_algoManager.h>
#include <components/marqueeToolButton.h>
class toolBarWidget : public QWidget {
Q_OBJECT
public:
@@ -80,7 +81,7 @@ public:
})
}
// 通用的创建工具按钮函数实现
QToolButton* createToolButton(QIcon icon, const QString& text,
MarqueeToolButton* createToolButton(QIcon icon, const QString& text,
std::function<void()> clickHandler, Auth::UserLevel level = Auth::UserLevel::GUEST,
const QSize& buttonSize = QSize(110, 60), const QSize& iconSize = QSize(32, 32)) {
@@ -99,7 +100,7 @@ public:
btnLayout->setContentsMargins(0, 0, 0, 0);
// 使用 QToolButton 来实现图标在上文字在下的效果
QToolButton* btn = new QToolButton(this);
MarqueeToolButton* btn = new MarqueeToolButton(this);
if (!btn) {
delete btnLayout;
return nullptr;
@@ -133,7 +134,7 @@ public:
btnLayout->setAlignment(Qt::AlignCenter);
// 连接点击事件
if (!connect(btn, &QToolButton::clicked, [this, level,clickHandler]() {
if (!connect(btn, &MarqueeToolButton::clicked, [this, level,clickHandler]() {
m_config->Authorization.executeWithPermission(level, [this, clickHandler]() {
clickHandler();
});
@@ -374,7 +375,7 @@ public:
std::string fileName = fileManager::getFileName(lnkPath);
auto exePath = getShortcutTarget(QString::fromLocal8Bit(lnkPath.c_str()));
auto icon = getFileIcon(exePath, QSize(32, 32));
_toolBarWidget->createToolButton(icon, QString::fromLocal8Bit(fileName.c_str()), [exePath]() {
_toolBarWidget->createToolButton(icon, TR(QString::fromLocal8Bit(fileName.c_str()).toStdString()), [exePath]() {
QProcess::startDetached(exePath);
});
}
+10 -5
View File
@@ -103,9 +103,12 @@ public:
stdConsoleOutput.setStyleSheet(textEditStyle);
qtConsoleOutput.setStyleSheet(textEditStyle);
QObject::connect(fSignalHub::getInstance(), &fSignalHub::languageChanged, this, [this]() {
Title->setText(TR("日志分析"));
});
addLogWidget(TR("控制台"), &stdConsoleOutput, logType::stdConsle);
addLogWidget(TR("系统日志"), &qtConsoleOutput, logType::qtConsle);
addLogWidget("控制台", &stdConsoleOutput, logType::stdConsle);
addLogWidget("系统日志", &qtConsoleOutput, logType::qtConsle);
btns[0]->pressed();
Layout->addWidget(widgets);
@@ -118,10 +121,10 @@ public:
void addLogWidget(QString btnText, QPlainTextEdit* textWidget, logType type) {
QPalette palette;
palette.setColor(QPalette::WindowText, Qt::white);
QFont font2("Microsoft YaHei", 8);
QFont font2("Microsoft YaHei", 8);
int index = btns.size();
gradientButton* btn = new gradientButton(160, 50, btnText, "");
gradientButton* btn = new gradientButton(160, 50, TR(btnText.toStdString()), "");
btnLayout->addWidget(btn);
btns.push_back(btn);
connect(btn, &QPushButton::clicked, [=]() {
@@ -131,7 +134,9 @@ public:
widgets->setCurrentIndex(index);
processCacheLog(index);
});
QObject::connect(fSignalHub::getInstance(), &fSignalHub::languageChanged, this, [btn, btnText]() {
btn->setText(TR(btnText.toStdString()));
});
QWidget* widget = new QWidget();
widgets->addWidget(widget);
widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+10 -8
View File
@@ -23,7 +23,7 @@
#include <qgroupbox.h>
#include <qframe.h>
#include <qcombobox.h>
//#include <systemStateMonitor.h>
#include <frontendSignalHub.h>
class settingWidget :public QWidget{
Q_OBJECT
@@ -214,12 +214,14 @@ public:
connect(languageSelect, &QComboBox::currentTextChanged, [=](const QString& str) {
Translation::getInstance().setLanguage(str.toStdString());
auto languageSelectTipTexts = Translation::getInstance().getAllTranslateResult("重启后将会更改");
QString languageSelectTipText;
for (auto& text : languageSelectTipTexts)
languageSelectTipText += QString::fromStdString(text.second) + "/";
languageSelectTipText.right(1);
languageSelectTip->setText(languageSelectTipText);
emit fSignalHub::getInstance()->languageChanged(str.toStdString());
//auto languageSelectTipTexts = Translation::getInstance().getAllTranslateResult("重启后将会更改");
//QString languageSelectTipText;
//for (auto& text : languageSelectTipTexts)
// languageSelectTipText += QString::fromStdString(text.second) + "/";
//languageSelectTipText.right(1);
//languageSelectTip->setText(languageSelectTipText);
});
m_layout->addWidget(languageGroupBox);
@@ -392,7 +394,7 @@ public:
const auto reply = QMessageBox::question(
this,
TR("确认"),
TR("确定要替换吗,在调试结束后哦请重启软件"),
TR("确定要替换吗,在调试结束后请重启软件"),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No);
if (reply == QMessageBox::Yes) {
+37 -10
View File
@@ -15,7 +15,7 @@ class sqliteReader : public QWidget{
public:
sqliteReader(SQLiteDB* dbIn):db(dbIn) {
QFont font("Microsoft YaHei", 12, QFont::Bold);
QFont font("Microsoft YaHei", 12, QFont::Bold);
QPalette palette;
palette.setColor(QPalette::WindowText, Qt::white);
@@ -28,13 +28,12 @@ public:
showLabel->setFont(font);
showLabel->setPalette(palette);
vLayout->addWidget(showLabel);
tableSelectComboBox->setFixedWidth(200);
vLayout->addWidget(tableSelectComboBox);
connect(tableSelectComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, [=]() {
if(tableSelectComboBox->currentText() != "")
switchWidget(tableSelectComboBox->currentText());
switchWidget(tableKeyPair[tableSelectComboBox->currentText()]);
});
refreshBtn->setFont(font);
@@ -58,15 +57,26 @@ public:
connect(dumpToCsvBtn, &QPushButton::clicked, this, [=]() {dumpToCsv(tableSelectComboBox->currentText()); });
//1-2
layout->addWidget(tableWidget);
QObject::connect(fSignalHub::getInstance(), &fSignalHub::languageChanged, this, [this, showLabel, showLabel2]() {
showLabel->setText(TR("班次"));
showLabel2->setText(TR("SN查询"));
refreshBtn->setText(TR("刷新"));
searchSnBtn->setText(TR("查询"));
dumpToCsvBtn->setText(TR("导出"));
});
}
//QTableView* view = new QTableView;
void refreshTable() {
tableKeyPair.clear();
tableSelectComboBox->clear();
auto tables = db->getTables();
for (int i = tables.size() - 1; i > 0 ; i--){
if (!tables[i].contains("set") && !tables[i].contains("hide"))
tableSelectComboBox->addItem(tables[i]);
if (!tables[i].contains("set") && !tables[i].contains("hide")) {
auto translatedStr = TR(tables[i].toStdString());
tableKeyPair[translatedStr] = tables[i];
tableSelectComboBox->addItem(translatedStr);
}
}
}
void switchWidget(QString tableName) {
@@ -77,6 +87,20 @@ public:
qDebug() << "查询失败:" << model->lastError().text();
return;
}
for (int col = 0; col < model->columnCount(); ++col){
QString header =
model->headerData(
col,
Qt::Horizontal
).toString();
model->setHeaderData(
col,
Qt::Horizontal,
TR(header.toStdString())
);
}
tableWidget->setModel(model);
}
void searchSN(QString tableName, QString SN) {
@@ -130,9 +154,10 @@ private:
QComboBox* tableSelectComboBox = new QComboBox();
QVBoxLayout* layout = new QVBoxLayout(this);
QTableView* tableWidget = new QTableView();
fancyButtonText* refreshBtn = new fancyButtonText(TR("刷新"), 80, 25, "");
fancyButtonText* searchSnBtn = new fancyButtonText(TR("查询"), 80, 25, "");
fancyButtonText* dumpToCsvBtn = new fancyButtonText(TR("导出"), 80, 25, "");
fancyButtonText* refreshBtn = new fancyButtonText(TR("刷新"), 140, 25, "");
fancyButtonText* searchSnBtn = new fancyButtonText(TR("查询"), 120, 25, "");
fancyButtonText* dumpToCsvBtn = new fancyButtonText(TR("导出"), 120, 25, "");
QMap<QString, QString> tableKeyPair;//展示文本与原始文本的对应
protected:
void showEvent(QShowEvent* event) override {
QWidget::showEvent(event);
@@ -144,7 +169,7 @@ public:
QVBoxLayout* Layout = new QVBoxLayout(this);
QHBoxLayout* btnLayout = new QHBoxLayout(this);
std::vector<gradientButton*>btns;
QLabel* icon, * Title;
QLabel* icon, *Title;
QStackedWidget* widgets = new QStackedWidget();
sqliteReader _sqliteReader;
QTableWidget* sqlWidget = new QTableWidget();
@@ -191,7 +216,9 @@ public:
Layout->addLayout(btnLayout);
Layout->addWidget(&_sqliteReader);
QObject::connect(fSignalHub::getInstance(), &fSignalHub::languageChanged, this, [this]() {
Title->setText(TR("数据库操作界面"));
});
}
};
#pragma once
+34 -24
View File
@@ -1,5 +1,5 @@
#pragma once
#include <translation.h>
#include <QDialog>
#include <QTableWidget>
#include <QPlainTextEdit>
@@ -26,6 +26,11 @@
#include <QListWidget>
#include <QSplitter>
#include <QFrame>
#include <components/ScrollingLabel.h>
struct paramName {
QString originName;//原始名字 键
QString showName;//展示用的名字 可能会经过翻译
};
class WheelBlocker : public QObject {
Q_OBJECT
public:
@@ -45,9 +50,8 @@ class paramTableEditor : public QDialog {
public:
explicit paramTableEditor(QWidget* parent = nullptr,bool viewOnlyModeIN = false)
: QDialog(parent), viewOnlyMode(viewOnlyModeIN){
this->setWindowTitle("参数编辑器");
this->setWindowTitle(TR("参数编辑器"));
this->resize(1400, 600);
setWindowFlags(this->windowFlags() | Qt::FramelessWindowHint);
this->setObjectName("paramTableEditor");
this->setStyleSheet(QString("QDialog#paramTableEditor {background-color: rgb(%1);border: 2px solid #000000;border-radius: 6px;}").arg(sColor_Blue));
@@ -74,7 +78,8 @@ public:
mainLayout->addWidget(m_sidebarWidget);
mainLayout->addWidget(m_dividerLine);
mainLayout->addWidget(m_contentWidget,1);
mainLayout->addWidget(m_imageTipWidget,1);
// mainLayout->addWidget(m_imageTipWidget,1);//无人员配合 暂时屏蔽
imageTipLabel->hide();
}
void setParams(std::vector<std::pair<QString, QVariant>> params) {
@@ -126,7 +131,10 @@ public:
if (viewOnlyMode && (group == "hide" || (!m_allParams["hide." + group].toBool()))) {
continue;
}
m_sidebarList->addItem(group);
auto translateResult = TR(group.toStdString());
m_sidebarKeyPair[translateResult] = group;
m_sidebarList->addItem(translateResult);
}
//保存分组参数
@@ -135,7 +143,7 @@ public:
//默认显示第一个分组
if (m_sidebarList->count() > 0) {
m_sidebarList->setCurrentRow(0);
displayGroupParams(m_sidebarList->item(0)->text());
displayGroupParams(m_sidebarKeyPair[m_sidebarList->item(0)->text()]);
}
}
@@ -180,7 +188,7 @@ private slots:
void onSidebarItemClicked(QListWidgetItem* item) {
if (!item) return;
QString groupName = item->text();
QString groupName = m_sidebarKeyPair[item->text()];
displayGroupParams(groupName);
switchImageTipArea(groupName);
}
@@ -191,11 +199,12 @@ private:
QTableView* m_tableView;
QStandardItemModel* m_tableModel;
nlohmann::json m_originalJson;
std::map<QString, QVariant::Type> m_originalTypes;
std::map<QString, QWidget*> m_editorWidgets;
std::map<QString, QVariant::Type> m_originalTypes;//参数原始的类型,类似浮点型 整型
std::map<QString, QWidget*> m_editorWidgets;//参数对应的控件
//左侧边栏相关成员
QWidget* m_sidebarWidget;
QMap <QString, QString> m_sidebarKeyPair;//侧边栏的展示值与原始值的对应
QWidget* m_contentWidget;
QListWidget* m_sidebarList;
QFrame* m_dividerLine;
@@ -224,10 +233,10 @@ private:
sidebarLayout->setContentsMargins(8, 8, 8, 8);
sidebarLayout->setSpacing(10);
//左侧边栏标题
QLabel* sidebarTitle = new QLabel("参数分组");
sidebarTitle->setStyleSheet("color: white; font-weight: bold; font-size: 14px; padding: 5px;");
sidebarTitle->setAlignment(Qt::AlignCenter);
////左侧边栏标题
//QLabel* sidebarTitle = new QLabel(TR("参数分组"));
//sidebarTitle->setStyleSheet("color: white; font-weight: bold; font-size: 14px; padding: 5px;");
//sidebarTitle->setAlignment(Qt::AlignCenter);
//侧边栏列表样式
m_sidebarList = new QListWidget();
@@ -257,7 +266,7 @@ private:
)");
connect(m_sidebarList, &QListWidget::itemClicked, this, &paramTableEditor::onSidebarItemClicked);
sidebarLayout->addWidget(sidebarTitle);
//sidebarLayout->addWidget(sidebarTitle);
sidebarLayout->addWidget(m_sidebarList);
}
void createImageTipArea() {
@@ -277,7 +286,7 @@ private:
imgaeTipLayout->setSpacing(10);
//左侧边栏标题
imageTipLabel = new QLabel("参数分组");
imageTipLabel = new QLabel(TR("参数分组"));
imageTipLabel->setStyleSheet("color: white; font-weight: bold; font-size: 14px; padding: 5px;");
imageTipLabel->setAlignment(Qt::AlignCenter);
@@ -306,7 +315,8 @@ private:
contentLayout->setSpacing(10);
// 当前分组标签
m_currentGroupLabel = new QLabel("所有参数" + viewOnlyMode == false ? "":"当前无法修改(权限不足,请登录后重新打开此界面)");
QString text = TR("所有参数") + viewOnlyMode == false ? "" : TR("当前无法修改(权限不足,请登录后重新打开此界面)");
m_currentGroupLabel = new QLabel(text);
m_currentGroupLabel->setStyleSheet("color: white; font-weight: bold; font-size: 16px; padding: 5px;");
contentLayout->addWidget(m_currentGroupLabel);
@@ -332,26 +342,26 @@ private:
m_tableView->setItemDelegateForColumn(0, new QStyledItemDelegate(m_tableView));
m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
m_tableView->setSelectionMode(QAbstractItemView::SingleSelection);
m_tableView->setColumnWidth(0, 320);
m_tableView->setColumnWidth(0, 600);
// 禁止第一列选中
m_tableView->setColumnHidden(0, false);
m_tableView->setFocusPolicy(Qt::NoFocus);
// 设置表头
m_tableModel->setHorizontalHeaderLabels({ "参数名", "值" });
m_tableModel->setHorizontalHeaderLabels({ TR("参数名"), TR("值") });
// 设置表格属性
m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
m_tableView->setAlternatingRowColors(true);
m_tableView->horizontalHeader()->setStretchLastSection(true);
m_tableView->setColumnWidth(1, 140);
m_tableView->setColumnWidth(1, 150);
fancyButtonText* okBtn = new fancyButtonText("保存", 70, 22, "");
fancyButtonText* okBtn = new fancyButtonText(TR("保存"), 140, 22, "");
okBtn->setDisabled(viewOnlyMode);
okBtn->setVisible(!viewOnlyMode);
fancyButtonText* cancelBtn = new fancyButtonText("取消", 70, 22, "");
fancyButtonText* cancelBtn = new fancyButtonText(TR("取消"), 70, 22, "");
connect(okBtn, &QPushButton::clicked, this, &paramTableEditor::onAccept);
connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
@@ -420,7 +430,7 @@ private:
void displayGroupParams(const QString& groupName) {
//更新当前分组标签
m_currentGroupLabel->setText(groupName);
m_currentGroupLabel->setText(TR(groupName.toStdString()));
// 清空表格
m_tableModel->setRowCount(0);
@@ -460,12 +470,12 @@ private:
}
// 设置键名(不可编辑)
QStandardItem* keyItem = new QStandardItem(displayName);
QStandardItem* keyItem = new QStandardItem(TR(displayName.toStdString()));
keyItem->setEditable(false);
keyItem->setBackground(QColor(60, 60, 60));
keyItem->setForeground(QColor(255, 255, 255));
keyItem->setData(fullKey, Qt::UserRole);//存储完整键名
keyItem->setToolTip(fullKey);//提示显示完整键名
keyItem->setToolTip(TR(fullKey.toStdString()));//提示显示完整键名
m_tableModel->setItem(row, 0, keyItem);
//确定类型(内部使用,不显示)
@@ -20,7 +20,7 @@ public:
timeCacheTimer.start();
const QTime now = cachedDateTime.time();
const int minutes = now.hour() * 60 + now.minute();
bool isDay = (minutes >= (8 * 60 + 30) && minutes < (20 * 60 + 30));
bool isDay = (minutes >= (dayShiftHour * 60 + dayShiftMin) && minutes < (nightShiftHour * 60 + nightShiftMin));
QString shiftDate = calcShiftDate(cachedDateTime, isDay);
lastIsDayState = isDay;
lastDayShiftDate = shiftDate;
@@ -39,7 +39,7 @@ public:
const QTime now = cachedDateTime.time();
const int minutes = now.hour() * 60 + now.minute();
bool isDay = (minutes >= (8 * 60 + 30) && minutes < (20 * 60 + 30));
bool isDay = (minutes >= (dayShiftHour * 60 + dayShiftMin) && minutes < (nightShiftHour * 60 + nightShiftMin));
QString shiftDate = calcShiftDate(cachedDateTime, isDay);
@@ -62,10 +62,10 @@ private:
QString calcShiftDate(const QDateTime& dt, bool isDay) {
QDate date = dt.date();
//夜班0.00~8.30为前一天的夜班
//例如夜班0.00~8.30为前一天的夜班
if (!isDay) {
if (dt.time().hour() < 8 ||
(dt.time().hour() == 8 && dt.time().minute() < 30)) {
if (dt.time().hour() < dayShiftHour ||
(dt.time().hour() == dayShiftHour && dt.time().minute() < dayShiftMin)) {
date = date.addDays(-1);
}
}
@@ -0,0 +1,190 @@
#pragma once
#include <QToolButton>
#include <QPainter>
#include <QTimer>
#include <QStyleOptionToolButton>
#include <QFontMetrics>
#include <QResizeEvent>
class MarqueeToolButton : public QToolButton
{
Q_OBJECT
public:
explicit MarqueeToolButton(QWidget* parent = nullptr)
: QToolButton(parent)
{
setMouseTracking(true);
connect(&m_timer, &QTimer::timeout, this, [this]() {
m_offset += 1;
QFontMetrics fm(font());
int textWidth = fm.horizontalAdvance(text());
if (m_offset > textWidth + m_spacing) {
m_offset = 0;
}
update();
});
m_timer.setInterval(30);
}
protected:
// Qt5 使用 QEvent*
void enterEvent(QEvent* event) override
{
QToolButton::enterEvent(event);
updateScrollState();
if (m_needScroll) {
m_offset = 0;
m_timer.start();
}
}
void leaveEvent(QEvent* event) override
{
QToolButton::leaveEvent(event);
m_timer.stop();
m_offset = 0;
update();
}
void resizeEvent(QResizeEvent* event) override
{
QToolButton::resizeEvent(event);
updateScrollState();
}
void paintEvent(QPaintEvent* event) override
{
Q_UNUSED(event);
QStyleOptionToolButton opt;
initStyleOption(&opt);
QPainter painter(this);
// 绘制按钮背景
style()->drawPrimitive(
QStyle::PE_PanelButtonTool,
&opt,
&painter,
this);
QRect rect = this->rect();
// 图标大小
int iconSize = iconSizeValue();
QRect iconRect(
(rect.width() - iconSize) / 2,
5,
iconSize,
iconSize
);
// 绘制图标
opt.icon.paint(&painter, iconRect);
// 文本区域
QRect textRect(
5,
iconRect.bottom() + 5,
rect.width() - 10,
rect.height() - iconRect.bottom() - 10
);
painter.setPen(palette().buttonText().color());
QFontMetrics fm(font());
int textWidth = fm.horizontalAdvance(text());
// 不需要滚动
if (!m_needScroll || !underMouse()) {
QString elided =
fm.elidedText(
text(),
Qt::ElideRight,
textRect.width());
painter.drawText(
textRect,
Qt::AlignCenter,
elided);
return;
}
// 裁剪文本区域
painter.setClipRect(textRect);
int y =
textRect.center().y()
+ fm.ascent() / 2;
// 第一段文字
painter.drawText(
textRect.left() - m_offset,
y,
text());
// 第二段文字(循环衔接)
painter.drawText(
textRect.left()
- m_offset
+ textWidth
+ m_spacing,
y,
text());
}
private:
QTimer m_timer;
int m_offset = 0;
bool m_needScroll = false;
int m_spacing = 30;
void updateScrollState()
{
QFontMetrics fm(font());
int textWidth =
fm.horizontalAdvance(text());
m_needScroll =
textWidth > width() - 10;
}
int iconSizeValue() const
{
QSize s = iconSize();
if (s.width() <= 0)
return 32;
return s.width();
}
};
@@ -23,7 +23,7 @@ public:
QMap<QString, std::vector<QLabel*>>& plcViwerLineedits)
: groupName(groupNameIN) {
QLabel* groupNameLabel = new QLabel(groupNameIN);
groupNameLabel = new QLabel(groupNameIN);
groupNameLabel->setFont(font1);
groupNameLabel->setStyleSheet("color: white;");
@@ -38,7 +38,7 @@ public:
QMap<int, std::vector<QLabel*>>& plcViwerLineedits)
: groupName(groupNameIN) {
QLabel* groupNameLabel = new QLabel(groupNameIN);
groupNameLabel = new QLabel(groupNameIN);
groupNameLabel->setFont(font1);
groupNameLabel->setStyleSheet("color: white;");
@@ -71,4 +71,5 @@ public:
QWidget* widget = new QWidget();
QHBoxLayout* layout = new QHBoxLayout(widget);
QMap<QString, QLabel*> map;
QLabel* groupNameLabel = nullptr;
};
+2 -2
View File
@@ -170,7 +170,7 @@ protected:
m_plot->replot();
QWidget::leaveEvent(event);
}
#if switchToTongYongUi == 1
void paintEvent(QPaintEvent* event) override {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
@@ -183,7 +183,7 @@ protected:
painter.setPen(Qt::NoPen);
painter.drawRoundedRect(r, radius, radius);
}
#endif
private slots:
void editJsonParameters() {
JsonParamTableEditor* dlg = new JsonParamTableEditor(this);
@@ -179,7 +179,7 @@ private:
}
void initChart() {
barSet = new QBarSet(QString::fromUtf8("数量"));
barSet = new QBarSet(TR("数量"));
barSet->setColor(QColor(255, 0, 0)); // 红色柱体
barSet->setBorderColor(QColor(255, 255, 255)); // 白色边框
@@ -191,8 +191,9 @@ private:
}
if (index < 0 || index >= categories.size())
return;
const QString text = QString::fromUtf8("* %1 数量: %2")
const QString text = QString::fromUtf8("* %1 %2 %3")
.arg(categories.at(index))
.arg(TR("数量"))
.arg(barSet->at(index));
buttomBarSetTipLabel.setText(text);
});
@@ -314,8 +315,13 @@ public:
histogramWidget* nightHistogramWidget;
dayNightShiftHistogramWidget() {
QString ymd = QDate::currentDate().toString("yyyy-MM-dd");
dayHistogramWidget = new histogramWidget(ymd + TR("白班") + "(08:30-20:30)");
nightHistogramWidget = new histogramWidget(ymd + TR("夜班") + "(20:30-08:30)");
dayHistogramWidget = new histogramWidget(ymd + TR("白班")
+ "(" + QString("%1:%2").arg(dayShiftHour, 2, 10, QChar('0')).arg(dayShiftMin, 2, 10, QChar('0'))
+ "-" + QString("%1:%2").arg(nightShiftHour, 2, 10, QChar('0')).arg(nightShiftMin, 2, 10, QChar('0')) + ")");
nightHistogramWidget = new histogramWidget(ymd + TR("夜班")
+ "(" + QString("%1:%2").arg(nightShiftHour, 2, 10, QChar('0')).arg(nightShiftMin, 2, 10, QChar('0'))
+ "-" + QString("%1:%2").arg(dayShiftHour, 2, 10, QChar('0')).arg(dayShiftMin, 2, 10, QChar('0')) + ")");
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
@@ -324,8 +330,13 @@ public:
BindTRLambda([this]() {
QString ymd = QDate::currentDate().toString("yyyy-MM-dd");
dayHistogramWidget->setTitle(ymd + TR("白班") + "(08:30-20:30)");
nightHistogramWidget->setTitle(ymd + TR("夜班") + "(20:30-08:30)");
dayHistogramWidget->setTitle(ymd + TR("白班")
+ "(" + QString("%1:%2").arg(dayShiftHour, 2, 10, QChar('0')).arg(dayShiftMin, 2, 10, QChar('0'))
+ "-" + QString("%1:%2").arg(nightShiftHour, 2, 10, QChar('0')).arg(nightShiftMin, 2, 10, QChar('0')) + ")");
nightHistogramWidget->setTitle(ymd + TR("夜班")
+ "(" + QString("%1:%2").arg(nightShiftHour, 2, 10, QChar('0')).arg(nightShiftMin, 2, 10, QChar('0'))
+ "-" + QString("%1:%2").arg(dayShiftHour, 2, 10, QChar('0')).arg(dayShiftMin, 2, 10, QChar('0')) + ")");
});
}
//defects为空 输入为修改量
@@ -336,11 +347,15 @@ public:
if (dayShiftResult.isShiftChanged) {
if (dayShiftResult.isDay == true) {
dayHistogramWidget->clearAllData();
dayHistogramWidget->setTitle(dayShiftResult.shiftDate + TR("白班") + "(08:30-20:30)");
dayHistogramWidget->setTitle(dayShiftResult.shiftDate + TR("白班")
+ "(" + QString("%1:%2").arg(dayShiftHour, 2, 10, QChar('0')).arg(dayShiftMin, 2, 10, QChar('0'))
+ "-" + QString("%1:%2").arg(nightShiftHour, 2, 10, QChar('0')).arg(nightShiftMin, 2, 10, QChar('0')) + ")");
}
else {
nightHistogramWidget->clearAllData();
nightHistogramWidget->setTitle(dayShiftResult.shiftDate + TR("夜班") + "(20:30-08:30)");
nightHistogramWidget->setTitle(dayShiftResult.shiftDate + TR("夜班")
+ "(" + QString("%1:%2").arg(nightShiftHour, 2, 10, QChar('0')).arg(nightShiftMin, 2, 10, QChar('0'))
+ "-" + QString("%1:%2").arg(dayShiftHour, 2, 10, QChar('0')).arg(dayShiftMin, 2, 10, QChar('0')) + ")");
}
}
@@ -126,9 +126,9 @@ private:
totalEdit->setText(QString::number(total));
okEdit->setText(QString::number(good));
ngEdit->setText(QString::number(ng));
double rate = total > 0 ? (double)good / total : 0.0;
rateEdit->setText(QString::number(rate * 100, 'f', 1) + "%");
pie->setGoodRate(rate, total > 0);
double goodRate = total > 0 ? (double)good / total : 0.0;
rateEdit->setText(QString::number(goodRate * 100, 'f', 3) + "%");
pie->setGoodRate(goodRate, total > 0);
}
void clearAllData() {
QMutexLocker locker(&mutex);
@@ -242,7 +242,8 @@ private:
if (GlobalMemoryStatusEx(&memInfo))
{
return ((double)(memInfo.ullTotalPageFile - memInfo.ullAvailPageFile) / (double)memInfo.ullTotalPageFile) * 100;
return std::min(((double)(memInfo.ullTotalPageFile - memInfo.ullAvailPageFile) / (double)memInfo.ullTotalPageFile) * 100,
((double)(memInfo.ullTotalPhys - memInfo.ullAvailPhys) / (double)memInfo.ullTotalPhys) * 100);
}
return -1.0;
}
+3 -20
View File
@@ -52,7 +52,7 @@ public:
//scene->addItem(rectGroup);
setScene(scene);
#if switchToTongYongUi == true
setStyleSheet(R"(
QGraphicsView#SonGraphicsView {
background: rgb(32, 36, 40);
@@ -62,15 +62,6 @@ public:
}
)");
#else
setStyleSheet(R"(
QGraphicsView#SonGraphicsView {
background: transparent;
background-image: url(:/icon/icon/waitting.png);
background-repeat: no-repeat;
background-position: center center;
})");
#endif
}
void setImg(const QPixmap& pixmap) {
@@ -588,15 +579,7 @@ protected:
}
}
private:
#if switchToTongYongUi == 0
void paintEvent(QPaintEvent* event) override {
QWidget::paintEvent(event);
QPainter painter(this);
painter.setPen(QPen(Qt::white, 1));
painter.drawRect(rect().adjusted(1, 1, -2, -2));
}
#else
void paintEvent(QPaintEvent* event) override {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
@@ -609,7 +592,7 @@ private:
painter.setPen(Qt::NoPen);
painter.drawRoundedRect(r, radius, radius);
}
#endif
int in = 0;
QLabel* label;
SonGraphicsView* viewer;
@@ -656,7 +639,7 @@ class ImageWidgetManager :public QWidget
Q_OBJECT
public:
ImageWidget* createViewer(QString windowName, std::vector<std::string> viewers, int xPos = -1, int yPos = -1){
ImageWidget* ImageView = new ImageWidget(this,TR(windowName.toStdString()));
ImageWidget* ImageView = new ImageWidget(this,windowName);
ImageWidgetList.push_back(ImageView);
map.insert({ windowName,ImageView });
if (xPos < 0 || yPos < 0){
@@ -56,7 +56,7 @@ public:
QHBoxLayout* topLayout = new QHBoxLayout();
topLayout->setAlignment(Qt::AlignLeft);
QLabel* modeSwitchTitle = new QLabel(TR("模式选择:"));
modeSwitchTitle = new QLabel(TR("模式选择:"));
modeSwitchTitle->setStyleSheet(
"QLabel {"
"font-family: 'Microsoft YaHei';"
@@ -92,6 +92,10 @@ public:
}
});
layout->addLayout(topLayout, 0, 0);
BindTRLambda([this]() {
this->setWindowTitle(TR("执行命令"));
modeSwitchTitle->setText(TR("模式选择:"));
});
}
signals:
void excuteCommandPreset(QString uuid);
@@ -101,7 +105,10 @@ public slots:
if (type == runCommandWidgetType::PlcSim) {
QListWidgetItem* item = new QListWidgetItem(btnWidgetsPlcSim);
btnWidgetsPlcSim->addItem(item);
fancyButtonText* funcBtn = new fancyButtonText(btnName, 0, 0, tip);
fancyButtonText* funcBtn = new fancyButtonText(TR(btnName.toStdString()), 0, 0, tip);
QObject::connect(fSignalHub::getInstance(), &fSignalHub::languageChanged, this, [funcBtn, btnName, tip]() {
funcBtn->setText(TR(btnName.toStdString()));
});
btnWidgetsPlcSim->setItemWidget(item, funcBtn);
connect(funcBtn, &fancyButtonText::clicked, this, [this, uuid]() {
QMessageBox::StandardButton reply;
@@ -120,6 +127,9 @@ public slots:
QListWidgetItem* item = new QListWidgetItem(btnWidgetsDirect);
btnWidgetsDirect->addItem(item);
fancyButtonText* funcBtn = new fancyButtonText(btnName, 0, 0, tip);
QObject::connect(fSignalHub::getInstance(), &fSignalHub::languageChanged, this, [funcBtn, btnName, tip]() {
funcBtn->setText(TR(btnName.toStdString()));
});
btnWidgetsDirect->setItemWidget(item, funcBtn);
connect(funcBtn, &fancyButtonText::clicked, this, [this, uuid]() {
QMessageBox::StandardButton reply;
@@ -136,7 +146,7 @@ public slots:
}
}
void addPlcValueViewer(const QString& groupName, const std::vector<std::pair<QString, int>>& viewerNameAndAddress) {
auto viewer = new valueViewer(groupName, viewerNameAndAddress, plcViwerLineedits);
auto viewer = new valueViewer(TR(groupName.toStdString()), viewerNameAndAddress, plcViwerLineedits);
// 添加到 QListView 中
QStandardItem* item = new QStandardItem();
// 根据内部 widget 的 sizeHint 设置行高
@@ -146,6 +156,9 @@ public slots:
plcValueViewerList->setIndexWidget(index, viewer->widget);
xExtraSize = std::max(groupName.size() * 20, xExtraSize);
this->setFixedSize(sizeX + xExtraSize, sizeY);
QObject::connect(fSignalHub::getInstance(), &fSignalHub::languageChanged, this, [viewer, groupName]() {
viewer->groupNameLabel->setText(TR(groupName.toStdString()));
});
}
void refreshPlcValueViewer(const int& address, const QString& value) {
for (auto& lineedit : plcViwerLineedits[address]) {
@@ -167,6 +180,7 @@ protected:
}
private:
QLabel* modeSwitchTitle;
CircularSwitch modeSwitch;
int sizeX = 1100, sizeY = 720,xExtraSize = 0;
QListWidget* btnWidgetsPlcSim, * btnWidgetsDirect;
@@ -21,7 +21,7 @@
#include <exception>
#include <appConfig/auth.hpp>
#include <appConfig/appConfig.h>
#include <appConfig/translation.h>
#include <translation.h>
#include <uiModule/components/ScrollingLabel.h>
@@ -36,14 +36,14 @@ public:
this->config = config;
}
void startAutoLogout() {
void startAutoLogout(int duration = 180) {
loginTime = QDateTime::currentDateTime();
lastDurationSec = duration;
if (!autoLogoutTimer) {
autoLogoutTimer = new QTimer(this);
connect(autoLogoutTimer, &QTimer::timeout, this, &AutoLogoutManager::onAutoLogout);
}
autoLogoutTimer->start(180000); // 3分钟
autoLogoutTimer->start(duration * 1000);
if (!remainingTimeTimer) {
remainingTimeTimer = new QTimer(this);
@@ -77,7 +77,7 @@ private slots:
if (loginTime.isValid()) {
QDateTime currentTime = QDateTime::currentDateTime();
qint64 elapsedSeconds = loginTime.secsTo(currentTime);
qint64 remainingSeconds = 180 - elapsedSeconds;
qint64 remainingSeconds = lastDurationSec - elapsedSeconds;
if (remainingSeconds > 0) {
int minutes = remainingSeconds / 60;
@@ -95,6 +95,7 @@ signals:
void userLoggedOut();
private:
std::atomic<int> lastDurationSec;
QTimer* autoLogoutTimer = nullptr;
QTimer* remainingTimeTimer = nullptr;
QDateTime loginTime;
@@ -108,7 +109,7 @@ class UserManagerDialog : public QDialog {
public:
explicit UserManagerDialog(appConfig* configIN, AutoLogoutManager* autoLogoutMgr, QTimer* updateTimer, QWidget* parent = nullptr)
: QDialog(parent), config(configIN), autoLogoutManager(autoLogoutMgr), globalUpdateTimer(updateTimer) {
setWindowTitle(TR("User Management"));
setWindowTitle(TR("用户管理"));
setFixedSize(400, 500);
setModal(true);
@@ -609,6 +610,13 @@ public:
}
}
// 临时权限提升方法
void useTemporaryPermission(const QString& accountName, int durationSeconds, int level) {
config->Authorization.useTemporaryPermission(accountName, durationSeconds, level);
autoLogoutManager->startAutoLogout(durationSeconds);
updateUserDisplay();
}
private slots:
void showUserManagerDialog() {
UserManagerDialog* dialog = new UserManagerDialog(config, autoLogoutManager, globalUpdateTimer, this);
+1 -1
View File
@@ -30,7 +30,7 @@ public:
saveJsonFromPath("./config/setting.json", settingJson);
saveOrderedJsonFromPath("./config/"+ objectSelect +".json", missionJson);
auto trans = &Translation::getInstance();
trans->saveToFile("./config/translation.json");
Translation::getInstance().saveToFile("./config/translation.json");
}
runtimeDataCacheStore* cacheStore;//配置缓存 将部分界面数据缓存到数据库中
std::atomic<bool> exitWithRestart = false;//是否重启后退出
+24
View File
@@ -16,6 +16,7 @@
#include <vector>
#include <QMessageBox>
#include <QWidget>
#include <QTimer>
namespace Auth {
@@ -569,6 +570,29 @@ namespace Auth {
int getUserCount() const {
return static_cast<int>(users.size());
}
// 临时权限提升方法(即使账号不存在也允许临时使用,结束后恢复到GUEST权限)
void useTemporaryPermission(const QString& accountName, int durationSeconds, int level) {
std::string username = accountName.toStdString();
// 临时提升权限
UserLevel targetLevel = static_cast<UserLevel>(level);
currentLoggedUser = username;
currentUserLevel = targetLevel;
isLoggedIn = true;
std::cout << "Temporary permission granted to " << username
<< " at level " << userLevelToString(targetLevel)
<< " for " << durationSeconds << " seconds." << std::endl;
// 定时恢复为GUEST权限
QTimer::singleShot(durationSeconds * 1000, [this]() {
currentLoggedUser = "";
currentUserLevel = UserLevel::GUEST;
isLoggedIn = false;
std::cout << "Temporary permission has expired, restored to GUEST." << std::endl;
});
}
};
// 权限验证辅助函数
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -59,5 +59,8 @@ signals:
void getAllParams(std::vector<std::pair<QString, QVariant>>* params);
void editParam(QString group, QString name, QVariant value);
void saveMissionJson();
void languageChanged(const std::string& language);
};
};
#define BindTR(languageChangedFunc) QObject::connect(fSignalHub::getInstance(), &fSignalHub::languageChanged,this, &languageChangedFunc);
#define BindTRLambda(languageChangedFunc) QObject::connect(fSignalHub::getInstance(), &fSignalHub::languageChanged,this, languageChangedFunc);
+48
View File
@@ -1,6 +1,11 @@
#include "logService.h"
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QFile>
#include <QDateTime>
#include <filesystem>
#include <global.h>
logService* logService::_instance = nullptr;
std::mutex logService::_instanceMutex;
@@ -67,10 +72,53 @@ logService::logService(){
}
});
// 清理超出保留天数的旧日志文件
cleanupOldLogs();
} catch (const std::exception& e) {
qDebug() << "Failed to initialize logService:" << e.what();
}
}
// 删除超出保留天数的旧日志文件
void logService::cleanupOldLogs() {
try {
int saveDays = logSaveDays;
if (saveDays <= 0) {
return;
}
QDir logDir(QString::fromStdString(logBasePath));
if (!logDir.exists()) {
return;
}
QDateTime deadline = QDateTime::currentDateTime().addDays(-saveDays);
const QFileInfoList fileList = logDir.entryInfoList(QStringList() << "*.log" << "*_*-*-*.log", QDir::Files, QDir::Name);
int deleteCount = 0;
for (const auto& fileInfo : fileList) {
if (fileInfo.lastModified() < deadline) {
if (QFile::remove(fileInfo.absoluteFilePath())) {
deleteCount++;
qDebug() << "[日志清理] 删除旧日志文件:" << fileInfo.fileName();
} else {
qWarning() << "[日志清理] 删除失败:" << fileInfo.absoluteFilePath();
}
}
}
if (deleteCount > 0) {
qDebug() << "[日志清理] 完成,共删除" << deleteCount << "个旧日志文件";
}
}
catch (const std::exception& e) {
qCritical() << "[日志清理] 异常:" << e.what();
}
catch (...) {
qCritical() << "[日志清理] 未知异常";
}
}
logService::~logService() {
try {
_stdConsoleRedirector.reset();
+4 -1
View File
@@ -13,7 +13,7 @@
#include "spdLogSupport.hpp"
#include "stdLogRediretor.hpp"
#include "qtLogRediretor.h"
#include <translation.h>
static const std::string logBasePath = "./Log/";
class logToQtWidget : public QObject
{
@@ -65,6 +65,9 @@ public:
// 获取日志组件
logToQtWidget* getLogWidget() const { return _logToQtWidget.get(); }
// 删除超出保留天数的旧日志文件
void cleanupOldLogs();
private:
std::unique_ptr<qtRediretor> _qtRediretor;
std::unique_ptr<stdConsoleRedirector> _stdConsoleRedirector;
+20 -16
View File
@@ -267,7 +267,6 @@ void connectFrontSignals(appCore* _core, MainWindow* _MainWindow, fSignalHub* br
//BackEnd To Front
//等待界面
//波形图
QObject::connect(bridge, &fSignalHub::triggerAndCaptureImage,
_core->_backEnd.backendPtr, &IBackend::triggerAndCaptureImage,Qt::DirectConnection);
QObject::connect(bridge, &fSignalHub::getCameraNames,
@@ -292,7 +291,9 @@ void connectFrontSignals(appCore* _core, MainWindow* _MainWindow, fSignalHub* br
void connectBackSignals(appCore* _core, MainWindow* _MainWindow, bSignalHub* bridge) {
//等待界面
QObject::connect(bridge, &bSignalHub::loadingWindow_appendInitLog,
_MainWindow, [=](const QString& text, const QColor& color) {_MainWindow->_loadingWidget.appendInitLog(text, color); });
_MainWindow, [=](const QString& text, const QColor& color) {
_MainWindow->_loadingWidget.appendInitLog(TR(text.toStdString()), color);
});
QObject::connect(bridge, &bSignalHub::loadingWindow_modifyLog,
_MainWindow, [=](int col, const QString& text, const QColor& color) {_MainWindow->_loadingWidget.modifyLog(col, text, color); });
QObject::connect(bridge, &bSignalHub::loadingWindow_clearLog,
@@ -376,10 +377,13 @@ void connectBackSignals(appCore* _core, MainWindow* _MainWindow, bSignalHub* bri
QObject::connect(bridge, &bSignalHub::waitForFrontendInitComfirm,
_core, [=](const QString& message,const std::vector<QString>& options, std::shared_ptr<std::atomic<bool>> recivedResult, std::shared_ptr<std::atomic<int>> Result) {
std::vector<QString> optionsCopy = options;
optionsCopy.push_back("退出程序");
std::vector<QString> optionsCopy;
for (auto& option : options) {
optionsCopy.push_back(TR(option.toStdString()));
}
optionsCopy.push_back(TR("退出程序"));
QMessageBox msgBox(_MainWindow);
msgBox.setWindowTitle(QObject::tr("确认"));
msgBox.setWindowTitle(TR("确认"));
msgBox.setIcon(QMessageBox::Question);
msgBox.setText(message);
@@ -392,16 +396,16 @@ void connectBackSignals(appCore* _core, MainWindow* _MainWindow, bSignalHub* bri
// options 为空时兜底,避免弹窗无按钮无法关闭
if (optionButtons.empty()) {
optionButtons.push_back(msgBox.addButton(QObject::tr("确定"), QMessageBox::AcceptRole));
optionButtons.push_back(msgBox.addButton(TR("确定"), QMessageBox::AcceptRole));
}
msgBox.exec();
int selectedOptionId = -1;
QAbstractButton* clicked = msgBox.clickedButton();
for (int i = 0; i < static_cast<int>(optionButtons.size()); ++i) {
if (optionButtons[i] == clicked) {
if (optionButtons[i]->text() == "退出程序")
if (optionButtons[i]->text() == TR("退出程序"))
exit(999);
selectedOptionId = i;
break;
@@ -443,6 +447,12 @@ void connectBackSignals(appCore* _core, MainWindow* _MainWindow, bSignalHub* bri
[=]() {
emit bridge->reloadAllCameraAsVirtual();
});
QObject::connect(fSignalHub::getInstance(), &fSignalHub::languageChanged,
_core->_backEnd.backendPtr, &IBackend::launguageChanged);
QObject::connect(bridge, &bSignalHub::updateUserPermission,
_MainWindow->_displayWidget->_userSwitch, &userSwitch::useTemporaryPermission);
}
std::unique_ptr<google_breakpad::ExceptionHandler> initBreakPad() {
std::wstring dumpPath = L"./dumps";
@@ -470,10 +480,11 @@ int main(int argc, char* argv[])
QApplication a(argc, argv);
//如果有同名文件则判断是否要启动软件
#if offlineMode == 0
if (!handleSameNameProcessOnStartup(nullptr)) {
return 0;
}
#endif
appCore _core;
auto& config = appConfig::getInstance();
config.consoleHandle = hWnd;
@@ -533,12 +544,5 @@ int main(int argc, char* argv[])
qCritical() << "==============================";
return -3;
}
catch (...) {
// 捕获所有其他异常
qCritical() << "========== 未知异常捕获 ==========";
qCritical() << "捕获到未知类型的异常";
qCritical() << "==============================";
return -2;
}
return -99;
}
-11
View File
@@ -1,11 +0,0 @@
# CMakeList.txt: 遍历文件夹 添加子项目
set(OUR_LIBS "")
SUBDIRENAMES(OUR_LIBS ${CMAKE_CURRENT_SOURCE_DIR} "" OFF)
message("OUR_LIBS :" ${OUR_LIBS})
# 遍历子库
foreach(dll ${OUR_LIBS})
add_subdirectory(${dll})
endforeach(dll ${})
@@ -248,8 +248,8 @@ bool ALGO_BaoMoHouDiBu(std::shared_ptr <detData> data, baoMoHouDiBuDetParams* _d
}
int Rate = (Paint.rows > Paint.cols ? Paint.cols : Paint.rows) / 20;
TextRendererZH textPainter1(Paint.cols, Paint.rows, Rate);
textPainter1.putText(Paint, "产品条码:" + data->SNcode, cv::Point(50, Paint.rows - Rate), cv::Scalar(0, 255, 255));
textPainter1.putText(Paint, "获取图像时间:" + createTime.toString("yyyy/MM/dd hh:mm:ss").toStdString(), cv::Point(50, Paint.rows - (Rate * 2)), cv::Scalar(0, 255, 255));
textPainter1.putText(Paint, data->service->translateManager->translate("产品条码:") + data->SNcode, cv::Point(50, Paint.rows - Rate), cv::Scalar(0, 255, 255));
textPainter1.putText(Paint, data->service->translateManager->translate("获取图像时间:") + createTime.toString("yyyy/MM/dd hh:mm:ss").toStdString(), cv::Point(50, Paint.rows - (Rate * 2)), cv::Scalar(0, 255, 255));
data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(hResult1.resultX));//发送结果
data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_" + QByteArray::number(hResult1.resultY));//发送结果
@@ -27,7 +27,7 @@ public:
[](std::map<std::string, HObject>& inputHObj,
std::map<std::string, HTuple>& inputHTuple,
const input& in) {
inputHObj["in_Image"] = in.in_Image;
inputHObj["in_ImageX"] = in.in_Image;
inputHTuple["in_Par"] = in.in_Par;
inputHTuple["in_FilePath"] = in.in_FilePath;
inputHTuple["in_Resolution"] = in.in_Resolution;
@@ -56,13 +56,12 @@ public:
//handleAiModelWithGuard testModel;
void onConfigReloaded() {
halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_581.hdpl", bridge);
//_serviceManager->_handleAiManager->loadSegModel("./handleAiModel/Seg_2026-3-25_1-05-47_2.130355.BHD", 16, 0, "铜Seg90度极耳定位网");
//_serviceManager->_handleAiManager->loadSegModel("./handleAiModel/Seg_2026-3-25_1-05-47_2.130355.BHD", 16, 0, "铜Seg90度焊印定位网");
_serviceManager->_handleAiManager->loadSegModel("./handleAiModel/Seg_2026-3-25_1-05-47_2.130355.BHD", 16, 0, "铜Seg90度极耳定位网");
_serviceManager->_handleAiManager->loadSegModel("./handleAiModel/Seg_2026-3-25_1-05-47_2.130355.BHD", 16, 0, "铜Seg90度焊印定位网");
}
bool initCustom() {
#pragma region 注册触发器回调
_station_1.init("Camera1","包膜后底托位置度检测-左-相机12",20400,1,2, &_baoMoHouDiBuDetParams,_serviceManager,bridge, ALGO_BaoMoHouDiBu);
_station_2.init("Camera2", "包膜后底托位置度检测-右-相机13",20200,1,2,&_baoMoHouDiBuDetParams, _serviceManager, bridge, ALGO_BaoMoHouDiBu);
#pragma endregion
@@ -305,6 +305,8 @@ Hd_CameraModule_HIK_hard_PCIE::Hd_CameraModule_HIK_hard_PCIE() {
}
Hd_CameraModule_HIK_hard_PCIE::~Hd_CameraModule_HIK_hard_PCIE()
{
runFlag = false;
callbackCollectTimeoutTimer.stop();
CloseDevice();
if (n_mutex)