rpcserver初版完善
This commit is contained in:
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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; // 任务加载器,用于加载任务配置
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -256,6 +256,9 @@ public:
|
||||
_configMap.loadFromJson(_missionJson);
|
||||
onConfigReloaded();
|
||||
}
|
||||
void reloadConfig() {
|
||||
onConfigReloaded();
|
||||
}
|
||||
protected:
|
||||
//供子类继承
|
||||
virtual void onConfigReloaded() = 0;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user