mainwindow.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. #include "mainwindow.h"
  2. #include <QtCore/qlist.h>
  3. #include <QtCore/qobject.h>
  4. #include <QDebug>
  5. #include <QPushButton>
  6. #include <QWidget>
  7. #include "backendapimanager.h"
  8. #include "global.h"
  9. #include "jobs.h"
  10. #include "loadingWidget.h"
  11. #include "logger.h"
  12. #include "mqtt/mqttclient.h"
  13. #include "ui_mainwindow.h"
  14. // 定义 ANzI 转义序列来设置颜色
  15. #define ANSI_COLOR_GREEN "\x1B[32m"
  16. #define ANSI_COLOR_RESET "\x1B[0m"
  17. #include <QMessageBox>
  18. #include <exception>
  19. MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
  20. try {
  21. this->setWindowFlags(Qt::FramelessWindowHint | Qt::Window);
  22. this->setWindowState(Qt::WindowMaximized); // Maximizes the window
  23. this->setStyleSheet("QWidget { font-size: 16px; }");
  24. this->setStyleSheet("QPushButton { font-size: 16px; }");
  25. ui->setupUi(this);
  26. LoadingWidget::init(ui->stackedWidget);
  27. initializeAnimate();
  28. initialMqttService();
  29. pageFactories[ui->btnHome] = new HomepageFactory();
  30. pageFactories[ui->btnNew] = new AddressFactory();
  31. pageFactories[ui->btnBlastProject] = new BlastProjectFactory();
  32. pageFactories[ui->btnEquipment] = new EquipmentFactory();
  33. pageFactories[ui->btnDet] = new DetInfoFactory();
  34. pageFactories[ui->btnBlastOper] = new BlastOperationFactory();
  35. pageFactories[ui->btnRecord] = new BlastRecordFactory();
  36. connect(ui->btnToggle, &QPushButton::clicked, this, &MainWindow::onToggleButtonClicked);
  37. for (auto *widget : left_button_station) {
  38. QPushButton *button = qobject_cast<QPushButton *>(widget);
  39. if (button) {
  40. connect(button, &QPushButton::clicked, this, [this, button] { onButtonClicked(button); });
  41. }
  42. }
  43. initDateTime();
  44. initialBtnSerial();
  45. // initialGPSSerial();
  46. ui->labLat->setText("经度: " + lat);
  47. ui->labLon->setText("维度: " + lon);
  48. connect(ui->btnClose, &QPushButton::clicked, this, &MainWindow::close);
  49. connect(this, &MainWindow::menubarTitleChanged, this, &MainWindow::updateProjectTitleLabel);
  50. } catch (const std::exception &ex) {
  51. Logger::getInstance().error(QString("Application crashed: %1").arg(ex.what()));
  52. QMessageBox::critical(this, "Error", QString("Application crashed: %1").arg(ex.what()));
  53. throw; // rethrow to allow further handling if needed
  54. } catch (...) {
  55. Logger::getInstance().error("Application crashed: Unknown exception");
  56. QMessageBox::critical(this, "Error", "Application crashed: Unknown exception");
  57. throw;
  58. }
  59. this->switchPage(ui->btnHome); // 默认显示首页
  60. }
  61. void MainWindow::updateProjectTitleLabel(const QString &newTitle) { ui->projectTitleLable->setText(newTitle); }
  62. void MainWindow::handleRefreshPage() {
  63. // 刷新页面
  64. ui->stackedWidget->currentWidget()->update();
  65. }
  66. void MainWindow::setMenubarTitle(const QString &newTitle) {
  67. if (m_currentProjectTitle != newTitle) {
  68. m_currentProjectTitle = newTitle;
  69. // Emit the signal to notify listeners (like our QLabel slot)
  70. emit menubarTitleChanged(m_currentProjectTitle);
  71. }
  72. }
  73. void MainWindow::initializeAnimate() {
  74. move(200, 200);
  75. animate_leftFrame = new QPropertyAnimation(ui->leftFrame, "minimumWidth");
  76. animate_leftFrame->setDuration(300);
  77. for (QObject *child : ui->left_buttonsBox->children()) {
  78. if (qobject_cast<QWidget *>(child)) {
  79. left_button_station.append(qobject_cast<QWidget *>(child));
  80. }
  81. }
  82. }
  83. void MainWindow::onToggleButtonClicked() {
  84. // 执行动画
  85. JOBS ::btn_animation(ui->leftFrame, animate_leftFrame);
  86. for (QWidget *b : left_button_station) {
  87. b->setProperty("spread", !b->property("spread").toBool());
  88. b->setStyleSheet(b->styleSheet());
  89. }
  90. }
  91. // 选中按钮
  92. void MainWindow::onButtonClicked(QPushButton *button) {
  93. setStyleSheets(static_cast<QPushButton *>(button));
  94. switchPage(static_cast<QPushButton *>(button));
  95. }
  96. void MainWindow::switchPage(QWidget *button) {
  97. LoadingWidget::showLoading(this, "请稍等");
  98. if (pageFactories.contains(button)) {
  99. PageFactory *factory = pageFactories[button];
  100. if (createdPageByButton.contains(button)) {
  101. QWidget *existingPage = createdPageByButton[button];
  102. existingPage->hide();
  103. ui->stackedWidget->removeWidget(existingPage);
  104. createdPageByButton.remove(button);
  105. }
  106. QWidget *newPage = factory->createPage(this);
  107. ui->stackedWidget->addWidget(newPage);
  108. ui->stackedWidget->setCurrentWidget(newPage);
  109. setMenubarTitle(qobject_cast<QPushButton *>(button)->text());
  110. createdPageByButton.insert(button, newPage);
  111. int pageCount = ui->stackedWidget->count();
  112. }
  113. LoadingWidget::hideLoading();
  114. }
  115. void MainWindow::initialMqttService() {
  116. Logger::getInstance().info("Start init Mqtt server.");
  117. if (mainMqttClient != nullptr) {
  118. QMqttSubscription *projectMsgSubsciber = mainMqttClient->subscribeToTopic(MQTT_TOPIC_COMPANY_PROJECTS_SUB);
  119. if (projectMsgSubsciber != nullptr) {
  120. connect(projectMsgSubsciber, &QMqttSubscription::messageReceived, this,
  121. &MainWindow::handleMqttProjectsMessage);
  122. } else {
  123. Logger::getInstance().error("Failed to subscribe to MQTT topic: " + MQTT_TOPIC_COMPANY_PROJECTS_SUB);
  124. }
  125. } else {
  126. Logger::getInstance().error("mainMqttClient is null, cannot initialize MQTT service");
  127. }
  128. }
  129. void MainWindow::publishBlastProjects() {
  130. // TODO: publish by emit message
  131. // QJsonArray jsonArray;
  132. // HProjectDao dao = HProjectDao(DatabaseManager::getInstance().getDatabase());
  133. // QList<QSharedPointer<HProject>> projectsReg = dao.getAllHProjectsReg();
  134. // for (const auto &projectPtr : projectsReg) {
  135. // if (projectPtr) {
  136. // QByteArray projectJson = projectPtr->ProjectToJson(*projectPtr);
  137. // QJsonDocument doc = QJsonDocument::fromJson(projectJson);
  138. // jsonArray.append(doc.object());
  139. // }
  140. // }
  141. // QJsonDocument jsonDoc(jsonArray);
  142. // QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Indented);
  143. // mainMqttClient->sendMessage(MQTT_TOPIC_COMPANY_PROJECTS_PUBLISH, jsonData, 2, true);
  144. }
  145. void MainWindow::handleMqttProjectsMessage(const QMqttMessage &message) {
  146. Logger::getInstance().info("Received Mqtt message on topic: " + message.topic().name() +
  147. ", message: " + QString::fromUtf8(message.payload()));
  148. QJsonDocument jsonDoc = QJsonDocument::fromJson(message.payload());
  149. if (!jsonDoc.isNull() && jsonDoc.isObject()) {
  150. // 注册工程信息
  151. QJsonObject jsonObj = jsonDoc.object();
  152. if (jsonObj.contains("uuid") && jsonObj.contains("status")) {
  153. QJsonValue uuidValue = jsonObj["uuid"];
  154. QJsonValue statusValue = jsonObj["status"];
  155. if (statusValue.isString() && statusValue.toString() == BlastStatus::Created) { // "1" 未注册
  156. if (uuidValue.isNull()) {
  157. qDebug() << "uuid 的值为 null";
  158. } else {
  159. QString uuid = uuidValue.toString();
  160. HProjectDao dao = HProjectDao(DatabaseManager::getInstance().getDatabase());
  161. dao.updateBlastStatusByUuid(uuid, BlastStatus::Registered);
  162. }
  163. }
  164. } else if (jsonObj.contains("msgType") && jsonObj["msgType"].toString() == "blastSuccess") {
  165. // 爆破成功, 更新状态; 尤其是离线爆破的; 服务器收到爆破记录后发布
  166. HProjectDao dao = HProjectDao(DatabaseManager::getInstance().getDatabase());
  167. for (const QJsonValue &projectUuid : jsonObj["projectUuids"].toArray()) {
  168. qDebug() << "Received offline project blasted UUID: " << projectUuid;
  169. dao.updateBlastStatusByUuid(projectUuid.toString(), BlastStatus::Blasted);
  170. }
  171. publishBlastProjects();
  172. } else if (jsonObj.contains("msgType") && jsonObj["msgType"].toString() == "safeCheck") {
  173. // 处理安全验证消息
  174. if (jsonObj["status"] != "ok") {
  175. QMessageBox::warning(this, "安全验证失败", QString("安全验证未通过,请联系安全员确认"));
  176. return;
  177. }
  178. const QString addressUuid = jsonObj["addressUuid"].toString();
  179. QJsonArray addressList = backendAPIManager::getHAddresses();
  180. QList<QString> subAddressUuids = findAllChildUuids(addressList, addressUuid);
  181. subAddressUuids.append(addressUuid); // 添加当前地址的 UUID
  182. if (subAddressUuids.isEmpty()) {
  183. QMessageBox::information(nullptr, "安全验证失败", QString("未找到子地址,请联系安全员确认"));
  184. return;
  185. }
  186. HProjectDao dao = HProjectDao(DatabaseManager::getInstance().getDatabase());
  187. QList<QSharedPointer<HProject>> pendingCheckProjects = dao.getRegistedProjectByAddressUuid(subAddressUuids);
  188. for (const auto &project : pendingCheckProjects) {
  189. dao.updateBlastStatusByUuid(project->getUuid(), BlastStatus::SafeChecked);
  190. }
  191. if (!pendingCheckProjects.isEmpty()) {
  192. QMessageBox::information(nullptr, "安全员确认信息",
  193. QString("收到工程被安全员确认,请刷新查看爆破作业列表"));
  194. return;
  195. }
  196. }
  197. }
  198. }
  199. void MainWindow::setStyleSheets(QPushButton *selectedButton) {
  200. for (auto *b : left_button_station) {
  201. b->setProperty("selected", b == selectedButton);
  202. b->setStyleSheet(b->styleSheet()); // 刷新显示
  203. }
  204. }
  205. // 处理 MQTT 连接成功的槽函数
  206. void MainWindow::onMqttConnected() {
  207. m_isMqttConnected = true;
  208. Logger::getInstance().info("Mqtt connected.");
  209. }
  210. void MainWindow::initialBtnSerial() {
  211. bool success;
  212. serialTool = SerialTool::getInstance(this, &success);
  213. connect(serialTool, &SerialTool::serialPortOpened, this, &MainWindow::onSerialToolCreated);
  214. serialTool->setupSerialPort();
  215. Logger::getInstance().info("Fire buttons initialized");
  216. }
  217. void MainWindow::onSerialToolCreated() {
  218. m_btnSerialInitialized = true;
  219. serialTool->releaseInstance();
  220. qDebug() << ANSI_COLOR_GREEN << "Serial tool initialized" << ANSI_COLOR_RESET;
  221. Logger::getInstance().info("SerialTool initialized");
  222. }
  223. void MainWindow::initialGPSSerial() {
  224. Logger::getInstance().info("开始初始化GPS");
  225. SerialGPSThread *threadGPS = new SerialGPSThread(this);
  226. connect(threadGPS, &SerialGPSThread::storedGNRMCDataUpdated, this, &MainWindow::handleStoredGNRMCData);
  227. threadGPS->start();
  228. }
  229. // 槽函数,用于接收 RMCData 数据
  230. void MainWindow::handleStoredGNRMCData(const RMCData &data) {
  231. if (data.isValid) {
  232. lat = QString::number(data.latitude);
  233. lon = QString::number(data.longitude);
  234. } else {
  235. lat = "定位失败";
  236. lon = "定位失败";
  237. }
  238. ui->labLat->setText("经度: " + lat);
  239. ui->labLon->setText("纬度: " + lon);
  240. labLat = lat;
  241. labLon = lon;
  242. }
  243. void MainWindow::initDateTime() {
  244. timeThread = new TimeUpdateThread(this);
  245. connect(timeThread, &TimeUpdateThread::timeUpdated, this, &MainWindow::onTimeUpdated);
  246. timeThread->start();
  247. }
  248. void MainWindow::onTimeUpdated(const QString &timeString) { ui->dateTimeShow->setText(timeString); }
  249. MainWindow::~MainWindow() {
  250. timeThread->stop();
  251. delete ui;
  252. }
  253. void MainWindow::mousePressEvent(QMouseEvent *event) {
  254. // if (event->button() == Qt::LeftButton) {
  255. // m_dragPosition = event->globalPos() - frameGeometry().topLeft();
  256. // event->accept();
  257. // }
  258. }
  259. void MainWindow::mouseMoveEvent(QMouseEvent *event) {
  260. // if (event->buttons() & Qt::LeftButton) {
  261. // move(event->globalPos() - m_dragPosition);
  262. // event->accept();
  263. // }
  264. }
  265. QList<QString> MainWindow::findAllChildUuids(const QJsonArray &addressArray, const QString targetUuid) {
  266. QList<QString> childrenUuids;
  267. for (const QJsonValue &value : addressArray) {
  268. if (!value.isObject()) continue;
  269. QJsonObject addressObj = value.toObject();
  270. QString currentUuid = addressObj["uuid"].toString();
  271. if (currentUuid == targetUuid) {
  272. // Found the target address, now collect all children UUIDs recursively
  273. if (addressObj.contains("children") && addressObj["children"].isArray()) {
  274. QJsonArray children = addressObj["children"].toArray();
  275. for (const QJsonValue &child : children) {
  276. if (child.isObject()) {
  277. QString childUuid = child.toObject()["uuid"].toString();
  278. childrenUuids.append(childUuid);
  279. // max depth 3
  280. if (child.toObject().contains("children") && child.toObject()["children"].isArray()) {
  281. QJsonArray grandchildren = child.toObject()["children"].toArray();
  282. for (const QJsonValue &grandchild : grandchildren) {
  283. if (grandchild.isObject()) {
  284. QString grandchildUuid = grandchild.toObject()["uuid"].toString();
  285. childrenUuids.append(grandchildUuid);
  286. }
  287. }
  288. }
  289. }
  290. }
  291. return childrenUuids;
  292. }
  293. } else if (addressObj.contains("children") && addressObj["children"].isArray()) {
  294. // Continue searching in children
  295. QJsonArray children = addressObj["children"].toArray();
  296. QList<QString> result = findAllChildUuids(children, targetUuid);
  297. if (!result.isEmpty()) {
  298. return result;
  299. }
  300. }
  301. }
  302. return childrenUuids;
  303. }