rpcserver初版完善

This commit is contained in:
2025-09-19 12:38:05 +08:00
parent 333caf38b4
commit f82af9201a
12 changed files with 1002 additions and 20 deletions
@@ -0,0 +1,216 @@
#include <qthread.h>
#include <serviceManager.h>
#include <sql/qSqlBase.h>
#include <missionLoader.h>
#include <CLIManager.h>
#include <back/missionResultHandle.h>
#include <jsonRpcServer.h>
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<typename T>
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
+1 -1
View File
@@ -8,7 +8,7 @@
#include <vector>
#include <functional>
#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
+1
View File
@@ -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;
};
+1
View File
@@ -168,6 +168,7 @@ bool rpc_finsTcp::writeData(std::vector<cv::Mat>& 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);
+3
View File
@@ -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);
}
+24 -10
View File
@@ -4,6 +4,7 @@
#include <missionLoader.h>
#include <CLIManager.h>
#include <back/missionResultHandle.h>
#include <jsonRpcServer.h>
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; // 任务加载器,用于加载任务配置
+13 -2
View File
@@ -2,6 +2,7 @@
#include <serviceManager.h>
#include <unordered_map>
#include <qvariant.h>
#include <missionBase.hpp>
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<QString, QStringList> presets;
ServiceManager* _serviceManager;
+3
View File
@@ -256,6 +256,9 @@ public:
_configMap.loadFromJson(_missionJson);
onConfigReloaded();
}
void reloadConfig() {
onConfigReloaded();
}
protected:
//供子类继承
virtual void onConfigReloaded() = 0;
+728
View File
@@ -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 <QObject>
#include <QThread>
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostAddress>
#include <QByteArray>
#include <QList>
#include <QMap>
#include <QHash>
#include <QMutex>
#include <QMutexLocker>
#include <QString>
#include <QVector>
#include <QPointer>
#include <QProcess>
#include <QDir>
#include <QUrl>
#include <QFileInfo>
#include <QDateTime>
#include <QCoreApplication>
#include <functional>
// 项目全局采用此头文件包含方式
#include <nlohmannjson.hpp>
namespace rpc {
using json = nlohmann::json;
// 运行在专用 QThread 中的工作者,持有 QTcpServer 与所有套接字。
class JsonRpcServerWorker : public QObject {
Q_OBJECT
public:
using MethodHandler = std::function<json(const json& params)>;
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<void(QAbstractSocket::*)(QAbstractSocket::SocketError)>(&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<QTcpSocket*>(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<QTcpSocket*>(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<QTcpSocket*>(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<std::string>();
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<std::string>());
}
if (param.contains("target_dir") && param["target_dir"].is_string()) {
targetDir = QString::fromStdString(param["target_dir"].get<std::string>());
}
if (param.contains("timeout") && param["timeout"].is_number()) {
timeoutSec = param["timeout"].get<int>();
timeoutSec = qBound(5, timeoutSec, 300); // 限制在5-300秒之间
}
if (param.contains("overwrite") && param["overwrite"].is_boolean()) {
overwrite = param["overwrite"].get<bool>();
}
if (param.contains("follow_redirects") && param["follow_redirects"].is_boolean()) {
followRedirects = param["follow_redirects"].get<bool>();
}
// 清理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<int>((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<double>(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<int64_t>(fileInfo.size())},
{"download_time_sec", downloadTime},
{"download_speed_bps", static_cast<int64_t>(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<std::string>());
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<QTcpSocket*> m_clients;
QHash<QTcpSocket*, QByteArray> m_recvBuffers;
QMap<QString, MethodHandler> 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
+1 -1
View File
@@ -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
+9 -3
View File
@@ -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;
+2 -3
View File
@@ -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);