Train_RFID/src/qt_source/mainwindow.cpp

721 lines
24 KiB
C++
Raw Normal View History

2024-02-29 06:20:06 +00:00
#include <QListView>
#include "MainWindow.h"
#include "ui_MainWindow.h"
#define DEBUG_HTTP 1
MainWindow::MainWindow(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowTitle("Matrix_RFID");
this->configPath_ = "./config/config.ini";
if (!this->mkLogDir())
{
this->logError("创建Log目录失败");
}
// 配置文件读取
QString errorMessage = "";
if (!ConfigUtil::readBaseConfig(this->configPath_, errorMessage, this->baseConfig_))
{
this->logError(errorMessage);
}
if (!ConfigUtil::readInterfaceConfig(this->configPath_, errorMessage, this->interfaceConfig_))
{
this->logError(errorMessage);
}
// 获取本机串口列表
this->getComList();
// 初始化界面上的串口打开参数
this->initComboBox();
this->serial_ = new QSerialPort;
m_pSystemTray=new QSystemTrayIcon(this);
connect(this->ui->openComButton, &QPushButton::clicked, this, &MainWindow::openComClicked);
connect(this, &MainWindow::getRfid_signals, this, &MainWindow::getQueueDataThread);
connect(this, &MainWindow::upRfid_signals, this, &MainWindow::upRfid);
//最小化信号槽
connect(m_pSystemTray,SIGNAL(activated(QSystemTrayIcon::ActivationReason)),this,SLOT(on_activatedSysTrayIcon(QSystemTrayIcon::ActivationReason)));
this->logRfidRecvName = "./Logs/rfid_data.txt";
recvLog.setFileName(logRfidRecvName);
recvLog.open(QIODevice::ReadWrite | QIODevice::Append);
//初始最小化
//this->showMinimized();//最小化
// 设置最大展示数据行数
this->ui->textBrowser->document()->setMaximumBlockCount(1000);
getStandardItemModel();
this->ui->resultTable->setModel(this->resultTableModel_);
this->ui->resultTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
this->ui->resultTable->verticalHeader()->setMinimumWidth(50);
if (!this->baseConfig_.upResult)
{
QIcon icon("./Fail.ico");
QPixmap m_pic = icon.pixmap(icon.actualSize(QSize(32, 32)));//size自行调整
this->ui->upflagLabel->setPixmap(m_pic);
}else{
QIcon icon("./Succ.ico");
QPixmap m_pic = icon.pixmap(icon.actualSize(QSize(32, 32)));//size自行调整
this->ui->upflagLabel->setPixmap(m_pic);
}
}
MainWindow::~MainWindow()
{
this->logInfo("-- 程序退出 --");
this->recvLog.close();
this->vecTrain.clear();
this->needIdentify = false;
this->rnameRfidLog();
this->queue_.clear();
delete ui;
}
void MainWindow::logDebug(const QString& message) {
LogDebug << message.toStdString();
ui->textBrowser->append(QString("[Debug] [") + this->currentTime_.toString("yyyy-MM-dd hh:mm:ss") + QString::fromStdString("] ") + message);
}
void MainWindow::logInfo(const QString& message) {
LogInfo << message.toStdString();
ui->textBrowser->append(QString("[Info] [") + this->currentTime_.toString("yyyy-MM-dd hh:mm:ss") + QString::fromStdString("] ") + message);
}
void MainWindow::logWarn(const QString& message) {
LogWarn << message.toStdString();
ui->textBrowser->append(QString("[Warn] [") + this->currentTime_.toString("yyyy-MM-dd hh:mm:ss") + QString::fromStdString("] ") + message);
}
void MainWindow::logError(const QString& message) {
LogError << message.toStdString();
ui->textBrowser->append(QString("[Error] [") + this->currentTime_.toString("yyyy-MM-dd hh:mm:ss") + QString::fromStdString("] ") + message);
}
void MainWindow::initParam() {
this->trainTime = "";
}
/**
*
* @return bool
*/
bool MainWindow::mkLogDir()
{
if (_access("Logs", 0) == -1) { //判断是否存在日志的文件夹,没有则创建
std::string folderPath = "Logs";
if (0 != _mkdir(folderPath.c_str()))
{
return false;
}
}
return true;
}
bool MainWindow::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->logError("创建、打开RFID原始数据日志文件失败,详细:");
this->logError(e.what());
}
return false;
}
bool MainWindow::rnameRfidLog()
{
try {
this->recvLog.close();
QFile logFile(this->logRfidRecvName);
QFileInfo fileInfo(logFile);
QString newLogFileName = fileInfo.path() + "/";
newLogFileName.append(QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss"));
newLogFileName.append(".txt");
if (!logFile.rename(newLogFileName))
{
this->logError("读取RFID原始数据存储重命名失败");
return false;
}
} catch (const std::exception &e) {
this->logError("读取RFID原始数据存储重命名失败");
return false;
}
return true;
}
void MainWindow::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->logError("保存RFID原数据失败");
}
}
/**
*
* @return
*/
QList<QString> MainWindow::getComList() {
// 获取系统所有串口信息
QList<QSerialPortInfo> availablePorts = QSerialPortInfo::availablePorts();
for (const auto & availablePort : availablePorts) {
this->comList.append(availablePort.portName());
}
return this->comList;
}
/**
* comboxBox
*/
void MainWindow::initComboBox() {
// ui->BTBox->setEditable(true);
ui->BTBox->setStyleSheet("QComboBox {background-color: rgb(255, 255, 255);} QComboBox QAbstractItemView::item {min-height: 30px; background-color: rgb(225, 225, 225);}");
ui->BTBox->setView(new QListView());
ui->comBox->setEditable(true);
ui->comBox->setStyleSheet("QComboBox {background-color: rgb(255, 255, 255);} QComboBox QAbstractItemView::item {min-height: 30px; background-color: rgb(225, 225, 225);}");
ui->comBox->setMaxVisibleItems(5);
ui->comBox->setView(new QListView());
ui->comBox->addItems(this->comList);
if (this->baseConfig_.comName != "") {
if (ui->comBox->findText(this->baseConfig_.comName) == -1)
{
ui->comBox->addItem(this->baseConfig_.comName);
}
ui->comBox->setCurrentText(this->baseConfig_.comName);
}
if (ui->BTBox->findText(QString::number(this->baseConfig_.baud)) == -1)
{
ui->BTBox->addItem(QString::number(this->baseConfig_.baud));
}
ui->BTBox->setCurrentText(QString::number(this->baseConfig_.baud));
ui->truckEdit->setText(QString::number(this->baseConfig_.trackName));
}
/**
* @brief:
* @param
* @return:
*/
QString MainWindow::getSystemTime()
{
return QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
}
/**
* ItemModel
* @return
*/
QStandardItemModel* MainWindow::getStandardItemModel()
{
if (this->resultTableModel_== nullptr) this->resultTableModel_ = new QStandardItemModel();
this->resultTableModel_->setColumnCount(1);
this->resultTableModel_->setHeaderData(0,Qt::Horizontal, "车号");
return this->resultTableModel_;
}
QStandardItem* MainWindow::getStandardItem(const QString &value)
{
QStandardItem *resultTableItem = new QStandardItem();
resultTableItem->setTextAlignment(Qt::AlignCenter);
resultTableItem->setText(value);
return resultTableItem;
}
/******************************同 web 交互****************************************/
/**
* @brief:Http发送RFID数据
* @paramcarriageType
* @paramcarriageNumber
* @paramcarriageOrder
* @paramtime
* @return: :true
* :false
*/
bool MainWindow::upWeb(std::string &carriageType, std::string &carriageNumber, std::string &carriageOrder, std::string &time)
{
try {
httplib::Client cli(this->interfaceConfig_.httpIp.toStdString(), this->interfaceConfig_.httpPort);
cli.set_connection_timeout(0, 300 * 1000);
httplib::Headers header;
httplib::Params params;
Json::Value arrayObj; //构建对象
header.emplace("blade-auth", this->webToken);
//header.emplace("Content-Type", "application/json");
arrayObj["comeTime"] = time;
arrayObj["carriageNumber"] = carriageNumber;
arrayObj["carriageType"] = carriageType;
arrayObj["carriageOrder"] = carriageOrder;
Json::Value trainParams;
trainParams["poundNo"] = std::to_string(this->baseConfig_.trackName);
arrayObj["trainParams"] = trainParams;
Json::StreamWriterBuilder writer;
std::string str = Json::writeString(writer, arrayObj);
this->logInfo("发送web: " + QString::fromStdString(str));
auto res = cli.Post(this->interfaceConfig_.upResultPath.toStdString(), header, str, "application/json");
if (res)
{
if (res->status == 200)
{
this->logInfo("web返回: " + QString::fromStdString(res->body));
Json::CharReaderBuilder readerBuilder;
std::istringstream iss(res->body);
Json::Value root;
std::string errs;
bool parsingSuccessful = Json::parseFromStream(readerBuilder, iss, &root, &errs);
if (parsingSuccessful)
{
if (root["success"].asBool())
{
return true;
}
else
{
if (root["msg"].asString() == "请求未授权") {
this->logWarn("因请求未授权而上传识别结果失败重新请求token。");
this->getToken();
return this->upWeb(carriageType, carriageNumber, carriageOrder, time);
}
this->logError("识别结果上传失败,原因:" + QString::fromStdString(root.asString()));
}
}
else
{
this->logError("识别结果上传失败返回数据解析异常返回数据非json" + QString::fromStdString(res->body));
}
}
else
{
this->logError("识别结果上传失败,原因:" + QString::number(res->status) + " - " + QString::fromStdString(res->body));
if (res->status == 401) {
this->logWarn("因请求未授权而上传识别结果失败重新请求token。");
this->getToken();
return this->upWeb(carriageType, carriageNumber, carriageOrder, time);
}
}
}
else
{
this->logError("上传数据失败,请检查网络,或者上传地址!");
}
}
catch (std::exception &e)
{
this->logError("上传识别结果失败,原因:");
this->logError(e.what());
}
return false;
}
/**
* @brief:Http获取授权
* @param
* @return:
*/
bool MainWindow::getToken()
{
try
{
httplib::Client cli(this->interfaceConfig_.httpIp.toStdString(), this->interfaceConfig_.httpPort);
httplib::Headers header;
httplib::Params params;
header.emplace("Authorization", "Basic Y2xpZW50X2VudGVycHJpc2U6Y2xpZW50X2VudGVycHJpc2Vfc2VjcmV0");
params.emplace("username", this->interfaceConfig_.username.toStdString());
params.emplace("password", this->interfaceConfig_.password.toStdString());
params.emplace("tenantId", "000000");
params.emplace("grant_type", "password");
auto res = cli.Post("/api/blade-auth/oauth/token", header, params);
if (res)
{
if (res->status == 200)
{
Json::CharReaderBuilder readerBuilder;
std::istringstream iss(res->body);
Json::Value root;
std::string errs;
bool parsingSuccessful = Json::parseFromStream(readerBuilder, iss, &root, &errs);
if (parsingSuccessful)
{
if (root["success"].asBool())
{
this->webToken = root["token_type"].asString();
this->webToken.append(" ");
this->webToken.append(root["access_token"].asString());
this->logInfo("已获取到web token");
return true;
}
else
{
this->logError("获取web token失败原因" + QString::fromStdString(root.asString()));
}
}
else
{
this->logError("获取web token返回数据解析异常返回数据非json。详细" + QString::fromStdString(res->body));
}
}
}
else
{
auto err = res.error();
// if (err == httplib::Error::Connection) {
// std::cout << " (连接出错)" << std::endl;
// }
this->logError("获取web token失败请检查网络或请求地址。详细" + QString::fromStdString(to_string(err)));
}
}
catch (std::exception &e)
{
this->logError("获取授权失败,原因:");
this->logError(e.what());
}
return false;
}
/***************************最小化相关****************************************/
/**
* @brief:
* @param
* @return:
*/
void MainWindow::changeEvent(QEvent *event)
{
if((event->type()==QEvent::WindowStateChange)&&isMinimized())
{
// hide();
m_pSystemTray->setIcon(QIcon("./logo.ico")); // 托盘时显示的图片
m_pSystemTray->setToolTip("Rfid车号识别-股道号:" + QString::number(this->baseConfig_.trackName)); // 鼠标在托盘图片时的提示
m_pSystemTray->showMessage("Rfid车号识别","已隐藏至托盘",QSystemTrayIcon::Information,10000);
event->ignore();
//建立托盘操作的菜单
creat_action();
creat_menu();
m_pSystemTray->show(); // 显示图片图标
}
}
void MainWindow::closeEvent(QCloseEvent *event)
{
// 弹出对话框要求用户输入账号和密码
QString password = QInputDialog::getText(this, "密码验证", "请输入密码:", QLineEdit::Password);
// 验证账号和密码是否正确
if (password == "matrix") {
this->recvLog.close();
this->vecTrain.clear();
this->needIdentify = false;
this->rnameRfidLog();
this->queue_.clear();
event->accept(); // 关闭窗口
exit(0);
} else {
QMessageBox::warning(this, "验证失败", "密码不正确!");
event->ignore(); // 阻止关闭窗口
}
}
void MainWindow::creat_action()
{
m_pActionShow = new QAction("主界面", this);
connect(m_pActionShow, &QAction::triggered, this,&MainWindow::on_ShowMainAction);
m_pActionQuit = new QAction("退出", this);
connect(m_pActionQuit, &QAction::triggered, this, &MainWindow::on_ExitAppAction);
}
void MainWindow::creat_menu()
{
m_pTrayMennu = new QMenu(this);
//新增菜单项---显示主界面
m_pTrayMennu->addAction(m_pActionShow);
//增加分隔符
m_pTrayMennu->addSeparator();
//新增菜单项---退出程序
m_pTrayMennu->addAction(m_pActionQuit);
//把QMenu赋给QSystemTrayIcon对象
m_pSystemTray->setContextMenu(m_pTrayMennu);
}
/**
* @brief:
* @param
* @return:
*/
void MainWindow::on_ShowMainAction()
{
this->show();
}
/**
* @brief:
* @param
* @return:
*/
void MainWindow::on_ExitAppAction()
{
QString password = QInputDialog::getText(this, "关闭账号密码验证", "请输入密码:", QLineEdit::Password);
// 验证账号和密码是否正确
if (password == "123") {
exit(0);
} else {
QMessageBox::warning(this, "验证失败", "账号或密码不正确!");
}
}
void MainWindow::on_activatedSysTrayIcon(QSystemTrayIcon::ActivationReason reason)
{
switch(reason){
case QSystemTrayIcon::Trigger:
//单击托盘图标
this->showNormal();
break;
case QSystemTrayIcon::DoubleClick:
//双击托盘图标
//双击后显示主程序窗口
this->showNormal();
break;
default:
break;
}
}
/********************************串口操作*************************************/
/**
*
*/
void MainWindow::openComClicked()
{
if (ui->openComButton->text() == "打开串口")
{
this->serial_ = this->comDetect_.openCom(ui->comBox->currentText(), ui->BTBox->currentIndex());
if (this->serial_ != nullptr)
{
QString logs;
logs.append("串口打开成功\n");
logs.append("串口号:" + ui->comBox->currentText() + "\n");
logs.append("波特率:" + QString::number(ui->BTBox->currentIndex()) + "\n");
logs.append("磁钢顺序:" + this->baseConfig_.magnetSteelOrder);
this->isOpenCom = true;
this->logInfo(logs);
this->ui->openComButton->setText("关闭串口");
this->ui->LEDlabel->setStyleSheet("border-radius: 16px;\ "
"background-color: rgba(74, 221, 108, 225);\ "
"border:1px solid rgba(168, 168, 168, 105);");
connect(this->serial_,&QSerialPort::readyRead,this,&MainWindow::readCom);
// QTimer* timer = new QTimer(this);
// connect(timer, &QTimer::timeout, this, &MainWindow::readCom);
// timer->start(10);
connect(this->serial_, &QSerialPort::errorOccurred, this, &MainWindow::serialPort_error);
}
else
{
this->isOpenCom = false;
this->logInfo("串口打开失败!");
}
}
else
{
if (this->comDetect_.closeCom())
{
if (this->needIdentify == true) this->needIdentify = false;
this->isOpenCom = false;
this->logInfo("串口已关闭!");
this->recvLog.close();
this->vecTrain.clear();
this->needIdentify = false;
this->rnameRfidLog();
this->queue_.clear();
this->ui->openComButton->setText("打开串口");
this->ui->LEDlabel->setStyleSheet("border-radius: 16px;\ "
"background-color: red;\ "
"border:1px solid rgba(168, 168, 168, 105);");
}
}
}
/**
* 退
* @param error
*/
void MainWindow::serialPort_error(QSerialPort::SerialPortError error)
{
if (error != QSerialPort::NoError)
{
//关闭串口
this->serial_->close();
this->ui->openComButton->setText("打开串口");
this->logError("串口异常退出,疑似接口松动");
this->ui->LEDlabel->setStyleSheet("border-radius: 16px; \ "
"background-color: red;\ "
"border:1px solid rgba(168, 168, 168, 105);");
}
}
void MainWindow::readCom()
{
try {
QByteArray data = this->serial_->readAll(); // 读取数据
if(!data.isEmpty())
{
this->queue_.push(QString(data));
emit getRfid_signals();
}
this->serial_->clear();
data.clear();
} catch(...) {
this->logError("读取串口数据异常!");
}
}
/**
* @brief:queue中获取数据
* @param:
* @return:
*/
void MainWindow::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) != "&")
{
// this->logDebug("--" + this->tmpRfid + " " + this->tmpRfid.right(1));
return;
}
// this->logDebug("--" + this->tmpRfid);
this->tmpRfid = this->tmpRfid.replace("@", "");
QStringList rfidSubList = this->tmpRfid.split("&");
this->tmpRfid = "";
for (const auto & i : rfidSubList)
{
if (i.size() == 26)
{
// 车厢信息
QString strTrainInfo = i.left(14);
if (vecTrain.empty() || !vecTrain.contains(strTrainInfo))
{
vecTrain.append(strTrainInfo);
this->resultTableModel_->setItem(vecTrain.size()-1, 0, new QStandardItem(strTrainInfo));
emit upRfid_signals();
this->logInfo(QString("第%1节 %2").arg(vecTrain.size()).arg(strTrainInfo));
}
}
else if (i.size() == 7)
{
// 磁钢信号
QString strFirst = i.left(1);
QString strOther = i.mid(1, i.size() - 1);
if (strFirst == "0")
{
// 关功放信号
// TODO: 需要结合 “来车状态”广播消息,确认是否真的车离开了 没有真离开,则向串口发送 @on& 避免关功放
this->vecTrain.clear();
this->needIdentify = false;
this->rnameRfidLog();
this->queue_.clear();
this->logInfo("功放关闭");
}
else
{
// TODO: 此处按照4个磁钢来配置可能存在一定限制; 另外逻辑需要优化
if (strOther == "000000" // 第一次检测来车时(前提是上一次功放已关闭)
&& (strFirst == this->baseConfig_.magnetSteelOrder.left(1)
|| strFirst == this->baseConfig_.magnetSteelOrder.mid(1, 1)))
{
this->logInfo("来车了");
this->mkRfidLog();
this->needIdentify = true;
this->resultTableModel_->clear();
this->resultTableModel_ = this->getStandardItemModel();
}
}
}
}
// logInfo(strRfidInfo);
}
void MainWindow::upRfid()
{
if (!this->baseConfig_.upResult) return;
if (!this->needIdentify)
{
QIcon icon("./Fail.ico");
QPixmap m_pic = icon.pixmap(icon.actualSize(QSize(32, 32)));//size自行调整
this->ui->upflagLabel->setPixmap(m_pic);
return;
}else{
QIcon icon("./Succ.ico");
QPixmap m_pic = icon.pixmap(icon.actualSize(QSize(32, 32)));//size自行调整
this->ui->upflagLabel->setPixmap(m_pic);
}
QString info = this->vecTrain.back();
int order = this->vecTrain.size();
info = info.mid(1,info.size() - 1);
bool isCarriage = false;
info.mid(2, 2).toInt(&isCarriage);
if (isCarriage) return;
std::string carriageType = info.left(7).toStdString();
std::string carriageNum = info.mid(7, info.size() - 7).toStdString();
std::string nowTime = this->getSystemTime().toStdString();
std::string strOrder = QString::number(order).toStdString();
if (!this->upWeb(carriageType, carriageNum, strOrder, nowTime))
{
this->logError("识别结果上传失败!");
}
}