From f82af9201a776bd239139dcd110fdf58434d8045 Mon Sep 17 00:00:00 2001 From: RCi <15089880081@163.com> Date: Fri, 19 Sep 2025 12:38:05 +0800 Subject: [PATCH] =?UTF-8?q?rpcserver=E5=88=9D=E7=89=88=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detProcess.hpp | 216 ++++++ include/global.h | 2 +- include/interface.h | 1 + rpcModule/rpc_finsTcp/rpc_finsTcp.cpp | 1 + src/Core/backendInterface.cpp | 3 + src/Core/detProcess.hpp | 34 +- src/core/back/CLIManager.h | 15 +- src/core/back/missionBase.hpp | 3 + src/core/jsonRpcServer.h | 728 ++++++++++++++++++ src/launcher/appConfig/appConfig.h | 2 +- src/launcher/appCore.hpp | 12 +- src/launcher/main.cpp | 5 +- 12 files changed, 1002 insertions(+), 20 deletions(-) create mode 100644 enc_temp_folder/133fe44af4994a7ec31e3fea081bbff/detProcess.hpp create mode 100644 src/core/jsonRpcServer.h diff --git a/enc_temp_folder/133fe44af4994a7ec31e3fea081bbff/detProcess.hpp b/enc_temp_folder/133fe44af4994a7ec31e3fea081bbff/detProcess.hpp new file mode 100644 index 0000000..171c00b --- /dev/null +++ b/enc_temp_folder/133fe44af4994a7ec31e3fea081bbff/detProcess.hpp @@ -0,0 +1,216 @@ +#include +#include +#include +#include +#include +#include +#include +class detProcess : public QThread +{ +public: + CsvWriter* _csvWriter = nullptr; + missionResultHandle* _missionResultHandle = nullptr; + ServiceManager _serviceManager; + CLIManager* _cliManager; // 命令行接口管理器 + rpc::JsonRpcServer _rpcServer; + bool initOnce = false; + detProcess(SQLiteDB* dbIn, nlohmann::ordered_json& jsonIn, qtFuncBridge* bridgeIn): + db(dbIn),bridge(bridgeIn), missionJson(jsonIn){ + + } + + // 对普通指针的安全删除 + template + void safeDelete(T*& ptr) { + if (ptr) { delete ptr; ptr = nullptr; } + } + + // 尝试对基于 QThread 的 serviceModule 进行关停,然后删除 + void safeShutdownDelete(serviceModule* ptr) { + if (!ptr) return; + // 如果对象正在运行,尝试停止 + if (ptr->isRunning()) { + // 如果存在 stop 槽,则尝试调用 + QMetaObject::invokeMethod(ptr, "stop", Qt::DirectConnection); + // 作为后备,尝试退出事件循环 + QMetaObject::invokeMethod(ptr, "quit", Qt::DirectConnection); + // 等待其退出,给予一定时间 + if (!ptr->wait(2000)) { + // 仍在运行时,作为最后手段强制终止 + ptr->terminate(); + ptr->wait(1000); + } + } + delete ptr; ptr = nullptr; + } + + void init() { + // 创建/重建需要的服务模块,避免重复创建 + if (!_serviceManager._imgSaveThreadManager) { + _serviceManager._imgSaveThreadManager = new imgSaveThreadManager(missionJson["imgSaveBasePath"]); + } + if (!_serviceManager._cameraManager) { + _serviceManager._cameraManager = new cameraManager(missionJson, bridge); + } + if (!_serviceManager._triggerManager) { + _serviceManager._triggerManager = new triggerManager(this, bridge, missionJson["Triggers"]); + } + if (initOnce == false) { + initOnce = true; + _csvWriter = new CsvWriter(db); + _serviceManager._detDataManager = new detDataManager(); + _missionResultHandle = new missionResultHandle(bridge); + _serviceManager._missionResultHandle = _missionResultHandle; + _serviceManager.dataBase = db; + _serviceManager._csvWriter = _csvWriter; + // handleAiModelManager 与 CLI 等只在首次初始化创建 + if (!_serviceManager._handleAiModelManager) { + _serviceManager._handleAiModelManager = new handleAiModelManager(missionJson, bridge); + } + connect(bridge, &qtFuncBridge::reInitConfig, this, [=]() {reinitConfig(); }); + _cliManager = new CLIManager(&_serviceManager, bridge); + } + } + + bool waitrForInitResult(serviceModule* ptr, int overtimeS = 999999) { + int overTimeMs = overtimeS * 1000; + int timecnt = 0; + while (timecnt <= overTimeMs) { + if (ptr->status != NotInit) + break; + QThread::msleep(100); + timecnt += 100; + } + if (timecnt > overTimeMs) + return false; + if (ptr->status == InitFailure) + return false; + return true; + } + + // 重新初始化配置 + void reinitConfig() { + // 停止并删除线程型模块 + safeShutdownDelete(_serviceManager._cameraManager); + safeShutdownDelete(_serviceManager._triggerManager); + if (_serviceManager._handleAiModelManager) { + // try invoking stop if exists + QMetaObject::invokeMethod(_serviceManager._handleAiModelManager, "stop", Qt::DirectConnection); + safeDelete(_serviceManager._handleAiModelManager); + } + init(); + if (!this->isRunning()) { + this->start(); + } else { + qDebug() << "detProcess already running, init refreshed. run() should handle reloading if needed."; + } + } + // 初始化并运行主流程 + void run() { +#if 1 + _serviceManager._cameraManager->start(); + _serviceManager._handleAiModelManager->start(); + _serviceManager._triggerManager->start(); + bool res1 = waitrForInitResult(_serviceManager._cameraManager); + bool res2 = waitrForInitResult(_serviceManager._handleAiModelManager); + bool res3 = waitrForInitResult(_serviceManager._triggerManager); + + if (res1 == true && res2 == true && res3 == true) { + _missionLoader.loadMission(missionJson["missionDllPath"]); + _missionLoader.getmission(missionJson["missionDllPath"])->initBasic( + &_serviceManager, + missionJson["missionSettings"], bridge + ); + _cliManager->initBasicPreset(_missionLoader.getmission(missionJson["missionDllPath"])); + _serviceManager._detDataManager->setTriggerManager(_serviceManager._triggerManager); + initJsonRpc(); + emit bridge->initResult(InitOK); + } + else { + emit bridge->initResult(InitFailure); + } +#endif + } + void initJsonRpc() { + //QObject::connect(&_rpcServer, &JsonRpcServer::clientConnected, + // [](const QString& address) { + // qInfo() << "New client connected:" << address; + // }); + + //QObject::connect(&_rpcServer, &JsonRpcServer::requestReceived, + // [](const QString& method, const json& params, const QString& address) { + // qDebug() << "Request from" << address << "- Method:" << method; + // }); + + /* _rpcServer.registerMethod("getServerInfo", [](const json& params) -> json { + return json{ + {"name", "JSON-RPC Server"}, + {"version", "1.0.0"}, + {"timestamp", QDateTime::currentSecsSinceEpoch()} + }; + });*/ + //_rpcServer.registerMethod("getMachineInfo", [](const json& params) -> json { + // auto& config = appConfig::getInstance(); + // return json{ + // {"name", config.softwareTile.c_str()}, + // {"timestamp", QDateTime::currentSecsSinceEpoch()} + // }; + // }); + //_rpcServer.registerMethod("reloadConfig", [this](const json& params) -> json { + // _cliManager->cliCommand("reloadConfig"); + // return json{ + // {"result", "OK"} }; + // }); + //// 添加连接信号用于调试 + //QObject::connect(&_rpcServer, &JsonRpcServer::clientConnected, + // [](const QString& address) { + // qInfo() << "RPC Client connected:" << address; + // }); + + //QObject::connect(&_rpcServer, &JsonRpcServer::clientDisconnected, + // [](const QString& address) { + // qInfo() << "RPC Client disconnected:" << address; + // }); + + //QObject::connect(&_rpcServer, &JsonRpcServer::requestReceived, + // [](const QString& method, const json& params, const QString& address) { + // qDebug() << "RPC Request from" << address << "- Method:" << method; + // }); + + //QObject::connect(&_rpcServer, &JsonRpcServer::errorOccurred, + // [](const QString& error, const QString& address) { + // qWarning() << "RPC Error from" << address << ":" << error; + // }); + + _rpcServer.registerMethod(QStringLiteral("reloadConfig"), [this](const nlohmann::json&) -> nlohmann::json { + _cliManager->cliCommand("reloadConfig"); + std::cout << "接收到远程命令->重新加载配置文件" << std::endl; + return nlohmann::json{ + {"result", "OK"} + }; + }); + // 检查服务器启动结果 + _rpcServer.start(2367); + //if (_rpcServer.start(2367)) { + // qInfo() << "JSON-RPC server started successfully on port" << _rpcServer.serverPort(); + //} + //else { + // qCritical() << "Failed to start JSON-RPC server on port 2367"; + // // 尝试其他端口 + // for (int port = 2368; port <= 2370; ++port) { + // if (_rpcServer.start(port)) { + // qInfo() << "JSON-RPC server started on alternative port" << port; + // break; + // } + // } + //} + } +private: + nlohmann::ordered_json missionJson; + SQLiteDB* db; + qtFuncBridge* bridge; + MissionLoader _missionLoader; // 任务加载器,用于加载任务配置 + +}; + +#pragma once diff --git a/include/global.h b/include/global.h index 4fbe57e..f9c6304 100644 --- a/include/global.h +++ b/include/global.h @@ -8,7 +8,7 @@ #include #include #pragma execution_character_set("utf-8") -const std::string softwareVersion = "2.0.2"; +const std::string softwareVersion = "2.0.3"; const std::string euiJsonVersion = "1.0.6"; #define globalDatabasePth "./sqlDatabase/eui_db.db" //全局数据库路径 #define saveImgEnable 1 diff --git a/include/interface.h b/include/interface.h index 0ad0a2d..6099498 100644 --- a/include/interface.h +++ b/include/interface.h @@ -7,6 +7,7 @@ public: virtual void test() = 0; virtual void init(SQLiteDB* ,qtFuncBridge*,nlohmann::ordered_json& json) = 0; virtual void cleanup() = 0; + virtual bool excuteCliCommand(const QString& command) = 0; virtual bool excuteCliCommandPreset(const QString& uuid) = 0; }; diff --git a/rpcModule/rpc_finsTcp/rpc_finsTcp.cpp b/rpcModule/rpc_finsTcp/rpc_finsTcp.cpp index e650fd1..8a2a878 100644 --- a/rpcModule/rpc_finsTcp/rpc_finsTcp.cpp +++ b/rpcModule/rpc_finsTcp/rpc_finsTcp.cpp @@ -168,6 +168,7 @@ bool rpc_finsTcp::writeData(std::vector& vec_mat, QByteArray& data){ bool rpc_finsTcp::initParas(QByteArray& data){ startProcess("./json_rpc_server_1_1_0.exe"); + QThread::msleep(500); subThread = new QThread(this); write_JsonRpcClient = new JsonRpcClientWorker(); write_JsonRpcClient->moveToThread(subThread); diff --git a/src/Core/backendInterface.cpp b/src/Core/backendInterface.cpp index 24883e8..f756a85 100644 --- a/src/Core/backendInterface.cpp +++ b/src/Core/backendInterface.cpp @@ -25,6 +25,9 @@ public: ~Backend() { delete _detProcess; } + bool excuteCliCommand(const QString& command) { + return _detProcess->_cliManager->cliCommand(command); + } bool excuteCliCommandPreset(const QString& uuid) { return _detProcess->_cliManager->excutePreset(uuid); } diff --git a/src/Core/detProcess.hpp b/src/Core/detProcess.hpp index 4c8f9e9..ef2ddd6 100644 --- a/src/Core/detProcess.hpp +++ b/src/Core/detProcess.hpp @@ -4,6 +4,7 @@ #include #include #include +#include class detProcess : public QThread { public: @@ -11,9 +12,10 @@ public: missionResultHandle* _missionResultHandle = nullptr; ServiceManager _serviceManager; CLIManager* _cliManager; // 命令行接口管理器 + rpc::JsonRpcServer _rpcServer; bool initOnce = false; detProcess(SQLiteDB* dbIn, nlohmann::ordered_json& jsonIn, qtFuncBridge* bridgeIn): - db(dbIn),bridge(bridgeIn),json(jsonIn){ + db(dbIn),bridge(bridgeIn), missionJson(jsonIn){ } @@ -45,13 +47,13 @@ public: void init() { // 创建/重建需要的服务模块,避免重复创建 if (!_serviceManager._imgSaveThreadManager) { - _serviceManager._imgSaveThreadManager = new imgSaveThreadManager(json["imgSaveBasePath"]); + _serviceManager._imgSaveThreadManager = new imgSaveThreadManager(missionJson["imgSaveBasePath"]); } if (!_serviceManager._cameraManager) { - _serviceManager._cameraManager = new cameraManager(json, bridge); + _serviceManager._cameraManager = new cameraManager(missionJson, bridge); } if (!_serviceManager._triggerManager) { - _serviceManager._triggerManager = new triggerManager(this, bridge, json["Triggers"]); + _serviceManager._triggerManager = new triggerManager(this, bridge, missionJson["Triggers"]); } if (initOnce == false) { initOnce = true; @@ -63,7 +65,7 @@ public: _serviceManager._csvWriter = _csvWriter; // handleAiModelManager 与 CLI 等只在首次初始化创建 if (!_serviceManager._handleAiModelManager) { - _serviceManager._handleAiModelManager = new handleAiModelManager(json, bridge); + _serviceManager._handleAiModelManager = new handleAiModelManager(missionJson, bridge); } connect(bridge, &qtFuncBridge::reInitConfig, this, [=]() {reinitConfig(); }); _cliManager = new CLIManager(&_serviceManager, bridge); @@ -114,13 +116,14 @@ public: bool res3 = waitrForInitResult(_serviceManager._triggerManager); if (res1 == true && res2 == true && res3 == true) { - _missionLoader.loadMission(json["missionDllPath"]); - _missionLoader.getmission(json["missionDllPath"])->initBasic( + _missionLoader.loadMission(missionJson["missionDllPath"]); + _missionLoader.getmission(missionJson["missionDllPath"])->initBasic( &_serviceManager, - json["missionSettings"], bridge + missionJson["missionSettings"], bridge ); - _cliManager->initBasicPreset(); + _cliManager->initBasicPreset(_missionLoader.getmission(missionJson["missionDllPath"])); _serviceManager._detDataManager->setTriggerManager(_serviceManager._triggerManager); + initJsonRpc(); emit bridge->initResult(InitOK); } else { @@ -128,8 +131,19 @@ public: } #endif } + void initJsonRpc() { + _rpcServer.registerMethod(QStringLiteral("reloadConfig"), [this](const nlohmann::json&) -> nlohmann::json { + _cliManager->cliCommand("reloadConfig"); + std::cout << "接收到远程命令->重新加载配置文件" << std::endl; + return nlohmann::json{ + {"result", "OK"} + }; + }); + // 检查服务器启动结果 + _rpcServer.start(2367); + } private: - nlohmann::ordered_json json; + nlohmann::ordered_json missionJson; SQLiteDB* db; qtFuncBridge* bridge; MissionLoader _missionLoader; // 任务加载器,用于加载任务配置 diff --git a/src/core/back/CLIManager.h b/src/core/back/CLIManager.h index 48ee1bf..f83a37a 100644 --- a/src/core/back/CLIManager.h +++ b/src/core/back/CLIManager.h @@ -2,6 +2,7 @@ #include #include #include +#include class algoManager { public: @@ -32,7 +33,8 @@ public: _serviceManager->_triggerManager->sendFinsMsg(strList[1]); return true; } - else if (strList[0] == "reconfig") { + else if (strList[0] == "reloadConfig") { + _mission->reloadConfig(); return true; } else if (strList[0] == "waitMs") { @@ -51,7 +53,7 @@ public: presets[btnName] = commands; } - void initBasicPreset() { + void initBasicPreset(mission* _missionIn) { for (auto& data : _serviceManager->_triggerManager->triggerCallbackFuncs) { for (auto& callbacks : data.second) { if (callbacks.funcName.find(avoid) != std::string::npos) { @@ -84,6 +86,14 @@ public: presets[uuid] = commands; } } + + QString uuid = QUuid::createUuid().toString();; + QStringList commands = { "reloadConfig" }; + QString tips = "reloadConfig"; + emit bridge->runCommand_addBtn( + QString::fromStdString("重新加载算法文件"), tips, uuid); + presets[uuid] = commands; + _mission = _missionIn; } QStringList basicTriggerPresetGenerate(int& triggerAddress) { QString triggerAddressStr = QString::number(triggerAddress); @@ -108,6 +118,7 @@ public: return commands; } private: + mission* _mission; qtFuncBridge* bridge; QMap presets; ServiceManager* _serviceManager; diff --git a/src/core/back/missionBase.hpp b/src/core/back/missionBase.hpp index 2df754d..d441eaf 100644 --- a/src/core/back/missionBase.hpp +++ b/src/core/back/missionBase.hpp @@ -256,6 +256,9 @@ public: _configMap.loadFromJson(_missionJson); onConfigReloaded(); } + void reloadConfig() { + onConfigReloaded(); + } protected: //供子类继承 virtual void onConfigReloaded() = 0; diff --git a/src/core/jsonRpcServer.h b/src/core/jsonRpcServer.h new file mode 100644 index 0000000..5a69000 --- /dev/null +++ b/src/core/jsonRpcServer.h @@ -0,0 +1,728 @@ +// jsonRpcServer.h - 基于 QThread 的 JSON-RPC 2.0 服务器,使用 QTcpServer 与 nlohmann::json +// +// 协议分帧:以换行符分隔的 JSON。每个请求/响应必须以单个 '\n' 结尾。 +// 线程模型:所有网络 I/O 在专用工作线程中运行。公开 API 为线程安全转发。 +// Qt 版本:兼容 5.14.2(对 QAbstractSocket::error 信号使用 static_cast 连接)。 + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// 项目全局采用此头文件包含方式 +#include + +namespace rpc { + + using json = nlohmann::json; + + // 运行在专用 QThread 中的工作者,持有 QTcpServer 与所有套接字。 + class JsonRpcServerWorker : public QObject { + Q_OBJECT + public: + using MethodHandler = std::function; + + enum ErrorCode { + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603, + ServerError = -32000 + }; + + explicit JsonRpcServerWorker(QObject* parent = nullptr) + : QObject(parent) + , m_tcpServer(nullptr) + , m_port(0) { + } + + ~JsonRpcServerWorker() override { + stopServer(); + } + + public slots: + // 必须在工作线程内调用 + void initializeInThread() { + if (m_tcpServer) return; + m_tcpServer = new QTcpServer(this); + connect(m_tcpServer, &QTcpServer::newConnection, + this, &JsonRpcServerWorker::onNewConnection); + // 注册内置方法(仅注册一次) + registerBuiltinsIfNeeded(); + } + + // 设置服务器名称与版本(可选) + void setVersion(const QString& name, const QString& version) { + m_serverName = name; + m_version = version; + } + + void startServer(quint16 port, const QHostAddress& address = QHostAddress::Any) { + if (!m_tcpServer) initializeInThread(); + if (m_tcpServer->isListening()) { + emit serverStarted(m_tcpServer->serverPort(), address.toString()); + return; + } + if (!m_tcpServer->listen(address, port)) { + emit errorOccurred("startServer", m_tcpServer->errorString()); + return; + } + m_port = m_tcpServer->serverPort(); + m_bindAddr = address; + emit serverStarted(m_port, address.toString()); + } + + void stopServer() { + if (m_tcpServer) { + if (m_tcpServer->isListening()) m_tcpServer->close(); + } + for (QTcpSocket* s : qAsConst(m_clients)) { + if (!s) continue; + s->disconnect(this); + s->close(); + s->deleteLater(); + } + m_clients.clear(); + m_recvBuffers.clear(); + emit serverStopped(); + emit clientCountChanged(0); + } + + void registerMethod(const QString& name, const MethodHandler& handler) { + QMutexLocker locker(&m_methodsMutex); + m_methods.insert(name, handler); + } + + void unregisterMethod(const QString& name) { + QMutexLocker locker(&m_methodsMutex); + m_methods.remove(name); + } + + signals: + void serverStarted(quint16 port, const QString& addressStr); + void serverStopped(); + void clientConnected(const QString& peer); + void clientDisconnected(const QString& peer); + void errorOccurred(const QString& where, const QString& what); + void clientCountChanged(int count); + + private slots: + void onNewConnection() { + while (m_tcpServer && m_tcpServer->hasPendingConnections()) { + QTcpSocket* socket = m_tcpServer->nextPendingConnection(); + if (!socket) continue; + socket->setParent(this); + m_clients.append(socket); + m_recvBuffers.insert(socket, QByteArray()); + + connect(socket, &QTcpSocket::readyRead, + this, &JsonRpcServerWorker::onDataReceived); + connect(socket, &QTcpSocket::disconnected, + this, &JsonRpcServerWorker::onClientDisconnected); + connect(socket, + static_cast(&QAbstractSocket::error), + this, &JsonRpcServerWorker::onSocketError); + + const QString peer = QString("%1:%2") + .arg(socket->peerAddress().toString()) + .arg(socket->peerPort()); + emit clientConnected(peer); + emit clientCountChanged(m_clients.size()); + } + } + + void onDataReceived() { + QTcpSocket* socket = qobject_cast(sender()); + if (!socket) return; + m_recvBuffers[socket] += socket->readAll(); + + while (true) { + // 每次循环重新获取 buffer 的引用,避免在处理期间连接断开导致引用失效 + auto it = m_recvBuffers.find(socket); + if (it == m_recvBuffers.end()) { + // 连接可能在处理过程中断开且已在 onClientDisconnected 中移除 + return; + } + QByteArray& buffer = it.value(); + int idx = buffer.indexOf('\n'); + if (idx == -1) break; + QByteArray line = buffer.left(idx); + buffer.remove(0, idx + 1); + if (line.trimmed().isEmpty()) continue; + processIncomingText(socket, QString::fromUtf8(line)); + } + } + + void onClientDisconnected() { + QTcpSocket* socket = qobject_cast(sender()); + if (!socket) return; + const QString peer = QString("%1:%2") + .arg(socket->peerAddress().toString()) + .arg(socket->peerPort()); + m_recvBuffers.remove(socket); + m_clients.removeAll(socket); + socket->deleteLater(); + emit clientDisconnected(peer); + emit clientCountChanged(m_clients.size()); + } + + void onSocketError(QAbstractSocket::SocketError) { + QTcpSocket* socket = qobject_cast(sender()); + if (!socket) return; + emit errorOccurred("socket", socket->errorString()); + } + + private: + // 如未注册则注册内置方法 + void registerBuiltinsIfNeeded() { + if (m_builtinsRegistered) return; + // 版本反馈:rpc.version -> { name, version, qt } + registerMethod(QStringLiteral("rpc.version"), [this](const json&) -> json { + return json{ + {"name", m_serverName.toStdString()}, + {"rpc_version", m_version.toStdString()}, + {"eui_version",softwareVersion}, + {"eui_json_version",euiJsonVersion}, + {"qt", QT_VERSION_STR} + }; + }); + registerMethod(QStringLiteral("rpc.heartBeat"), [this](const json&) -> json { + return json{ + {"result", "OK"} + }; + }); + registerMethod(QStringLiteral("rpc.download"), [this](const json& param) -> json { + try { + // 检查必需参数 + if (!param.contains("url") || !param["url"].is_string()) { + return json{ + {"error", "Missing or invalid 'url' parameter"} + }; + } + std::string url = param["url"].get(); + QString urlStr = QString::fromStdString(url); + + // 获取可选参数 + QString customFileName; + QString targetDir; + int timeoutSec = 30; + bool overwrite = false; + bool followRedirects = true; + + if (param.contains("filename") && param["filename"].is_string()) { + customFileName = QString::fromStdString(param["filename"].get()); + } + + if (param.contains("target_dir") && param["target_dir"].is_string()) { + targetDir = QString::fromStdString(param["target_dir"].get()); + } + + if (param.contains("timeout") && param["timeout"].is_number()) { + timeoutSec = param["timeout"].get(); + timeoutSec = qBound(5, timeoutSec, 300); // 限制在5-300秒之间 + } + + if (param.contains("overwrite") && param["overwrite"].is_boolean()) { + overwrite = param["overwrite"].get(); + } + + if (param.contains("follow_redirects") && param["follow_redirects"].is_boolean()) { + followRedirects = param["follow_redirects"].get(); + } + + // 清理URL,移除可能的不可见字符 + urlStr = urlStr.trimmed(); + urlStr = urlStr.remove(QChar('\r')).remove(QChar('\n')).remove(QChar('\t')); + + // 验证URL格式 + QUrl qurl(urlStr); + if (!qurl.isValid()) { + return json{ + {"error", QString("Invalid URL format: %1").arg(urlStr).toStdString()} + }; + } + + // 确定文件名 + QString fileName; + if (!customFileName.isEmpty()) { + fileName = customFileName; + } + else { + fileName = qurl.fileName(); + // 如果文件名包含UUID前缀,提取真实文件名 + if (fileName.contains("_") && fileName.split("_").size() > 1) { + QStringList parts = fileName.split("_"); + if (parts.first().length() == 32) { // UUID长度 + parts.removeFirst(); + fileName = parts.join("_"); + } + } + if (fileName.isEmpty()) { + QString baseName = qurl.path().split("/").last(); + if (baseName.isEmpty()) { + fileName = QString("download_%1").arg(QDateTime::currentMSecsSinceEpoch()); + } + else { + fileName = baseName; + } + } + } + std::cout << "接收到远程命令->更新文件" << fileName.toStdString() << std::endl; + // 确定目标目录 + QString rootDir; + if (!targetDir.isEmpty()) { + QDir dir(targetDir); + if (!dir.exists()) { + if (!dir.mkpath(targetDir)) { + return json{ + {"error", QString("Cannot create target directory: %1").arg(targetDir).toStdString()} + }; + } + } + rootDir = dir.absolutePath(); + } + else { + rootDir = QCoreApplication::applicationDirPath(); + } + + QString filePath = QDir(rootDir).absoluteFilePath(fileName); + + // 检查文件是否已存在并处理覆盖逻辑 + bool fileExists = QFileInfo::exists(filePath); + if (fileExists && !overwrite) { + return json{ + {"error", QString("File already exists: %1 (use overwrite=true to replace)").arg(filePath).toStdString()} + }; + } + + // 如果需要覆盖且文件存在,先备份原文件 + QString backupPath; + if (fileExists && overwrite) { + QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss"); + backupPath = filePath + QString(".backup-%1").arg(timestamp); + if (!QFile::copy(filePath, backupPath)) { + return json{ + {"warning", QString("Could not create backup of existing file: %1").arg(filePath).toStdString()} + }; + } + } + + // 构建curl命令参数 + QStringList arguments; + arguments << "/c" << "curl"; + + // 基本参数 + arguments << "--connect-timeout" << "10"; + arguments << "--max-time" << QString::number(timeoutSec); + arguments << "--retry" << "3"; + arguments << "--retry-delay" << "1"; + + // 如果overwrite为true,添加强制覆盖参数 + if (overwrite) { + arguments << "--create-dirs"; // 创建必要的目录 + // curl默认会覆盖现有文件,但我们可以添加更多控制 + } + + // 跟随重定向 + if (followRedirects) { + arguments << "-L"; + } + + // 显示进度信息 + arguments << "--progress-bar"; + + // 输出文件 + arguments << "-o" << filePath; + + // URL + arguments << urlStr; + + // 记录开始时间 + qint64 startTime = QDateTime::currentMSecsSinceEpoch(); + + // 执行curl命令 + QProcess process; + process.start("cmd", arguments); + process.waitForFinished(timeoutSec * 1000 + 5000); // 额外5秒缓冲 + + qint64 endTime = QDateTime::currentMSecsSinceEpoch(); + int downloadTime = static_cast((endTime - startTime) / 1000); + + int exitCode = process.exitCode(); + QString stdOutput = process.readAllStandardOutput(); + QString stdError = process.readAllStandardError(); + + if (exitCode != 0) { + // 清理可能创建的空文件 + if (QFileInfo::exists(filePath)) { + QFile::remove(filePath); + } + + // 如果有备份文件,恢复它 + if (!backupPath.isEmpty() && QFileInfo::exists(backupPath)) { + QFile::rename(backupPath, filePath); + } + + QString errorMsg; + switch (exitCode) { + case 6: errorMsg = "Couldn't resolve host"; break; + case 7: errorMsg = "Failed to connect to host"; break; + case 22: errorMsg = "HTTP page not retrieved (404, 403, etc.)"; break; + case 28: errorMsg = "Operation timeout"; break; + default: errorMsg = stdError.isEmpty() ? stdOutput : stdError; break; + } + + return json{ + {"error", QString("Download failed (exit code %1): %2") + .arg(exitCode) + .arg(errorMsg) + .toStdString()} + }; + } + + // 验证下载的文件 + QFileInfo fileInfo(filePath); + if (!fileInfo.exists()) { + return json{ + {"error", "Downloaded file does not exist"} + }; + } + + if (fileInfo.size() == 0) { + QFile::remove(filePath); // 删除空文件 + return json{ + {"error", "Downloaded file is empty"} + }; + } + + // 计算下载速度 + double downloadSpeed = 0; + if (downloadTime > 0) { + downloadSpeed = static_cast(fileInfo.size()) / downloadTime; // bytes/sec + } + + // 保留备份文件(不删除) + bool hadBackup = false; + QString backupFilename; + if (!backupPath.isEmpty() && QFileInfo::exists(backupPath)) { + hadBackup = true; + backupFilename = QFileInfo(backupPath).fileName(); + // 备份文件保留,不删除 + } + + json result = json{ + {"result", "OK"}, + {"filename", fileName.toStdString()}, + {"filepath", filePath.toStdString()}, + {"filesize", static_cast(fileInfo.size())}, + {"download_time_sec", downloadTime}, + {"download_speed_bps", static_cast(downloadSpeed)}, + {"url_used", urlStr.toStdString()}, + {"target_directory", rootDir.toStdString()}, + {"file_modified", fileInfo.lastModified().toString(Qt::ISODate).toStdString()}, + {"overwrite_enabled", overwrite}, + {"file_was_replaced", hadBackup} + }; + + // 如果有备份文件,添加备份信息到返回结果 + if (hadBackup) { + result["backup_file"] = backupFilename.toStdString(); + result["backup_path"] = backupPath.toStdString(); + } + + return result; + + } + catch (const std::exception& e) { + return json{ + {"error", QString("Exception: %1").arg(e.what()).toStdString()} + }; + } + }); + m_builtinsRegistered = true; + } + + void processIncomingText(QTcpSocket* socket, const QString& text) { + json id = nullptr; + try { + // 在解析前先进行快速 JSON 合法性验证,避免抛出解析异常 + if (!json::accept(text.toStdString())) { + json err = createErrorResponse(nullptr, ParseError, QStringLiteral("Invalid JSON")); + sendResponse(socket, err); + return; + } + json req = json::parse(text.toStdString()); + if (req.is_array()) { + // 批量请求 + json batchResp = processBatchRequest(req); + if (!batchResp.is_null()) { + sendResponse(socket, batchResp); + } + return; + } + // 单个请求 + id = getRequestId(req); + json resp = processRequest(req); + if (!resp.is_null()) { + sendResponse(socket, resp); + } + } + catch (const json::parse_error& e) { + json err = createErrorResponse(nullptr, ParseError, QString("Parse error: %1").arg(e.what())); + sendResponse(socket, err); + } + catch (const std::exception& e) { + json err = createErrorResponse(id.is_null() ? nullptr : id, InternalError, QString("Exception: %1").arg(e.what())); + sendResponse(socket, err); + } + } + + json processBatchRequest(const json& batch) { + if (!batch.is_array()) { + return createErrorResponse(nullptr, InvalidRequest, QStringLiteral("Batch must be an array")); + } + if (batch.empty()) { + return createErrorResponse(nullptr, InvalidRequest, QStringLiteral("Empty batch")); + } + json responses = json::array(); + for (const auto& item : batch) { + try { + json resp = processRequest(item); + if (!resp.is_null()) responses.push_back(resp); + } + catch (const std::exception& e) { + json id = item.is_object() && item.contains("id") ? item.at("id") : json(nullptr); + responses.push_back(createErrorResponse(id, InternalError, QString("Exception: %1").arg(e.what()))); + } + } + if (responses.empty()) return json(nullptr); // all notifications + return responses; + } + + json processRequest(const json& req) { + // 校验基础结构 + if (!req.is_object()) { + return createErrorResponse(nullptr, InvalidRequest, QStringLiteral("Request must be object")); + } + if (!req.contains("jsonrpc") || req.at("jsonrpc") != "2.0") { + json id = getRequestId(req); + return createErrorResponse(id, InvalidRequest, QStringLiteral("jsonrpc must be '2.0'")); + } + if (!req.contains("method") || !req.at("method").is_string()) { + json id = getRequestId(req); + return createErrorResponse(id, InvalidRequest, QStringLiteral("Missing method")); + } + + const QString method = QString::fromStdString(req.at("method").get()); + const json params = req.contains("params") ? req.at("params") : json(); + const json id = getRequestId(req); + + // 查找处理器 + MethodHandler handler; + { + QMutexLocker locker(&m_methodsMutex); + auto it = m_methods.find(method); + if (it != m_methods.end()) handler = it.value(); + } + if (!handler) { + return id.is_null() ? json(nullptr) + : createErrorResponse(id, MethodNotFound, QString("Method not found: %1").arg(method)); + } + + try { + json result = handler(params); + if (id.is_null()) return json(nullptr); // 通知(无 id) + json resp = json::object(); + resp["jsonrpc"] = "2.0"; + resp["result"] = result; + resp["id"] = id; + return resp; + } + catch (const std::exception& e) { + return id.is_null() ? json(nullptr) + : createErrorResponse(id, InternalError, QString("Exception: %1").arg(e.what())); + } + } + + json createErrorResponse(const json& id, ErrorCode code, const QString& message) { + json resp = json::object(); + resp["jsonrpc"] = "2.0"; + json err = json::object(); + err["code"] = code; + err["message"] = message.toStdString(); + resp["error"] = err; + resp["id"] = id.is_null() ? json(nullptr) : id; + return resp; + } + + json getRequestId(const json& req) const { + if (req.is_object() && req.contains("id")) { + return req.at("id"); + } + return json(nullptr); + } + + void sendResponse(QTcpSocket* socket, const json& response) { + const std::string s = response.dump(); + QByteArray out = QByteArray::fromStdString(s); + out.append('\n'); + socket->write(out); + socket->flush(); + } + + private: + QTcpServer* m_tcpServer; + QList m_clients; + QHash m_recvBuffers; + + QMap m_methods; + QMutex m_methodsMutex; + + quint16 m_port; + QHostAddress m_bindAddr; + + // 版本信息与内置方法注册标志 + bool m_builtinsRegistered = false; + QString m_serverName = QStringLiteral("JsonRpcServer"); + QString m_version = QStringLiteral("0.1.0"); + }; + + // 公共门面类:可在任意线程安全使用;实际工作转发到工作线程执行。 + class JsonRpcServer : public QObject { + Q_OBJECT + public: + explicit JsonRpcServer(QObject* parent = nullptr) + : QObject(parent) + , m_workerThread(new QThread(this)) + , m_worker(new JsonRpcServerWorker()) + , m_port(0) + , m_clientCount(0) + , m_isListening(false) { + m_worker->moveToThread(m_workerThread); + + // 在工作线程内初始化工作者端的服务器对象 + connect(m_workerThread, &QThread::started, m_worker, &JsonRpcServerWorker::initializeInThread); + + // 转发信号并镜像内部状态 + connect(m_worker, &JsonRpcServerWorker::serverStarted, this, [this](quint16 port, const QString&) { + m_port = port; + m_isListening = true; + emit serverStarted(m_port); + }); + connect(m_worker, &JsonRpcServerWorker::serverStopped, this, [this]() { + m_isListening = false; + m_port = 0; + emit serverStopped(); + }); + connect(m_worker, &JsonRpcServerWorker::clientConnected, this, &JsonRpcServer::clientConnected); + connect(m_worker, &JsonRpcServerWorker::clientDisconnected, this, &JsonRpcServer::clientDisconnected); + connect(m_worker, &JsonRpcServerWorker::errorOccurred, this, &JsonRpcServer::errorOccurred); + connect(m_worker, &JsonRpcServerWorker::clientCountChanged, this, [this](int c) { m_clientCount = c; emit clientCountChanged(c); }); + + // 清理 + connect(m_workerThread, &QThread::finished, m_worker, &QObject::deleteLater); + m_workerThread->start(); + } + + ~JsonRpcServer() override { + stop(); + if (m_workerThread->isRunning()) { + m_workerThread->quit(); + m_workerThread->wait(); + } + } + + // 设置版本信息(转发到工作线程) + void setVersion(const QString& name, const QString& version) { + QMetaObject::invokeMethod(m_worker, [this, name, version]() { + m_worker->setVersion(name, version); + }, Qt::QueuedConnection); + } + + // 开始监听(异步)。成功后触发 serverStarted 信号。 + void start(quint16 port, const QHostAddress& address = QHostAddress::Any) { + QMetaObject::invokeMethod(m_worker, [this, port, address]() { + m_worker->startServer(port, address); + }, Qt::QueuedConnection); + } + + // 停止监听并关闭所有客户端连接。 + void stop() { + QMetaObject::invokeMethod(m_worker, [this]() { m_worker->stopServer(); }, Qt::QueuedConnection); + } + + // 注册方法处理器。处理器签名:json(const json& params) + void registerMethod(const QString& name, const JsonRpcServerWorker::MethodHandler& handler) { + QMetaObject::invokeMethod(m_worker, [this, name, handler]() { + m_worker->registerMethod(name, handler); + }, Qt::QueuedConnection); + } + + void unregisterMethod(const QString& name) { + QMetaObject::invokeMethod(m_worker, [this, name]() { + m_worker->unregisterMethod(name); + }, Qt::QueuedConnection); + } + + bool isListening() const { return m_isListening; } + quint16 serverPort() const { return m_port; } + int clientCount() const { return m_clientCount; } + + signals: + void serverStarted(quint16 port); + void serverStopped(); + void clientConnected(const QString& peer); + void clientDisconnected(const QString& peer); + void errorOccurred(const QString& where, const QString& what); + void clientCountChanged(int count); + + private: + QThread* m_workerThread; + JsonRpcServerWorker* m_worker; + quint16 m_port; + int m_clientCount; + bool m_isListening; + }; + + // 便捷宏:用于快速注册函数 +#define RPC_REGISTER(serverPtr, methodName, lambdaExpr) \ + do { \ + (serverPtr)->registerMethod(QString::fromUtf8(methodName), (lambdaExpr)); \ + } while (0) + +/* +使用示例: + + auto* rpcServer = new rpc::JsonRpcServer(this); + rpcServer->registerMethod("echo", [](const rpc::json& params) -> rpc::json { + return params; // 原样回显 + }); + rpcServer->start(12345, QHostAddress::LocalHost); + +客户端应按行发送单条 JSON,例如: + {"jsonrpc":"2.0","id":1,"method":"echo","params":{"msg":"hi"}}\n +*/ + +} // namespace rpc + diff --git a/src/launcher/appConfig/appConfig.h b/src/launcher/appConfig/appConfig.h index f8343b5..aa9eebd 100644 --- a/src/launcher/appConfig/appConfig.h +++ b/src/launcher/appConfig/appConfig.h @@ -43,6 +43,7 @@ public: HWND consoleHandle; SQLiteDB* dataBase = nullptr; std::vector< viewerStruct>viewDatas; + std::string softwareTile;//软件标题 Auth::UserManager Authorization; private: @@ -50,7 +51,6 @@ private: std::string jsonVersion; std::string objectSelect; std::string loginPassWord; - std::string softwareTile;//软件标题 Translation* _translation; }; #pragma once \ No newline at end of file diff --git a/src/launcher/appCore.hpp b/src/launcher/appCore.hpp index 58a0075..df7dd0b 100644 --- a/src/launcher/appCore.hpp +++ b/src/launcher/appCore.hpp @@ -43,6 +43,10 @@ public: backend = nullptr; FreeLibrary(hDll); } + void excuteCli(const QString& str) { + if(backend != nullptr) + backend->excuteCliCommand(str); + } private: qtFuncBridge* bridge; nlohmann::json json; @@ -52,11 +56,13 @@ private: class appCore : public QObject{ Q_OBJECT public: - backEnd backend; + backEnd _backEnd; logService* _logService = logService::getInstance(); systemStateMonitor* _systemStateMonitor = &systemStateMonitor::getInstance(); - // 获取日志服务实例 - + + appCore() { + initConfig(); + } ~appCore(){ auto& config = appConfig::getInstance(); if(config.dataBase) delete config.dataBase; diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp index 9989bc1..6e567b8 100644 --- a/src/launcher/main.cpp +++ b/src/launcher/main.cpp @@ -106,7 +106,7 @@ void connectSignalAndSlot(appCore* _core, MainWindow* _MainWindow, qtFuncBridge* _MainWindow->_displayWidget->_runCommandWidget, &runCommandWidget::addBtn); QObject::connect(_MainWindow->_displayWidget->_runCommandWidget, &runCommandWidget::excuteCommandPreset, - [=](QString uuid) {_core->backend.backend->excuteCliCommandPreset(uuid); }); + [=](QString uuid) {_core->_backEnd.backend->excuteCliCommandPreset(uuid); }); } int main(int argc, char* argv[]) @@ -136,7 +136,6 @@ int main(int argc, char* argv[]) #pragma endregion appCore _core; - _core.initConfig(); auto& config = appConfig::getInstance(); config.consoleHandle = hWnd; qtFuncBridge bridge; // 创建 C++ 对象 @@ -164,7 +163,7 @@ int main(int argc, char* argv[]) w.show(); QThread::msleep(100); // 创建后端对象 - _core.backend.loadCore(config.dataBase,&bridge, config.settingJson["backend"]); // 加载核心功能 + _core._backEnd.loadCore(config.dataBase,&bridge, config.settingJson["backend"]); // 加载核心功能 #if HIDE_CONSOLE if (config.consoleHandle) { ShowWindow(config.consoleHandle, SW_HIDE);