Train_RFID/src/qt_source/mainwindow.cpp

712 lines
23 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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->initParam();
delete ui;
}
void MainWindow::logDebug(const QString& message) {
LogDebug << message.toStdString();
ui->textBrowser->append(QString("[Debug] [") + this->getSystemTime() + QString::fromStdString("] ") + message);
}
void MainWindow::logInfo(const QString& message) {
LogInfo << message.toStdString();
ui->textBrowser->append(QString("[Info] [") + this->getSystemTime() + QString::fromStdString("] ") + message);
}
void MainWindow::logWarn(const QString& message) {
LogWarn << message.toStdString();
ui->textBrowser->append(QString("[Warn] [") + this->getSystemTime() + QString::fromStdString("] ") + message);
}
void MainWindow::logError(const QString& message) {
LogError << message.toStdString();
ui->textBrowser->append(QString("[Error] [") + this->getSystemTime() + QString::fromStdString("] ") + message);
}
void MainWindow::initParam() {
this->trainTime = "";
this->recvLog.close();
this->vecTrain.clear();
this->needIdentify = false;
this->rnameRfidLog();
this->queue_.clear();
}
/**
* 创建日志目录
* @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。");
if (!this->getToken()) return false;
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.get("token_type", "").asString().empty())
{
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->initParam();
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->initParam();
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))
{
bool isCarriage = false;
strTrainInfo.mid(3, 2).toInt(&isCarriage);
if (isCarriage) continue;
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->initParam();
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->trainTime = this->getSystemTime();
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);
std::string carriageType = info.left(7).toStdString();
std::string carriageNum = info.mid(7, info.size() - 7).toStdString();
std::string nowTime = this->trainTime.toStdString();
std::string strOrder = QString::number(order).toStdString();
if (!this->upWeb(carriageType, carriageNum, strOrder, nowTime))
{
this->logError("识别结果上传失败!");
}
}