V20260829-2.6.1
完成框架目录和模块重构;新增核心、单例、工具、加密及框架 SDK;统一任务、相机和 RPC 接口,并增加离线检测、相机标定、备份恢复、版本管理、崩溃守护和心跳监控功能。
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
#pragma once
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QMutex>
|
||||
#include <QMutexLocker>
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <QWaitCondition>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <windows.h>
|
||||
|
||||
class RpcExeProcessThread : public QThread {
|
||||
public:
|
||||
explicit RpcExeProcessThread(QObject* parent = nullptr)
|
||||
: QThread(parent) {}
|
||||
|
||||
~RpcExeProcessThread() override {
|
||||
stopExe();
|
||||
}
|
||||
|
||||
bool startExe(const QString& exePath, const QStringList& args = QStringList()) {
|
||||
if (isRunning()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
exePath_ = resolvePath(exePath);
|
||||
args_ = args;
|
||||
stopping_.store(false);
|
||||
{
|
||||
QMutexLocker locker(&stateMutex_);
|
||||
startAttempted_ = false;
|
||||
startedOk_ = false;
|
||||
startError_.clear();
|
||||
}
|
||||
|
||||
if (!QFileInfo::exists(exePath_)) {
|
||||
qCritical() << "RPC server exe not found:" << exePath_;
|
||||
return false;
|
||||
}
|
||||
|
||||
start();
|
||||
QMutexLocker locker(&stateMutex_);
|
||||
if (!startAttempted_) {
|
||||
startCondition_.wait(&stateMutex_, 1500);
|
||||
}
|
||||
if (!startedOk_) {
|
||||
qWarning() << "RPC server process did not start:" << exePath_ << startError_;
|
||||
}
|
||||
return startedOk_;
|
||||
}
|
||||
|
||||
void stopExe(int timeoutMs = 1500) {
|
||||
stopping_.store(true);
|
||||
if (isRunning() && !wait(timeoutMs)) {
|
||||
qWarning() << "RPC server process thread stop timeout:" << exePath_;
|
||||
}
|
||||
}
|
||||
|
||||
QString exePath() const {
|
||||
return exePath_;
|
||||
}
|
||||
|
||||
protected:
|
||||
void run() override {
|
||||
QProcess process;
|
||||
HANDLE jobHandle = nullptr;
|
||||
|
||||
auto closeJobHandle = [&]() {
|
||||
if (jobHandle) {
|
||||
CloseHandle(jobHandle);
|
||||
jobHandle = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
auto assignProcessToJob = [&]() {
|
||||
closeJobHandle();
|
||||
const DWORD pid = process.processId();
|
||||
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
|
||||
if (!hProcess) {
|
||||
qWarning() << "Failed to open RPC server process:" << exePath_;
|
||||
return;
|
||||
}
|
||||
|
||||
jobHandle = CreateJobObject(nullptr, nullptr);
|
||||
if (!jobHandle) {
|
||||
qWarning() << "Failed to create job object";
|
||||
CloseHandle(hProcess);
|
||||
return;
|
||||
}
|
||||
|
||||
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {};
|
||||
jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
||||
SetInformationJobObject(jobHandle, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli));
|
||||
|
||||
if (!AssignProcessToJobObject(jobHandle, hProcess)) {
|
||||
qWarning() << "Failed to assign process to job object";
|
||||
}
|
||||
CloseHandle(hProcess);
|
||||
};
|
||||
|
||||
std::function<void()> startChild;
|
||||
startChild = [&]() {
|
||||
if (stopping_.load()) {
|
||||
quit();
|
||||
return;
|
||||
}
|
||||
if (process.state() != QProcess::NotRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
process.setProgram(exePath_);
|
||||
process.setArguments(args_);
|
||||
process.start();
|
||||
const bool ok = process.waitForStarted(1000);
|
||||
{
|
||||
QMutexLocker locker(&stateMutex_);
|
||||
startAttempted_ = true;
|
||||
startedOk_ = ok;
|
||||
startError_ = ok ? QString() : process.errorString();
|
||||
startCondition_.wakeAll();
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
qWarning() << "Process failed to start:" << exePath_ << process.errorString();
|
||||
if (!stopping_.load()) {
|
||||
QTimer::singleShot(1000, &process, startChild);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "RPC server process started:" << exePath_;
|
||||
assignProcessToJob();
|
||||
};
|
||||
|
||||
QObject::connect(&process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
||||
&process, [&](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
qWarning() << "RPC server process finished:" << exePath_
|
||||
<< "exitCode:" << exitCode << "exitStatus:" << exitStatus;
|
||||
closeJobHandle();
|
||||
if (stopping_.load()) {
|
||||
quit();
|
||||
}
|
||||
else {
|
||||
QTimer::singleShot(1000, &process, startChild);
|
||||
}
|
||||
});
|
||||
|
||||
QTimer stopTimer;
|
||||
stopTimer.setInterval(50);
|
||||
QObject::connect(&stopTimer, &QTimer::timeout, &process, [&]() {
|
||||
if (!stopping_.load()) {
|
||||
return;
|
||||
}
|
||||
if (process.state() == QProcess::Running) {
|
||||
process.kill();
|
||||
process.waitForFinished(500);
|
||||
}
|
||||
closeJobHandle();
|
||||
quit();
|
||||
});
|
||||
|
||||
stopTimer.start();
|
||||
startChild();
|
||||
exec();
|
||||
|
||||
if (process.state() == QProcess::Running) {
|
||||
process.kill();
|
||||
process.waitForFinished(500);
|
||||
}
|
||||
closeJobHandle();
|
||||
}
|
||||
|
||||
private:
|
||||
QString resolvePath(const QString& exePath) const {
|
||||
QFileInfo exeInfo(exePath);
|
||||
if (!exeInfo.isRelative()) {
|
||||
return exePath;
|
||||
}
|
||||
|
||||
QString resolved = QCoreApplication::applicationDirPath() + "/" + exePath;
|
||||
if (!QFileInfo::exists(resolved)) {
|
||||
resolved = QDir::currentPath() + "/" + exePath;
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
QString exePath_;
|
||||
QStringList args_;
|
||||
std::atomic<bool> stopping_{ true };
|
||||
QMutex stateMutex_;
|
||||
QWaitCondition startCondition_;
|
||||
bool startAttempted_ = false;
|
||||
bool startedOk_ = false;
|
||||
QString startError_;
|
||||
};
|
||||
Reference in New Issue
Block a user