369 lines
16 KiB
C++
369 lines
16 KiB
C++
#include <windows.h>
|
|
#include "client/windows/handler/exception_handler.h"
|
|
#include <global.h>
|
|
#include <QApplication>
|
|
#include <QException>
|
|
#include <QSystemSemaphore>
|
|
#include <QSharedMemory>
|
|
#include <QMessageLogger>//日志
|
|
#include <QDebug>
|
|
#include <QDateTime>
|
|
#include <qmessagebox.h>
|
|
#include <qpushbutton.h>
|
|
#include <qlineedit.h>
|
|
#include <qlabel.h>
|
|
#include <qcombobox.h>
|
|
#include <QMetaType>
|
|
|
|
#include <appCore.hpp>
|
|
#include <QGuiApplication>
|
|
#include <windows.h> // GetModuleFileName, ShellExecuteEx, SW_NORMAL 等
|
|
#include <tchar.h> // TCHAR 相关操作
|
|
#include <shellapi.h> // SHELLEXECUTEINFO 结构体
|
|
#include <iostream> // std::cout (如果需要打印信息)
|
|
#include <tlhelp32.h> // CreateToolhelp32Snapshot, Process32First, Process32Next
|
|
#include <filesystem> // std::filesystem::path
|
|
#include <thread> // std::thread for restart functionality
|
|
#include <chrono> // std::chrono for time operations
|
|
#include <memory> // std::unique_ptr for smart pointer management
|
|
|
|
#include <GUI/mainwindow.h>
|
|
#include <frontendSignalHub.h>
|
|
#pragma region 程序启动功能
|
|
|
|
// 获取当前exe文件名
|
|
std::string getCurrentExeName() {
|
|
char exePath[MAX_PATH];
|
|
GetModuleFileNameA(NULL, exePath, MAX_PATH);
|
|
std::filesystem::path path(exePath);
|
|
return path.filename().string();
|
|
}
|
|
|
|
// 枚举窗口回调函数,用于置顶指定进程的窗口
|
|
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
|
|
DWORD* targetPid = (DWORD*)lParam;
|
|
DWORD windowPid;
|
|
GetWindowThreadProcessId(hwnd, &windowPid);
|
|
|
|
if (windowPid == *targetPid && IsWindowVisible(hwnd)) {
|
|
// 将窗口置顶并激活
|
|
SetForegroundWindow(hwnd);
|
|
ShowWindow(hwnd, SW_RESTORE);
|
|
return FALSE; // 停止枚举
|
|
}
|
|
return TRUE; // 继续枚举
|
|
}
|
|
|
|
// 检查应用是否已在运行,如果是则置顶
|
|
bool appIsRuning(){
|
|
std::string currentExeName = getCurrentExeName();
|
|
DWORD currentPid = GetCurrentProcessId();
|
|
|
|
// 创建进程快照
|
|
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
|
if (hSnapshot == INVALID_HANDLE_VALUE) {
|
|
return true; // 如果无法创建快照,允许启动
|
|
}
|
|
|
|
PROCESSENTRY32 pe32;
|
|
pe32.dwSize = sizeof(PROCESSENTRY32);
|
|
|
|
bool foundOtherInstance = false;
|
|
DWORD otherInstancePid = 0;
|
|
|
|
// 遍历进程
|
|
if (Process32First(hSnapshot, &pe32)) {
|
|
do {
|
|
// 比较进程名(忽略大小写)
|
|
if (_stricmp(pe32.szExeFile, currentExeName.c_str()) == 0 &&
|
|
pe32.th32ProcessID != currentPid) {
|
|
foundOtherInstance = true;
|
|
otherInstancePid = pe32.th32ProcessID;
|
|
break;
|
|
}
|
|
} while (Process32Next(hSnapshot, &pe32));
|
|
}
|
|
|
|
CloseHandle(hSnapshot);
|
|
|
|
if (foundOtherInstance) {
|
|
// 找到其他实例,将其置顶
|
|
EnumWindows(EnumWindowsProc, (LPARAM)&otherInstancePid);
|
|
return false; // 返回false表示已有实例在运行
|
|
}
|
|
|
|
return true; // 没有其他实例在运行
|
|
}
|
|
#pragma endregion
|
|
static bool dumpCallback(const wchar_t* dump_path,
|
|
const wchar_t* minidump_id,
|
|
void* context,
|
|
EXCEPTION_POINTERS* exinfo,
|
|
MDRawAssertionInfo* assertion,
|
|
bool succeeded) {
|
|
|
|
QString dumpFile = QString::fromWCharArray(dump_path) + "\\" + QString::fromWCharArray(minidump_id) + ".dmp";
|
|
QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
|
|
|
|
if (succeeded) {
|
|
qDebug() << "========== 程序崩溃 ==========" << timestamp;
|
|
qDebug() << "崩溃转储文件已生成:" << dumpFile;
|
|
|
|
if (exinfo && exinfo->ExceptionRecord) {
|
|
DWORD exceptionCode = exinfo->ExceptionRecord->ExceptionCode;
|
|
qDebug() << "异常代码:" << Qt::hex << Qt::showbase << exceptionCode;
|
|
qDebug() << "异常地址:" << Qt::hex << Qt::showbase << (quintptr)exinfo->ExceptionRecord->ExceptionAddress;
|
|
|
|
// 解释常见的异常代码
|
|
QString exceptionType;
|
|
switch (exceptionCode) {
|
|
case EXCEPTION_ACCESS_VIOLATION: exceptionType = "访问违规 (Access Violation)"; break;
|
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: exceptionType = "数组越界 (Array Bounds Exceeded)"; break;
|
|
case EXCEPTION_DATATYPE_MISALIGNMENT: exceptionType = "数据类型未对齐 (Datatype Misalignment)"; break;
|
|
case EXCEPTION_FLT_DIVIDE_BY_ZERO: exceptionType = "浮点除零 (Float Divide by Zero)"; break;
|
|
case EXCEPTION_INT_DIVIDE_BY_ZERO: exceptionType = "整数除零 (Integer Divide by Zero)"; break;
|
|
case EXCEPTION_STACK_OVERFLOW: exceptionType = "栈溢出 (Stack Overflow)"; break;
|
|
case EXCEPTION_ILLEGAL_INSTRUCTION: exceptionType = "非法指令 (Illegal Instruction)"; break;
|
|
default: exceptionType = "未知异常"; break;
|
|
}
|
|
qDebug() << "异常类型:" << exceptionType;
|
|
|
|
// 记录更多上下文信息
|
|
if (exinfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION &&
|
|
exinfo->ExceptionRecord->NumberParameters >= 2) {
|
|
ULONG_PTR operation = exinfo->ExceptionRecord->ExceptionInformation[0];
|
|
ULONG_PTR address = exinfo->ExceptionRecord->ExceptionInformation[1];
|
|
QString operationType = (operation == 0) ? "读取" : (operation == 1) ? "写入" : "执行";
|
|
qDebug() << "访问类型:" << operationType << "地址:" << Qt::hex << Qt::showbase << address;
|
|
}
|
|
}
|
|
|
|
// 记录当前线程ID
|
|
qDebug() << "崩溃线程ID:" << GetCurrentThreadId();
|
|
qDebug() << "==============================";
|
|
|
|
// 程序崩溃后自动重启
|
|
qDebug() << "程序即将自动重启...";
|
|
|
|
} else {
|
|
qDebug() << "========== 崩溃转储失败 ==========" << timestamp;
|
|
qDebug() << "生成崩溃转储文件失败!";
|
|
qDebug() << "程序即将自动重启...";
|
|
qDebug() << "==============================";
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
|
|
|
restartProgram(false, true, 5000);
|
|
return succeeded;
|
|
}
|
|
bool checkResourceExists(const QString& resourcePath) {
|
|
if (QFile::exists(resourcePath)) {
|
|
qDebug() << "文件存在:" << resourcePath;
|
|
return true;
|
|
}
|
|
else {
|
|
qDebug() << "文件不存在:" << resourcePath;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void connectFrontSignals(appCore* _core, MainWindow* _MainWindow, fSignalHub* bridge) {
|
|
QObject::connect(_core->_logService->getLogWidget(), &logToQtWidget::stdLogSignal,
|
|
_MainWindow->_logWidget, &logWidget::recvStdLogMsg);
|
|
QObject::connect(_core->_logService->getLogWidget(), &logToQtWidget::qtLogSignal,
|
|
_MainWindow->_logWidget, &logWidget::recvQtLogMsg);
|
|
QObject::connect(_core->_logService->getLogWidget(), &logToQtWidget::mainWindowStdLogSignal,
|
|
_MainWindow->_displayWidget, &displayWidget::recvMainWindowStdLogMsg);
|
|
QObject::connect(_core->_logService->getLogWidget(), &logToQtWidget::loadingWindow_appendConsoleLog,
|
|
_MainWindow, [=](const QString& text) {_MainWindow->_loadingWidget.appendConsoleLog(text); });
|
|
//BackEnd To Front
|
|
//等待界面
|
|
|
|
//波形图
|
|
QObject::connect(bridge, &fSignalHub::triggerAndCaptureImage,
|
|
_core->_backEnd.backendPtr, &IBackend::triggerAndCaptureImage,Qt::DirectConnection);
|
|
QObject::connect(bridge, &fSignalHub::getCameraNames,
|
|
_core->_backEnd.backendPtr, &IBackend::getCameraNames, Qt::DirectConnection);
|
|
QObject::connect(bridge, &fSignalHub::getAllParams,
|
|
_core->_backEnd.backendPtr, &IBackend::getAllParams, Qt::DirectConnection);
|
|
QObject::connect(bridge, &fSignalHub::editParam,
|
|
_core->_backEnd.backendPtr, &IBackend::editParam, Qt::DirectConnection);
|
|
QObject::connect(bridge, &fSignalHub::saveMissionJson,
|
|
_core,[=]() {
|
|
auto& config = appConfig::getInstance();
|
|
config.missionJson["backend"] = _core->_backEnd.backendPtr->getBackendJson();
|
|
config.saveSettingJson();
|
|
}, Qt::DirectConnection);
|
|
|
|
QObject::connect(bridge, &fSignalHub::restartInit,[=]() {
|
|
appConfig::getInstance().exitWithRestart = true;
|
|
QApplication::quit();
|
|
});
|
|
}
|
|
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); });
|
|
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,
|
|
_MainWindow, [=]() {_MainWindow->_loadingWidget.clearLog(); });
|
|
QObject::connect(bridge, &bSignalHub::pieWidgetModifyData,
|
|
_MainWindow, [=](int modifyOkData, int modiftNgData) {_MainWindow->_displayWidget->_dsStatsWidget->modifyData(modifyOkData, modiftNgData); });
|
|
QObject::connect(bridge, &bSignalHub::histogramWidgetAddData,
|
|
_MainWindow, [=](int modifyOkData, int modiftNgData,std::vector<std::string>defects)
|
|
{_MainWindow->_displayWidget->_dayNightShiftHistogramWidget->addDefect(modifyOkData, modiftNgData,defects); });
|
|
QObject::connect(bridge, &bSignalHub::waveFormAddData,
|
|
_MainWindow, [=](QString windowName, double value)
|
|
{_MainWindow->_SummaryDataPage.addData(windowName, value); });
|
|
QObject::connect(bridge, &bSignalHub::setPlcConnectionError,
|
|
_MainWindow, [=]() {_MainWindow->_displayWidget->_toolBarWidget->_ConnectionWidget->setPlcDisconnected(); });
|
|
QObject::connect(bridge, &bSignalHub::setPlcConnectionOk,
|
|
_MainWindow, [=]() {_MainWindow->_displayWidget->_toolBarWidget->_ConnectionWidget->setPlcConnected(); });
|
|
QObject::connect(bridge, &bSignalHub::setCamConnectionError,
|
|
_MainWindow, [=]() {_MainWindow->_displayWidget->_toolBarWidget->_ConnectionWidget->setCamDisconnected(); });
|
|
QObject::connect(bridge, &bSignalHub::setCamConnectionOk,
|
|
_MainWindow, [=]() {_MainWindow->_displayWidget->_toolBarWidget->_ConnectionWidget->setCamConnected(); });
|
|
//登录界面
|
|
QObject::connect(bridge, &bSignalHub::initResult,
|
|
_MainWindow, &MainWindow::recvInitFinished);
|
|
QObject::connect(bridge, &bSignalHub::initResult,
|
|
_core, &appCore::recvInitFinished);
|
|
//展示界面//主界面
|
|
QObject::connect(bridge, &bSignalHub::displayWidget_sendImg,
|
|
_MainWindow, &MainWindow::recvPixmapImageWidget);
|
|
|
|
//波形图
|
|
QObject::connect(bridge, &bSignalHub::summaryData_addData,
|
|
_MainWindow, &MainWindow::slot_summaryData_addData);
|
|
|
|
//排障
|
|
QObject::connect(bridge, &bSignalHub::runCommand_addBtn,
|
|
&_MainWindow->_displayWidget->_runCommandWidget, &runCommandWidget::addBtn);
|
|
QObject::connect(&_MainWindow->_displayWidget->_runCommandWidget,
|
|
&runCommandWidget::excuteCommandPreset,
|
|
[=](QString uuid) {_core->_backEnd.backendPtr->excuteCliCommandPreset(uuid); });
|
|
QObject::connect(bridge, &bSignalHub::runCommand_addPlcValueViewer,
|
|
&_MainWindow->_displayWidget->_runCommandWidget, &runCommandWidget::addPlcValueViewer);
|
|
QObject::connect(bridge, &bSignalHub::runCommand_refeshPlcValueViewer,
|
|
&_MainWindow->_displayWidget->_runCommandWidget, &runCommandWidget::refreshPlcValueViewer);
|
|
|
|
//性能监测
|
|
QObject::connect(bridge, &bSignalHub::sendTriggerManagerLoopData,
|
|
_MainWindow->_displayWidget->_softwareDataDisplay, &softwareDataDisplay::onTriggerSateChanged);
|
|
|
|
QObject::connect(bridge, &bSignalHub::saveMissionJson,
|
|
_core,[=]() {
|
|
auto& config = appConfig::getInstance();
|
|
config.missionJson["backend"] = _core->_backEnd.backendPtr->getBackendJson();
|
|
config.saveSettingJson();
|
|
}, Qt::DirectConnection);
|
|
|
|
}
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
try {
|
|
#pragma region 设置显示控制栏
|
|
disableConsoleSelection();
|
|
HWND hWnd = GetConsoleWindow();
|
|
ShowWindow(hWnd, SW_SHOW);
|
|
#pragma endregion
|
|
#pragma region 启动breakPad崩溃捕获
|
|
std::wstring dumpPath = L"./dumps";
|
|
CreateDirectoryW(dumpPath.c_str(), nullptr);
|
|
|
|
// 初始化 breakpad
|
|
auto eh = std::make_unique<google_breakpad::ExceptionHandler>(
|
|
dumpPath,
|
|
/*FilterCallback*/ nullptr,
|
|
dumpCallback,
|
|
/*context*/ nullptr,
|
|
google_breakpad::ExceptionHandler::HANDLER_ALL
|
|
);
|
|
#pragma endregion
|
|
|
|
appCore _core;
|
|
auto& config = appConfig::getInstance();
|
|
config.consoleHandle = hWnd;
|
|
auto bridge = fSignalHub::getInstance(); // 创建 C++ 对象
|
|
//检测是否已运行
|
|
if (appIsRuning() == false)
|
|
exit(0);
|
|
//////////
|
|
#pragma region 注册信号槽变量
|
|
qRegisterMetaType<cv::Mat*>("matPtr");
|
|
qRegisterMetaType<std::string>("stdString");
|
|
qRegisterMetaType<historyPack>("historyPack");
|
|
qRegisterMetaType<Qt::GlobalColor>("QtGlobalColor");
|
|
|
|
qRegisterMetaType<showImgPack>("showImgPack");
|
|
qRegisterMetaType<initStatus>("initStatus");
|
|
qRegisterMetaType<cv::Mat>("mat");
|
|
qRegisterMetaType< std::vector<cv::Mat>*>("vecmatptr");
|
|
qRegisterMetaType<std::vector<std::string>>("vectorstring");
|
|
qRegisterMetaType<std::vector<std::pair<QString, int>>>("constvpqi");
|
|
qRegisterMetaType<PlcValueViewerStruct>("PlcValueViewerStruct");
|
|
qRegisterMetaType<std::vector<std::pair<QString, QVariant>>>("vecQStringQVarint");
|
|
qRegisterMetaType<std::vector<std::pair<QString, QVariant>>*>("vecQStringQVarintPtr");
|
|
#pragma endregion
|
|
QApplication a(argc, argv);
|
|
_core.initDatabase(); // 初始化数据库
|
|
QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + "/sqldrivers");
|
|
QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath());
|
|
MainWindow w(&config,bridge, config.missionJson);
|
|
w.show();
|
|
QThread::msleep(100);
|
|
// 创建后端对象
|
|
_core._backEnd.loadCore(config.dataBase, config.missionJson["backend"]); // 加载核心功能
|
|
connectFrontSignals(&_core, &w, bridge); // 连接前端UI的信号和槽
|
|
connectBackSignals(&_core, &w, &_core._backEnd.backendPtr->_signalHub); // 连接前端UI的信号和槽
|
|
w.registerQuitFunc([&]() {
|
|
auto& config = appConfig::getInstance();
|
|
_core._backEnd.releaseCore(config.missionJson["backend"]);
|
|
QThread::msleep(100);
|
|
if (config.dataBase) delete config.dataBase;
|
|
config.dataBase = nullptr;
|
|
});
|
|
|
|
int qtRes = a.exec();
|
|
if (config.exitWithRestart) {
|
|
ShowWindow(hWnd, SW_SHOW);
|
|
std::cout << "程序将在五秒后重启..." << std::endl;
|
|
restartProgram(false,true,5000);
|
|
}
|
|
qDebug() << "程序退出,返回值" << qtRes;
|
|
if (qtRes != 0) {
|
|
qDebug() << "返回值不为0,非正常退出,即将重启程序";
|
|
restartProgram(false, true, 5000);
|
|
}
|
|
QThread::msleep(200);
|
|
return qtRes;
|
|
}
|
|
catch (const std::exception& e) {
|
|
// C++标准异常处理
|
|
qCritical() << "========== C++异常捕获 ==========";
|
|
qCritical() << "异常信息:" << e.what();
|
|
qCritical() << "==============================";
|
|
restartProgram(false, true, 5000);
|
|
return -1;
|
|
}
|
|
catch (const QException& e) {
|
|
// Qt异常处理
|
|
qCritical() << "========== Qt异常捕获 ==========";
|
|
qCritical() << "Qt异常信息:" << e.what();
|
|
qCritical() << "==============================";
|
|
restartProgram(false, true, 5000);
|
|
return -3;
|
|
}
|
|
catch (...) {
|
|
// 捕获所有其他异常
|
|
qCritical() << "========== 未知异常捕获 ==========";
|
|
qCritical() << "捕获到未知类型的异常";
|
|
qCritical() << "==============================";
|
|
restartProgram(false, true, 5000);
|
|
return -2;
|
|
}
|
|
return -99;
|
|
} |