Compare commits

...

4 Commits

Author SHA1 Message Date
Mr.V 6be58c3892 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	src/ConfigUtil/ConfigUtil.h
#	src/common/common.h
2024-04-24 16:20:45 +08:00
zhangwei 9c365e2658 更新 src/qt_source/mainwindow.cpp
修改过滤条件解决部分特殊字符导致标签信息过滤
2024-04-24 16:19:41 +08:00
zhangwei 35f1e6dd81 revert 920f3fa341
revert 更新自动重连串口功能,若人为点击“打开串口”,则只要串口连接失败,就会在10秒后重新尝试连接;直到连接成功或人为点击“关闭串口”
2024-04-24 16:19:35 +08:00
Mr.V 64c2559b0f 更新自动重连串口功能,若人为点击“打开串口”,则只要串口连接失败,就会在10秒后重新尝试连接;直到连接成功或人为点击“关闭串口” 2024-04-24 16:14:33 +08:00
13 changed files with 662 additions and 47 deletions

2
.gitignore vendored
View File

@ -15,7 +15,7 @@
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
#*.dll
# Fortran module files
*.mod

View File

@ -16,5 +16,6 @@ username=guest_01
password=d55b0f642e817eea24725d2f2a31dd08
[socket_server]
server_ip=192.168.2.212
server_ip=192.168.2.1
server_port=7000
delayed_upload=4

BIN
lib/libcurl.dll Normal file

Binary file not shown.

BIN
lib/libyaml-cpp.dll Normal file

Binary file not shown.

BIN
lib/libyaml-cpp.dll.a Normal file

Binary file not shown.

View File

@ -38,6 +38,7 @@ QT5_WRAP_CPP(MOC_Files
qt_source/mainwindow.h
interface/TcpClient.h
threads/UpResultThread.h
threads/ReadComThread.h
)
include_directories(
@ -70,6 +71,8 @@ include_directories(
interface
#
serial
# 线
threads
)

View File

@ -7,46 +7,9 @@
#include <string>
#include <QFileInfo>
#include <QTextCodec>
#include "common.h"
namespace ai_matrix {
struct BaseConfig {
// com口名称
QString comName;
// 波特率
int baud;
// 股道编号
int trackName;
// 是否有磁钢
bool havaMagnetSteel;
// 磁钢顺序
QString magnetSteelOrder;
// 上传识别结果标志
bool upResult;
// 使用socket来车通讯
bool useSocketServer;
};
struct InterfaceConfig {
// 接口服务器IP
QString httpIp;
// 接口服务器端口
int httpPort;
// 获取token的方法
QString tokenPath;
// 上传识别结果的方法
QString upResultPath;
// 用户名
QString username;
// 密码
QString password;
};
struct SServerConfig {
// socket server IP
QString server_ip;
// server_port
int server_port;
};
};
class ConfigUtil {

View File

@ -1,6 +1,4 @@
//
// Created by Mr.V on 2023/8/16.
//
#include "common.h"
QString io_car = "";
QString direction = "";
#include "common.h"

View File

@ -20,6 +20,51 @@
namespace ai_matrix {
const QString g_config_path = "./config/config.ini";
// --- 配置文件 ---
struct BaseConfig {
// com口名称
QString comName;
// 波特率
int baud;
// 股道编号
int trackName;
// 是否有磁钢
bool havaMagnetSteel;
// 磁钢顺序
QString magnetSteelOrder;
// 上传识别结果标志
bool upResult;
// 使用socket来车通讯
bool useSocketServer;
};
struct InterfaceConfig {
// 接口服务器IP
QString httpIp;
// 接口服务器端口
int httpPort;
// 获取token的方法
QString tokenPath;
// 上传识别结果的方法
QString upResultPath;
// 用户名
QString username;
// 密码
QString password;
};
struct SServerConfig {
// socket server IP
QString server_ip;
// server_port
int server_port;
// 识别结果延后X节上传以等待Socket反馈火车运行方向
int delayed_upload;
};
// --- 配置文件 ---
struct TrainInfo {
std::string carriageType;
std::string carriageNum;
@ -29,6 +74,20 @@ namespace ai_matrix {
std::string strRfidInfo;
};
struct ComeTrain {
QString trainTime;
bool needUpLoad;
ComeTrain () {
this->trainTime = "";
this->needUpLoad = false;
};
};
struct RfidInfo {
QString rfid;
QString rfidTime;
};
}

View File

@ -0,0 +1,485 @@
//
// Created by Mr.V on 2024/4/15.
//
#include "ReadComThread.h"
ReadComThread::ReadComThread(MQueue<TrainInfo> *queueTrainInfo,
QObject *parent) :
QThread(parent)
{
// 配置文件读取
QString errorMessage = "";
if (!ConfigUtil::readBaseConfig(g_config_path, errorMessage, this->baseConfig_))
{
this->ErrorSignals(errorMessage);
}
if (!ConfigUtil::readSocketServerConfig(g_config_path, errorMessage, this->socketServerConfig_))
{
this->ErrorSignals(errorMessage);
}
this->queueTrainInfo_ = queueTrainInfo;
this->logRfidRecvName = "./Logs/rfid_data.txt";
this->recvLog.setFileName(logRfidRecvName);
this->recvLog.open(QIODevice::ReadWrite | QIODevice::Append);
connect(this, &ReadComThread::getRfid_signals, this, &ReadComThread::getQueueDataThread);
connect(this, &ReadComThread::IdentifyType, this, &ReadComThread::IdentifyTypeUpdate);
connect(this, &ReadComThread::upRfid_signals, this, &ReadComThread::upRfid);
if (this->baseConfig_.useSocketServer)
{
// 创建线程
QThread *threadWork = new QThread; // 任务线程
this->tcpClient = new TcpClient(this);
this->tcpClient->moveToThread(threadWork);
connect(this, &ReadComThread::connect_socket, this->tcpClient, &TcpClient::connectToServer);
// 子线程反馈 socket 到主线程
connect(this->tcpClient, &TcpClient::socketComplete, this, [=](QTcpSocket* tcp, QString ip, quint16 port){
this->tcp_ = tcp;
});
// 接收子线程的消息提示
connect(this->tcpClient, &TcpClient::sendTcpInfoSignals, this, [=](const QString& info){
this->InfoSignals(info);
this->statusInfoUpdate(info);
if (info == "链接服务端超时"
|| info == "未连接Socket服务器"
|| info == "链接服务端失败"
|| info == "服务端断开连接"
|| info == "链接服务端失败")
{
this->videoHasTrain = false;
}
});
// 接收子线程的TCP消息内容展示
connect(this->tcpClient, &TcpClient::getTcpInfoSignals, this, [=](const QString& info){
this->InfoSignals(info);
});
emit connect_socket(this->socketServerConfig_.server_ip, this->socketServerConfig_.server_port);
connect(this->tcpClient, &TcpClient::comeTrainSignals, this, [=](bool type) {
if (type)
{
this->InfoSignals("视频车号识别-来车了");
this->videoHasTrain = true;
emit IdentifyType();
}
else
{
this->InfoSignals("视频车号识别-火车离开了");
this->videoHasTrain = false;
if (this->needIdentify)
emit IdentifyType();
}
});
connect(this->tcpClient, &TcpClient::getDirectionSignals, this, [=](int direction) {
if (this->iDirection == 0)
{
this->InfoSignals("获得方向判定:" + QString(direction > -1 ? (direction > 0 ? "需识别" : "待定") : "无需上传"));
this->iDirection = direction;
}
});
}
}
void ReadComThread::run()
{
while (true)
{
QThread::msleep(100);
if (this->auto_reconnect_serial_)
{
QThread::msleep(10 * 1000);
if ((this->serial_ == nullptr || !this->serial_->isOpen()))
{
this->openCom(true, 0);
}
}
}
}
void ReadComThread::openCom(const bool &type, const int &msec)
{
QThread::msleep(msec);
if (type)
{
this->auto_reconnect_serial_ = true;
this->serial_ = new QSerialPort;
this->serial_ = this->comDetect_.openCom(this->baseConfig_.comName, this->baseConfig_.baud);
if (this->serial_ != nullptr)
{
QString logs;
logs.append("串口打开成功\n");
logs.append("串口号:" + this->baseConfig_.comName + "\n");
logs.append("波特率:" + QString::number(this->baseConfig_.baud) + "\n");
if (this->baseConfig_.havaMagnetSteel)
logs.append("磁钢顺序:" + this->baseConfig_.magnetSteelOrder);
this->isOpenCom = true;
this->InfoSignals(logs);
connect(this->serial_,&QSerialPort::readyRead,this,&ReadComThread::readCom); //
connect(this->serial_, &QSerialPort::errorOccurred, this, &ReadComThread::serialPort_error);
this->openComSuccess(true);
this->initParam();
}
else
{
this->isOpenCom = false;
this->ErrorSignals("串口打开失败!");
this->openComSuccess(false);
}
}
else
{
this->auto_reconnect_serial_ = false;
if (this->comDetect_.closeCom())
{
this->initParam();
this->InfoSignals("串口已关闭!");
this->closeComSuccess(true);
}
else
{
this->closeComSuccess(false);
}
}
}
void ReadComThread::readCom()
{
try {
if (this->serial_->waitForReadyRead(10)) {
QByteArray data = this->serial_->readAll(); // 读取数据
if(!data.isEmpty())
{
this->queue_.push(QString(data));
emit getRfid_signals();
}
data.clear();
}
else
{
this->ErrorSignals("等待读取超时");
}
} catch(...) {
this->ErrorSignals("读取串口数据异常!");
}
}
void ReadComThread::readTestInfo(const QString &filePath) {
// 创建一个QFile对象并打开要读取的文本文件
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
this->ErrorSignals("无法打开测试文件:" + filePath);
return;
}
this->InfoSignals("开始读取测试数据...");
// 将文件内容读入到QStringList中
QTextStream in(&file);
while(!in.atEnd()) {
QString line = in.readLine();
this->queue_.push(line);
emit getRfid_signals();
}
// 关闭文件
file.close();
this->InfoSignals("---读取测试数据完毕---");
}
/**
* @brief:queue中获取数据
* @param:
* @return:
*/
void ReadComThread::getQueueDataThread()
{
if (this->queue_.isEmpty()) return;
QString strRfidInfo = this->queue_.getTop();
this->queue_.pop();
this->saveRfidLog(strRfidInfo);
this->tmpRfid.append(strRfidInfo);
if (this->tmpRfid.right(1) != "&")
{
return;
}
this->tmpRfid = this->tmpRfid.replace("&", "");
QStringList rfidSubList = this->tmpRfid.split("@");
this->tmpRfid = "";
auto split_lambda = [=](QString strFirst, QString strOther){
if (strFirst == "0")
{
this->rfidHasTrain = false;
this->InfoSignals("RFID-火车离开了");
// 关功放信号
// 需要结合 “来车状态”广播消息,确认是否真的车离开了; TODO:没有真离开,则向串口发送 @on& 避免关功放
emit IdentifyType();
}
else
{
// TODO: 此处按照4个磁钢来配置可能存在一定限制; 另外逻辑需要优化
if (strOther == "000000" // 第一次检测来车时(前提是上一次功放已关闭)
&& (strFirst == this->baseConfig_.magnetSteelOrder.left(1)
|| strFirst == this->baseConfig_.magnetSteelOrder.mid(1, 1)))
{
this->rfidHasTrain = true;
this->InfoSignals("RFID-来车了");
emit IdentifyType();
}
}
};
for (const auto & i : rfidSubList)
{
if (i.size() == 26 || i.size() == 24)
{
// 车厢信息
QString strTrainInfo = i.mid(1,13);
if (vecTrain.empty() || !vecTrain.contains(strTrainInfo))
{
// 判断是否是车头 车头的第14位是 K或H (慎重)
if (!i.mid(14, 1).contains(QRegExp("^[0-9]+$"))) continue;
// 过滤掉车号中有非数字的
if (!strTrainInfo.right(7).contains(QRegExp("^[0-9]+$"))) continue;
// 因信号不稳定 增加一行过滤 出现读到的编号数据里 空格替代了实际字符的情况
if (strTrainInfo.right(7).simplified().size() < 7) continue;
vecTrain.append(strTrainInfo);
int train_order = vecTrain.size();
// this->resultTableModel_->setItem(train_order - 1, 0, new QStandardItem(strTrainInfo));
this->addResultSignals(strTrainInfo);
this->InfoSignals(QString("第%1节 %2").arg(train_order).arg(strTrainInfo));
// 将接收到的RFID使用@符拼接起来以待后续上传web
this->rfidSourceInfo.append("@" + i);
QString rfidinfo = this->rfidSourceInfo;
this->rfidSourceInfo = "";
emit upRfid_signals(rfidinfo);
}
}
else if (i.size() == 7)
{
// 不使用磁钢的情况下 过滤掉磁钢信号
if (!this->baseConfig_.havaMagnetSteel) continue;
// 磁钢信号
QString strFirst = i.left(1);
QString strOther = i.mid(1, i.size() - 1);
// 将接收到的RFID使用@符拼接起来以待后续上传web
this->rfidSourceInfo.append("@" + i);
split_lambda(strFirst, strOther);
}
else if (i.size() == 14) // 特殊情况,来车信号少了&符
{
// 不使用磁钢的情况下 过滤掉磁钢信号
if (!this->baseConfig_.havaMagnetSteel) continue;
for (int j = 0; j < i.size(); j+=7) {
QString rfid_cg = i.mid(j, 7);
QString strFirst = rfid_cg.left(1);
QString strOther = rfid_cg.mid(1, 6);
// 将接收到的RFID使用@符拼接起来以待后续上传web
this->rfidSourceInfo.append("@" + rfid_cg);
split_lambda(strFirst, strOther);
}
}
}
}
void ReadComThread::IdentifyTypeUpdate() {
if (this->rfidHasTrain || this->videoHasTrain)
{
if (!this->needIdentify)
{
this->InfoSignals("来车了");
this->mkRfidLog();
this->vecTrain.clear();
this->needIdentify = true;
this->trainTime = this->getSystemTime();
emit this->comeTrain(false);
}
}
else if (!this->rfidHasTrain && !this->videoHasTrain)
{
this->initParam();
this->InfoSignals("火车离开了");
}
}
void ReadComThread::upRfid(const QString &rfidInfo)
{
if (!this->baseConfig_.upResult) return;
if (!this->needIdentify && this->baseConfig_.havaMagnetSteel)
{
emit upResultType(false);
return;
}else{
emit upResultType(true);
}
QString info = this->vecTrain.back();
int order = this->vecTrain.size();
if (order > this->socketServerConfig_.delayed_upload && this->iDirection == 0)
{
this->iDirection = 1;
}
if (!this->baseConfig_.useSocketServer) this->iDirection = 1;
TrainInfo trainInfo;
trainInfo.carriageType = info.left(6).simplified().toStdString();
trainInfo.carriageNum = info.mid(6, info.size() - 6).simplified().toStdString();
trainInfo.strOrder = QString::number(order).toStdString();
trainInfo.trainTime = this->trainTime.toStdString();
trainInfo.collectTime = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz").toStdString();
trainInfo.strRfidInfo = rfidInfo.toStdString();
this->queueTmpTrainInfo_.push(trainInfo);
if (this->iDirection < 0)
{
this->queueTmpTrainInfo_.clear();
return;
}
if (this->iDirection > 0)
{
while (!this->queueTmpTrainInfo_.isEmpty())
{
this->queueTrainInfo_->push(this->queueTmpTrainInfo_.pop());
}
}
}
bool ReadComThread::mkRfidLog()
{
try {
QFileInfo fileInfo(this->logRfidRecvName);
if(fileInfo.isFile())
{
return true;
}
this->recvLog.setFileName(this->logRfidRecvName);
if (!this->recvLog.isOpen())
{
this->recvLog.open(QIODevice::ReadWrite | QIODevice::Append);
}
return true;
} catch (const std::exception &e) {
this->ErrorSignals("创建、打开RFID原始数据日志文件失败,详细:");
this->ErrorSignals(e.what());
}
return false;
}
void ReadComThread::saveRfidLog(const QString &value)
{
try {
QString logValue = value + "\n";
if (!this->recvLog.isOpen())
{
this->recvLog.open(QIODevice::ReadWrite | QIODevice::Append);
}
this->recvLog.write(logValue.toUtf8());
this->recvLog.flush();
} catch (const std::exception &e) {
this->ErrorSignals("保存RFID原数据失败");
}
}
bool ReadComThread::rnameRfidLog()
{
try {
this->recvLog.close();
QFile logFile(this->logRfidRecvName);
QFileInfo fileInfo(logFile);
if (fileInfo.size() == 0) return false;
QString newLogFileName = fileInfo.path() + "/";
newLogFileName.append(QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss"));
newLogFileName.append(".txt");
if (!logFile.rename(newLogFileName))
{
this->ErrorSignals("读取RFID原始数据存储重命名失败");
return false;
}
this->deleteOldFiles(fileInfo.path(), 3);
} catch (const std::exception &e) {
this->ErrorSignals("读取RFID原始数据存储重命名失败");
return false;
}
return true;
}
void ReadComThread::deleteOldFiles(const QString &path, int days)
{
QDir dir(path);
foreach(QFileInfo fi, dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot))
{
if((fi.lastModified().addDays(days) < QDateTime::currentDateTime()))
{
QFile::remove(fi.absoluteFilePath());
}
}
}
/**
* 退
* @param error
*/
void ReadComThread::serialPort_error(QSerialPort::SerialPortError error)
{
if (error != QSerialPort::NoError)
{
//关闭串口
this->serial_->close();
this->ErrorSignals("串口异常退出,疑似接口松动;");
this->openComSuccess(false);
// this->ui->openComButton->setText("打开串口");
// this->ui->LEDlabel->setStyleSheet("border-radius: 16px; \ "
// "background-color: red;\ "
// "border:1px solid rgba(168, 168, 168, 105);");
}
}
void ReadComThread::initParam() {
if (this->recvLog.isOpen()) this->recvLog.close();
this->rfidSourceInfo = "";
this->vecTrain.clear();
this->rnameRfidLog();
this->queue_.clear();
this->trainTime = "";
this->needIdentify = false;
this->rfidHasTrain = false;
this->videoHasTrain = false;
this->iDirection = 0;
}
/**
* @brief:
* @param
* @return:
*/
QString ReadComThread::getSystemTime()
{
return QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
}

108
src/threads/ReadComThread.h Normal file
View File

@ -0,0 +1,108 @@
//
// Created by Mr.V on 2024/4/15.
//
#ifndef TRAIN_RFID_READCOMTHREAD_H
#define TRAIN_RFID_READCOMTHREAD_H
#include <QThread>
#include <QtSerialPort/QSerialPortInfo>
#include <QtSerialPort/QSerialPort>
#include "MQueue.h"
#include "common.h"
#include "ComDetect.h"
#include "TcpClient.h"
#include "ConfigUtil.h"
using namespace ai_matrix;
class ReadComThread : public QThread{
Q_OBJECT
public:
ReadComThread(MQueue<TrainInfo> *queueTrainInfo,
QObject *parent = nullptr);
~ReadComThread() {};
void run() override;
private:
MQueue<TrainInfo> *queueTrainInfo_ = nullptr;
MQueue<TrainInfo> queueTmpTrainInfo_{}; // RFID临时信息队列
QSerialPort *serial_; //串口
bool auto_reconnect_serial_ = false;
ai_matrix::BaseConfig baseConfig_;
ai_matrix::SServerConfig socketServerConfig_;
TcpClient* tcpClient;
QTcpSocket* tcp_ = nullptr;
// Com口数据读取类
ComDetect comDetect_;
// 打开com口标志
bool isOpenCom = false;
// RFID读取的数据队列
MQueue<QString> queue_{};
QString logRfidRecvName;
QFile recvLog;
QString trainTime;
// 是否是需要识别的方向 -1:不识别 0:未确定 1:识别
int iDirection = 0;
// 整列车识别结果存储
QStringList vecTrain;
QString rfidSourceInfo;
QString tmpRfid; //临时存储的RFID原始数据用于防止接收的数据不完整
// 识别方向正确标志
bool needIdentify = false;
// RFID识别有火车标志
bool rfidHasTrain = false;
// 视频识别有火车标志使用socket服务时才有用
bool videoHasTrain = false;
void initParam();
// 创建日志目录
bool mkRfidLog();
void deleteOldFiles(const QString &path, int days);
bool rnameRfidLog();
void saveRfidLog(const QString &value);
QString getSystemTime();
signals:
void DebugSignals(const QString &info);
void InfoSignals(const QString &info);
void WarnSignals(const QString &info);
void ErrorSignals(const QString &info);
void statusInfoUpdate(const QString &info);
void connect_socket(QString ip, int port);
void openComSuccess(const bool &success);
void closeComSuccess(const bool &success);
void upResultType(const bool &type);
void addResultSignals(const QString &trainInfo);
void comeTrain(const bool &type);
void getRfid_signals();
void upRfid_signals(const QString &rfidInfo);
void IdentifyType();
public slots:
void openCom(const bool &type, const int &msec = 0);
void readCom();
void serialPort_error(QSerialPort::SerialPortError error);
void getQueueDataThread();
void IdentifyTypeUpdate();
void upRfid(const QString &rfidInfo);
void readTestInfo(const QString &filePath);
};
#endif //TRAIN_RFID_READCOMTHREAD_H

View File

@ -5,11 +5,10 @@
#include "UpResultThread.h"
UpResultThread::UpResultThread(MQueue<TrainInfo> *queueTrainInfo, int *iDirection, QObject *parent) :
UpResultThread::UpResultThread(MQueue<TrainInfo> *queueTrainInfo, QObject *parent) :
QThread(parent)
{
this->queueTrainInfo_ = queueTrainInfo;
this->iDirection = iDirection;
// 配置文件读取
QString errorMessage = "";

View File

@ -19,13 +19,12 @@ class UpResultThread : public QThread{
public:
~UpResultThread() {};
UpResultThread(MQueue<TrainInfo> *queueTrainInfo, int *iDirection, QObject *parent = nullptr);
UpResultThread(MQueue<TrainInfo> *queueTrainInfo, QObject *parent = nullptr);
void run() override;
private:
MQueue<TrainInfo> *queueTrainInfo_ = nullptr;
int *iDirection;
ai_matrix::BaseConfig baseConfig_;
ai_matrix::InterfaceConfig interfaceConfig_;
std::string webToken; //授权信息