🎩 欢迎来到技术探索的奇幻世界👨💻
📜 个人主页:@一伦明悦-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数据文件可以提高系统效率、减少错误、增强灵活性,并方便系统的维护和升级。