🎩 欢迎来到技术探索的奇幻世界👨💻
📜 个人主页:@一伦明悦-CSDN博客
✍🏻 作者简介:C++软件开发、Python机器学习爱好者
🗣️ 互动与支持:💬评论 👍🏻点赞 📂收藏 👀关注+
如果文章有所帮助,欢迎留下您宝贵的评论!
欢迎点赞加收藏支持我,点击关注,一起进步!
前言
本篇在该篇博客的基础上,继续进行系统功能的完善操作,比如,一般生产国臣各种,跟定希望机器是能自动化的,这样即使工作人员不在,生产依然在进行。
【C++ QT项目实战-02】---- C++ QT系统实现基于QT调用RESTful接口访问JSON文件中数据-CSDN博客文章浏览阅读457次,点赞5次,收藏3次。本篇在做项目的基础上进行了项目中难点的一些总结,我们都知道,在如今大数据时代下,数据对项目系统开发起到决定性的作用,可以帮助企业实现实时决策、提升用户体验、实现业务智能、提高系统性能、提供定制化服务和增强业务竞争力等方面的优势,有利于项目系统的发展和成功。因此,在项目系统开发过程中,需要充分重视数据的快速读取和处理能力,以满足日益增长的数据需求和用户期望。https://blog.csdn.net/m0_59951855/article/details/139065776
项目中的计算等功能已经是自动化模式的,这里的难题就是如何在读取数据的过程中也实现自动化模式,当然,为了便于操作,不仅需要自动化模式,也需要随时可以切换到手动模式。
这里包含了以下几个难点:
(1)主系统线程与计算线程是双线程运行,两种模式下,如何进行线程通信?
(2)自动模式下,系统访问平台的方式,或者说是否需要给定时间,定时访问平台?而不是不停歇的访问。
(3)自动模式下如何实现系统读完数据直接进行计算,手动模式下又怎样切换?这些也需要进行解决。
(4)如何判断平台中是否存在数据,当判断之后,又该怎样运行?
带着这四个问题,将进行详细分析如何实现这些功能,并给出代码解释和功能演示。
正文
01- 双模式下,主线程与计算线程通信实现
主界面上的自动和手动两种模式通过控件进行选择,当选择自动模式时,主线程发送一个信号,通过信号与槽函数的方式与计算线程中的函数进行连接。
在自动模式下,发送信号 send_cmBox_ToWFC,计算线程中使用函数cmBox_switch()和model_switch()来接收信号并实现各种功能。在手动模式下,发送信号 send_modelcmBox_ToWFC,计算线程中使用函数model_switch_0来接收信号。
这种设计模式可以让你在不同模式下灵活地控制计算线程的行为,实现自动化或手动化操作。
mainwindow.h文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
/*#include "Mp_Pred.h"*/
#include"ui_mainwindow.h"
#include <QMainWindow>
#include <QLabel>
#include "spdlog/fmt/ostr.h"
#include "Zanj_WFC_ctrl.h"
#include <QVariant>
#include "wfc_to_main.h"
#include <QtSql/QtSql>
#include <QDateTime>
#include <QTimer>
#include <qtextcodec.h>
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>
#include <QtCharts/QChartView>
#include <QtCharts/QBarSeries>
#include <QtCharts/QBarSet>
#include <QtCharts/QLegend>
#include <QtCharts/QBarCategoryAxis>
#include <QMouseEvent>
//一定要声明!!!
#include"dialog_data_log.h"
namespace Ui {
class MainWindow;
}
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
Zanj_WFC_ctrl * m_Zanj_WFC_ctrl; // 预报计算的类
QThread * m_Zanj_WFC_ctrl_Thread; //m_Mp_Pred将被移动到此线程执行
void connectZanj_WFC_ctrl(Zanj_WFC_ctrl *m_Zanj_WFC_ctrl);
signals:
void send_cmBox_ToWFC();
void send_modelcmBox_ToWFC();
mainwindow.cpp文件
#include <QTimer>
#include <time.h>
#include <QLabel>
#include <QValueAxis>
#include <QMargins>
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>
#include <QtCharts/QChartView>
#include <QtCharts/QBarSeries>
#include <QtCharts/QBarSet>
#include <QtCharts/QLegend>
#include <QtCharts/QBarCategoryAxis>
#include <ctime>
using namespace std::literals;
// using namespace fmt;
using namespace std;
namespace spd = spdlog;
auto console_mainWindow = spd::stdout_color_mt("主界面控制台");
auto rotating_logger_mainWindow = spdlog::rotating_logger_mt("baori_CAO8_Winmain_rotating", "logs/baori_CAO8_rotating.logger", 1048576 * 5, 12);
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
OpenDatabase();
//Matlab_mcl_Init();
setWindowTitle(QString::fromLocal8Bit("主系统")); // 此处写应用程序在标题栏上显示的名称
setStyleSheet("background - color:pink; ");
reshow();
data_log_tblview();
connect(this, &MainWindow::send_cmBox_ToWFC, m_Zanj_WFC_ctrl, &Zanj_WFC_ctrl::cmBox_switch);
connect(this, &MainWindow::send_cmBox_ToWFC, m_Zanj_WFC_ctrl, &Zanj_WFC_ctrl::model_switch);
connect(this, &MainWindow::send_modelcmBox_ToWFC, m_Zanj_WFC_ctrl, &Zanj_WFC_ctrl::model_switch_0);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::deploy_cmBox_switch()
{
if (ui->deploy_cmBox->currentText() == QString::fromLocal8Bit("自动模式"))
{
ui->Iplate4j_IP_QLE->setText("127.0.0.1");
ui->model_PC_IP_QLE->setText("127.0.0.1");
emit send_cmBox_ToWFC();
}
else if (ui->deploy_cmBox->currentText() == QString::fromLocal8Bit("手动模式"))
{
emit send_modelcmBox_ToWFC();
}
}
02-cmBox_switch()函数实现
这里接受信号的计算线程的函数分为两个,第一个cmBox_switch()函数是实现打开配置文件和定时访问数据,定时访问数据这里加入死循环,设置30s的时间间隔。
startSystem()函数的这段代码是一个无限循环,在每次循环中执行以下操作:
调用
OnGETSlot()
函数:这个函数用于执行数据读取和预测的操作。具体实现可能涉及从网络或文件系统中读取数据,并对数据进行处理和预测。调用
this_thread::sleep_for(chrono::seconds(30))
:这行代码会使当前线程暂停执行,模拟定时器或轮询间隔。在这个例子中,线程会休眠30秒钟,然后再次执行循环中的操作。整体来说,这段代码实现了一个简单的定时器功能,每隔30秒钟执行一次数据读取和预测的操作,然后再次休眠30秒钟,循环执行这个过程
计算线程.cpp文件
void Zanj_WFC_ctrl::cmBox_switch()
{
configuration_read();
startSystem();
}
bool Zanj_WFC_ctrl::configuration_read()
{
string err;
QFile config_ini("config.ini");
//QFile config_ini("config_baori.ini"); 打开baori配置文件,如果需要访问宝日大数据平台,就需要打开baori配置文件
if (!config_ini.open(QIODevice::ReadOnly | QIODevice::Text))
{
console_WFC_thread->critical("配置文件config.ini不能打开!");
rlogger_WFC_thread->critical("配置文件config.ini不能打开!");
// std::cout << "configuration.ini Open failed." << endl;
return false;
}
console_WFC_thread->info("配置文件config.ini已打开! ");
rlogger_WFC_thread->info("配置文件config.ini已打开! ");
QTextStream config_txtInput(&config_ini);
QString config_lineStr;
config_lineStr = config_txtInput.readLine();
config_lineStr = config_txtInput.readLine();
clientID = config_lineStr.mid(9);
config_lineStr = config_txtInput.readLine();
clientSecret = config_lineStr.mid(13);
config_lineStr = config_txtInput.readLine();
serverIP = config_lineStr.mid(9);
config_lineStr = config_txtInput.readLine();
serviceID_get = config_lineStr.mid(14);
config_lineStr = config_txtInput.readLine();
serviceID_post = config_lineStr.mid(15);
config_lineStr = config_txtInput.readLine();
name = config_lineStr.mid(5);
config_lineStr = config_txtInput.readLine();
unit = config_lineStr.mid(5);
config_lineStr = config_txtInput.readLine();
user_ID = config_lineStr.mid(8);
config_lineStr = config_txtInput.readLine();
companyCode = config_lineStr.mid(12);
config_lineStr = config_txtInput.readLine();
mssql_serverName = config_lineStr.mid(17);
config_lineStr = config_txtInput.readLine();
mssql_dbName = config_lineStr.mid(13);
config_lineStr = config_txtInput.readLine();
mssql_username = config_lineStr.mid(15);
config_lineStr = config_txtInput.readLine();
mssql_pwd = config_lineStr.mid(10);
config_lineStr = config_txtInput.readLine();
mssql_datasource = config_lineStr.mid(17);
return true;
}
void Zanj_WFC_ctrl::startSystem() {
while (true) {
// 每隔一定时间执行一次数据读取和预测
OnGETSlot();
//Temp_KNNval_Auto_Cal();
// 休眠一段时间,模拟定时器或轮询间隔
this_thread::sleep_for(chrono::seconds(30)); // 30秒钟间隔
}
}
03-model_switch()函数实现
实现另一个函数之前,先定义了一个全局变量,用于设置工作模式,这里设置的是,当变量为0,为手动模式,变量为1,为自动模式,在读取数据函数那里,加入if判断语句,如果变量为1,则直接进行计算,否则,需要手动计算
如果变量为1,直接调用该函数Temp_KNNval_Auto_Cal()进行计算,如果为0,则手动操作进行计算。
计算线程.cpp文件
void Zanj_WFC_ctrl::json_doc_get_parse()
{
//总的思路是:对象.value(),如果取到string、int、double类型 就直接.toString()等输出;
//如果取到object或array类型 ,就通过toObject()或toArray() 转到对象或数组类型,接着取值。。循环
if (json_doc_get.isNull())
{
console_WFC_thread->critical("unable to parse! ");
rlogger_WFC_thread->critical("unable to parse! ");
}
else
{
console_WFC_thread->info("parse start! ");
rlogger_WFC_thread->info("parse start! ");
}
QJsonObject::Iterator it;
for (it = doc_get_Obj.begin(); it != doc_get_Obj.end(); ++it)
{
HdglPredict tempData;
QString aentid, atentid, atime, acode, agrade, asource;
float ahthick, athick, awidth, CT_TMP, FT_TMP, DSH, SPEED, TPM;
float C, Si, Mn, P, S, Cu, Ni, Cr, Mo, Nb, Ti, B, N, Al;
int st_product_no;
QString zoneKey = it.key();
QJsonObject zoneObj = it.value().toObject();
// 检查钢卷是否已经读取过,如果已经读取过,则跳过
if (readCoils.contains(zoneKey))
{
console_WFC_thread->warn("文件里没有其他钢卷! ");
continue;
}
// 将已读取的钢卷添加到集合中
readCoils.insert(zoneKey);
aentid = zoneObj["aentid"].toString();
atentid = zoneObj["atentid"].toString();
atime = zoneObj["atime"].toString();
acode = zoneObj["acode"].toString();
agrade = zoneObj["agrade"].toString();
asource = zoneObj["asource"].toString();
tempData.ZoneKey = zoneKey;
tempData.ENTID = aentid;
tempData.AENTID = atentid;
tempData.ATIME = atime;
tempData.ST_code = acode;
tempData.ST_grade = agrade;
tempData.ST_source = asource;
tempData.CGL_SPEED_INPUT = SPEED;
tempData.SPM_ELONG_INPUT = TPM;
tempData.st_product_no = st_product_no_determin();
// 将结构体对象添加到容器中
hdglPredictList.append(tempData);
strt_hdgl_predict.ENTID = aentid;
strt_hdgl_predict.AENTID = atentid;
strt_hdgl_predict.ATIME = atime;
strt_hdgl_predict.ST_code = acode;
strt_hdgl_predict.ST_grade = agrade;
strt_hdgl_predict.ST_source = asource;
strt_hdgl_predict.CGL_SPEED_INPUT = SPEED;
strt_hdgl_predict.SPM_ELONG_INPUT = TPM;
if (model_switch_num == 1)
{
Temp_KNNval_Auto_Cal();
}
else
{
continue;
}
}
}
void Zanj_WFC_ctrl::model_switch() {
model_switch_num = 1;
}
void Zanj_WFC_ctrl::model_switch_0() {
model_switch_num = 0;
}
04-数据存在判断操作实现
最后实现系统对平台是否存在新的数据的判断,定义了一个字符串变量readCoils,用于存储已经读取的钢卷,对于已经读取过的钢卷,直接跳过,不进行计算,若是没有钢卷存在,系统也会发出警告。通过下面这部分代码判断即可
if (readCoils.contains(zoneKey))
{
console_WFC_thread->warn("文件里没有其他钢卷! ");
continue;
}// 将已读取的钢卷添加到集合中
readCoils.insert(zoneKey);
void Zanj_WFC_ctrl::json_doc_get_parse()
{
//总的思路是:对象.value(),如果取到string、int、double类型 就直接.toString()等输出;
//如果取到object或array类型 ,就通过toObject()或toArray() 转到对象或数组类型,接着取值。。循环
if (json_doc_get.isNull())
{
console_WFC_thread->critical("unable to parse! ");
rlogger_WFC_thread->critical("unable to parse! ");
}
else
{
console_WFC_thread->info("parse start! ");
rlogger_WFC_thread->info("parse start! ");
}
QJsonObject::Iterator it;
for (it = doc_get_Obj.begin(); it != doc_get_Obj.end(); ++it)
{
HdglPredict tempData;
QString aentid, atentid, atime, acode, agrade, asource;
float ahthick, athick, awidth, CT_TMP, FT_TMP, DSH, SPEED, TPM;
float C, Si, Mn, P, S, Cu, Ni, Cr, Mo, Nb, Ti, B, N, Al;
int st_product_no;
QString zoneKey = it.key();
QJsonObject zoneObj = it.value().toObject();
// 检查钢卷是否已经读取过,如果已经读取过,则跳过
if (readCoils.contains(zoneKey))
{
console_WFC_thread->warn("文件里没有其他钢卷! ");
continue;
}
// 将已读取的钢卷添加到集合中
readCoils.insert(zoneKey);
aentid = zoneObj["aentid"].toString();
atentid = zoneObj["atentid"].toString();
atime = zoneObj["atime"].toString();
acode = zoneObj["acode"].toString();
agrade = zoneObj["agrade"].toString();
asource = zoneObj["asource"].toString();
tempData.ZoneKey = zoneKey;
tempData.ENTID = aentid;
tempData.AENTID = atentid;
tempData.ATIME = atime;
tempData.ST_code = acode;
tempData.ST_grade = agrade;
tempData.ST_source = asource;
tempData.CGL_SPEED_INPUT = SPEED;
tempData.SPM_ELONG_INPUT = TPM;
tempData.st_product_no = st_product_no_determin();
// 将结构体对象添加到容器中
hdglPredictList.append(tempData);
strt_hdgl_predict.ENTID = aentid;
strt_hdgl_predict.AENTID = atentid;
strt_hdgl_predict.ATIME = atime;
strt_hdgl_predict.ST_code = acode;
strt_hdgl_predict.ST_grade = agrade;
strt_hdgl_predict.ST_source = asource;
strt_hdgl_predict.CGL_SPEED_INPUT = SPEED;
strt_hdgl_predict.SPM_ELONG_INPUT = TPM;
if (model_switch_num == 1)
{
Temp_KNNval_Auto_Cal();
}
else
{
continue;
}
}
}
05-功能演示
下面对上述介绍的功能进行演示:
如下图所示,为系统登录界面,登录之后,才可以对各种功能进行操作。
主界面如下图所示:仅展示部分主界面,分为自动模式和手动模式
选择自动模式之后,就可以持续进行计算,不断进行访问数据文件
当再次访问,发现数据文件中没有数据之后,便会发出警告,如下图所示。
总结
在C++ QT系统中实现自动化读取JSON数据文件的过程有几个好处:
提高效率:自动化读取JSON数据文件可以省去手动操作的时间和精力,提高系统运行效率。特别是在需要定期更新或获取数据时,自动化读取可以保证数据及时可用,减少了人为干预的需要。
减少错误:手动处理JSON数据文件容易出现错误,例如文件路径错误、解析错误等。自动化读取通过编程实现,可以减少人为错误的发生,提高系统的稳定性和可靠性。
增强灵活性:自动化读取JSON数据文件的实现可以根据需求进行定制化,例如可以设置读取频率、读取条件等,从而增强了系统的灵活性和可配置性。
方便维护:通过自动化读取,可以将数据获取的逻辑封装在函数或模块中,便于维护和修改。当需求变化时,只需要修改相应的代码逻辑,而不必修改大量的手动操作步骤。
综上所述,自动化读取JSON数据文件可以提高系统效率、减少错误、增强灵活性,并方便系统的维护和升级。