Files
eUi/rpcModule/rpc_finsTcp_boost_V2/rpc_finsTcp.cpp
T
RCiRoot c48cf25896 V20260112-2.3.7
在更多关键的地方添加了错误捕获。
CsvWriter修改为DataRecorder。
打包图片不再开启新线程处理。
部分错误修复。
2026-01-21 09:41:31 +08:00

483 lines
16 KiB
C++

#include "rpc_finsTcp.h"
#include "qprocess.h"
#include <QHostAddress>
#include <QVariant>
#include <QDebug>
#include <QTcpSocket>
#include <QJsonObject>
#include <QJsonDocument>
#include <windows.h>
#include <QDateTime>
#include <QCoreApplication>
#include <QThread>
#include <QTimer>
#include <QCryptographicHash>
#include <QScopedPointer>
#include <QBuffer>
#include <QPointer>
#include <QRegularExpression>
#include <QDataStream>
#include <QElapsedTimer>
#include <QMutexLocker>
#include <algorithm>
#include <memory>
#include <cmath>
#include <cstring>
#include <atomic>
static QProcess* process = nullptr;
static HANDLE jobHandle = nullptr;
rpc_finsTcp* create(){
qDebug() << "rpc_finsTcp create success!";
return new rpc_finsTcp;
}
void destory(rpc_finsTcp* ptr){
if (ptr)
{
delete ptr;
ptr = nullptr;
}
qDebug() << " rpc_finsTcp destory success!";
}
rpc_finsTcp::rpc_finsTcp(){
}
rpc_finsTcp::~rpc_finsTcp() {
for (auto& client : write_JsonRpcClients) {
if (client.first) {
// 断开所有信号槽连接
client.first->disconnect();
if (client.first->isRunning()) {
client.first->quit();
if (!client.first->wait(1000)) {
qWarning() << "subThread quit timeout!";
}
}
// 如果 subThread 没有 parent,则需要手动 delete
//if (!subThread->parent()) {
// delete subThread;
//}
//subThread = nullptr;
}
if (client.second) {
delete client.second;
client.second = nullptr;
}
}
if (hearBeatThread) {
// 断开所有信号槽连接
hearBeatThread->disconnect();
if (hearBeatThread->isRunning()) {
hearBeatThread->quit();
if (!hearBeatThread->wait(1000)) {
qWarning() << "subThread quit timeout!";
}
}
// 如果 subThread 没有 parent,则需要手动 delete
//if (!subThread->parent()) {
// delete subThread;
//}
//subThread = nullptr;
}
if (hearBeatClinet) {
delete hearBeatClinet;
hearBeatClinet = nullptr;
}
qDebug() << " ~rpc_finsTcp success!";
}
QQueue<QByteArray> readDataQueue;
// 从类中读数据到实例对象
QByteArray variantListToByteArray(const QVariant& var) {
QByteArray byteArray;
if (var.canConvert<QVariantList>()) {
QVariantList list = var.toList();
byteArray.reserve(list.size());
for (const QVariant& item : list) {
// 每个元素转为 uchar 存入 QByteArray
byteArray.append(static_cast<char>(item.toInt()));
}
}
return byteArray;
}
bool rpc_finsTcp::readData(std::vector<cv::Mat>& vec_mat, QByteArray& data){
JsonRpcClient* ptr = read_JsonRpcClient[data];
if(ptr == nullptr) {
ptr = new JsonRpcClient("127.0.0.1", 9000);
ptr->writeCall("init", QJsonArray{ m_IP,m_port }, "finstcp_read");
read_JsonRpcClient[data] = ptr;
}
QByteArray resp = ptr->readCall("excuteCli", QString(data), "finstcp_read");
data = resp; // 已是解码后的原始字节
return true;
}
bool rpc_finsTcp::writeData(std::vector<cv::Mat>& vec_mat, QByteArray& data) {
QList<QByteArray> m_list = data.split('_');
if (m_list.size() < 4)
{
qCritical() << data + " m_list.size() != 4;command err";
return false;
}
QString commond = m_list.at(0);
if (commond == "init") {
m_IP = m_list.at(1);
m_port = m_list.at(2).toInt();
threadCount = std::min(std::max(m_list.at(3).toInt(),1), 3);
}
else if (data.contains("read")) {
readDataQueue.append(data);
}
else {
if (data.contains("null"))
return true;
if (data.contains("Heart") == true) {//专门的发送心跳线程去发送此数据
auto request = packageWriteRequest("excuteCli", QJsonArray{ QString(data) }, finsWrite);
emit writeCallSignal(request.requestByte, request.id,data, -1);
return true;
}
//检查是不是复数命令
QList<QByteArray> commands = data.split('#');
auto request = packageWriteRequest("excuteCli", QJsonArray{ QString(data) }, finsWrite);
emit writeCallSignal(request.requestByte, request.id, data, request.threadId);
//
}
return true;
}
void startProcess(const QString& exePath,bool enableLog = false) {//注意:日志文件很大
if (process) return; // 避免重复启动
process = new QProcess();
process->setProgram(exePath);
if (enableLog)process->setArguments({ "-l" });
process->start();
if (!process->waitForStarted(1000)) {
qDebug() << "Process failed to start.";
delete process;
process = nullptr;
return;
}
DWORD pid = process->processId();
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
jobHandle = CreateJobObject(nullptr, nullptr);
if (jobHandle == nullptr) {
qDebug() << "Failed to create job object";
return;
}
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {};
jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
SetInformationJobObject(jobHandle, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli));
if (!AssignProcessToJobObject(jobHandle, hProcess)) {
qDebug() << "Failed to assign process to job object";
}
CloseHandle(hProcess);
}
void stopProcess() {
if (process) {
if (process->state() == QProcess::Running) {
process->kill();
process->waitForFinished(500);
}
delete process;
process = nullptr;
}
if (jobHandle) {
CloseHandle(jobHandle);
jobHandle = nullptr;
}
}
void rpc_finsTcp::initClientAndThread(std::pair<QThread*, JsonRpcClientWorker*>& pairData,const int& i) {
pairData.first = new QThread();
pairData.second = new JsonRpcClientWorker(i);
pairData.second->moveToThread(pairData.first);
QObject::connect(this, &rpc_finsTcp::initRpcClient,
pairData.second, &JsonRpcClientWorker::initClient, Qt::QueuedConnection);
QObject::connect(this, &rpc_finsTcp::writeCallSignal,
pairData.second, &JsonRpcClientWorker::writeCallSlot, Qt::QueuedConnection);
pairData.first->start();
while (pairData.second->connection == None)
QThread::msleep(1);
if (pairData.second->connection == Disconnected) {
qDebug() << "JsonRpcClientSubThread connection failed! threadId" << i;
}
else if (pairData.second->connection == Connected) {
qDebug() << "JsonRpcClientSubThread connected successfully! threadId" << i;
}
}
void rpc_finsTcp::initHearBeatThread() {
hearBeatThread = new QThread();
hearBeatClinet = new JsonRpcClientWorker(-1);
hearBeatClinet->moveToThread(hearBeatThread);
QObject::connect(this, &rpc_finsTcp::initRpcClient,
hearBeatClinet, &JsonRpcClientWorker::initClient, Qt::QueuedConnection);
QObject::connect(this, &rpc_finsTcp::writeCallSignal,
hearBeatClinet, &JsonRpcClientWorker::writeCallSlot, Qt::QueuedConnection);
hearBeatThread->start();
while (hearBeatClinet->connection == None)
QThread::msleep(1);
if (hearBeatClinet->connection == Disconnected) {
qDebug() << "JsonRpcClientSubThread connection failed! heartBeatThread";
}
else if (hearBeatClinet->connection == Connected) {
qDebug() << "JsonRpcClientSubThread connected successfully! threadId heartBeatThread";
}
}
bool rpc_finsTcp::initParas(QByteArray& data){
startProcess("./json_rpc_server_1_1_3.exe");
QThread::msleep(500);
write_JsonRpcClients = std::vector<std::pair<QThread*, JsonRpcClientWorker*>>(threadCount);
for (int i = 0; i < write_JsonRpcClients.size(); i++) {
initClientAndThread(write_JsonRpcClients[i],i);
}
//心跳线程
initHearBeatThread();
//qDebug() << "返回结果:" << result;
emit initRpcClient();
for (int i = 0; i < threadCount; i++) {
auto request = packageWriteRequest("init", QJsonArray{ m_IP,m_port }, packageType::finsWrite_1 + i);
emit writeCallSignal(request.requestByte, request.id, "init", -1);
}
return true;
}
bool rpc_finsTcp::run(){
// 没有用到
//qDebug() << "[info] " << __FUNCTION__ << " line:" << __LINE__ << " success!";
return true;
}
JsonRpcClient::JsonRpcClient(const QString& host, quint16 port)
:host_(host), port_(port), idCounter_(1) {
socket_.connectToHost(host_, port_);
if (!socket_.waitForConnected(3000)) {
qWarning() << "[JsonRpcClient] Initial connection failed:" << socket_.errorString();
_connectionStatus = Disconnected;
}
else {
qDebug() << "[JsonRpcClient] Connected to" << host_ << ":" << port_;
_connectionStatus = Connected;
}
}
bool JsonRpcClient::reconnect() {
if (socket_.state() == QAbstractSocket::ConnectedState) {
return true;
}
socket_.abort(); // 清理旧连接
socket_.connectToHost(host_, port_);
if (socket_.waitForConnected(3000)) {
qDebug() << "[JsonRpcClient] Reconnected successfully.";
return true;
}
else {
qWarning() << "[JsonRpcClient] Reconnection failed:" << socket_.errorString();
return false;
}
}
QVariant JsonRpcClient::call(const QString& method, const QJsonArray& params, const QString& module){
QMutexLocker locker(&mutex_);
if (socket_.state() != QAbstractSocket::ConnectedState) {
if (!reconnect()) return QVariant();
}
int myId = idCounter_++;
QJsonObject req{
{"jsonrpc", "2.0"},
{"method", method},
{"params", params},
{"id", myId}
};
if (!module.isEmpty()) req.insert("module", module);
QJsonObject resp = sendRequestBuildAndAwait(req);
if (resp.contains("error")) {
qWarning() << "[JsonRpcClient] Error:" << QJsonDocument(resp["error"].toObject()).toJson(QJsonDocument::Compact);
return QVariant();
}
if (!resp.contains("result")) return QVariant();
QJsonValue r = resp.value("result");
if (r.isString()) return r.toString();
if (r.isDouble()) return r.toDouble();
if (r.isBool()) return r.toBool();
if (r.isArray()) return r.toArray().toVariantList();
if (r.isObject()) return r.toObject().toVariantMap();
return QVariant();
}
QByteArray JsonRpcClient::readCall(const QString& method, const QString& params, const QString& module) {
auto commands = params.split("_");
QMutexLocker locker(&mutex_);
if (socket_.state() != QAbstractSocket::ConnectedState) {
if (!reconnect()) return QByteArray();
}
int myId = idCounter_++;
QJsonArray p; p.append(params);
QJsonObject req{{"jsonrpc","2.0"},{"method",method},{"params",p},{"id",myId}};
if (!module.isEmpty()) req.insert("module", module);
QJsonDocument doc(req);
QByteArray payload = doc.toJson(QJsonDocument::Compact);
if (socket_.write(payload) == -1 || !socket_.waitForBytesWritten(1000)) {
qWarning() << "[JsonRpcClient] Write failed.";
return QByteArray();
}
QJsonObject respObj;
if (!waitAndReadResponse(myId, respObj)) {
qWarning() << "[JsonRpcClient] Read timeout or parse error.";
return QByteArray();
}
QString b64 = respObj.value("result").toString();
QByteArray decoded = QByteArray::fromBase64(b64.toUtf8());
if (b64.contains("error") || decoded.size()% commands[3].toInt() != 0) {
qWarning() << "[ERROR]" << b64;
QThread::msleep(500);
return b64.toUtf8();
}
return decoded;
}
QList<QByteArray> JsonRpcClient::extractJsonFrames(QByteArray& buffer) {
QList<QByteArray> frames;
int start = -1;
int braceDepth = 0;
bool inString = false;
char prev = 0;
for (int i = 0; i < buffer.size(); ++i) {
char c = buffer[i];
if (c == '"' && prev != '\\') inString = !inString;
if (!inString) {
if (c == '{') {
if (braceDepth == 0) start = i;
braceDepth++;
} else if (c == '}') {
braceDepth--;
if (braceDepth == 0 && start != -1) {
int len = i - start + 1;
frames.append(buffer.mid(start, len));
start = -1;
}
}
}
prev = c;
}
if (braceDepth == 0) {
if (!frames.isEmpty()) {
int lastEnd = buffer.lastIndexOf('}');
if (lastEnd != -1) buffer = buffer.mid(lastEnd + 1);
}
} else {
if (start > 0) buffer = buffer.mid(start);
}
return frames;
}
bool JsonRpcClient::waitAndReadResponse(int expectId, QJsonObject& outResp, int timeoutMs) {
QElapsedTimer timer; timer.start();
while (timer.elapsed() < timeoutMs) {
if (socket_.bytesAvailable() == 0) {
int remain = timeoutMs - (int)timer.elapsed();
if (!socket_.waitForReadyRead(qMin(remain, 1000))) {
continue;
}
}
QByteArray chunk = socket_.readAll();
if (!chunk.isEmpty()) recvBuffer_.append(chunk);
auto frames = extractJsonFrames(recvBuffer_);
for (const QByteArray& f : frames) {
QJsonParseError perr;
QJsonDocument jd = QJsonDocument::fromJson(f, &perr);
if (perr.error != QJsonParseError::NoError) {
qWarning() << "[JsonRpcClient] parse frame error:" << perr.errorString();
continue;
}
if (jd.isArray()) {
for (const QJsonValue& v : jd.array()) {
if (!v.isObject()) continue;
QJsonObject obj = v.toObject();
if (obj.value("id").toInt() == expectId) { outResp = obj; return true; }
}
} else if (jd.isObject()) {
QJsonObject obj = jd.object();
if (obj.value("id").toInt() == expectId) { outResp = obj; return true; }
}
}
}
return false;
}
QJsonObject JsonRpcClient::sendRequestBuildAndAwait(const QJsonObject& req, int timeoutMs) {
QByteArray payload = QJsonDocument(req).toJson(QJsonDocument::Compact);
int id = req.value("id").toInt();
if (socket_.write(payload) == -1 || !socket_.waitForBytesWritten(1000)) {
return QJsonObject{{"error", QJsonObject{{"code", -1},{"message", "write failed"}}}};
}
QJsonObject resp;
if (!waitAndReadResponse(id, resp, timeoutMs)) {
return QJsonObject{{"error", QJsonObject{{"code", -2},{"message", "timeout"}}}};
}
return resp;
}
void JsonRpcClient::writeCall(const QString& method, const QJsonArray& params, const QString& module) {
call(method, params, module);
}
void JsonRpcClient::writeCall(const QByteArray& requestByte, const int& id) {
QMutexLocker locker(&mutex_);
if (socket_.state() != QAbstractSocket::ConnectedState) {
if (!reconnect()) {
qWarning() << "write failure: no connnection";
return;
}
}
QJsonObject resp;
if (socket_.write(requestByte) == -1 || !socket_.waitForBytesWritten(1000)) {
qWarning() << "write failure: failed";
resp = QJsonObject{ {"error", QJsonObject{{"code", -1},{"message", "write failed"}}} };
return;
}
if (!waitAndReadResponse(id, resp, 8000)) {
qWarning() << "write failure: no response";
return;
}
if (resp.contains("error")) {
qWarning() << "[JsonRpcClient] Error:" << QJsonDocument(resp["error"].toObject()).toJson(QJsonDocument::Compact);
return;
}
return;
}