File indexing completed on 2024-10-13 10:08:20

0001 /* AtCore Test Client
0002  * SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0003  * SPDX-FileCopyrightText: 2016-2018 Patrick José Pereira <patrickjp@kde.org>
0004  * SPDX-FileCopyrightText: 2016-2017, 2019 Lays Rodrigues <lays.rodrigues@kde.org>
0005  * SPDX-FileCopyrightText: 2016-2020 Chris Rizzitello <rizzitello@kde.org>
0006  * SPDX-FileCopyrightText: 2016 Tomaz Canabrava <tcanabrava@kde.org>
0007  */
0008 
0009 #include <QFileDialog>
0010 #include <QLoggingCategory>
0011 #include <QMessageBox>
0012 #include <QSerialPortInfo>
0013 #include <QTextStream>
0014 #include <QTimer>
0015 
0016 #include "about.h"
0017 #include "gcodecommands.h"
0018 #include "machineinfo.h"
0019 #include "mainwindow.h"
0020 #include "seriallayer.h"
0021 
0022 Q_LOGGING_CATEGORY(TESTCLIENT_MAINWINDOW, "org.kde.atelier.core")
0023 
0024 int MainWindow::fanCount = 4;
0025 
0026 MainWindow::MainWindow(QWidget *parent)
0027     : QMainWindow(parent)
0028     , core(new AtCore(this))
0029 {
0030     setWindowTitle(tr("AtCore - Test Client"));
0031     setWindowIcon(QIcon(QStringLiteral(":/icon/windowIcon")));
0032     QCoreApplication::setApplicationVersion(core->version());
0033     initMenu();
0034     initStatusBar();
0035     initWidgets();
0036 
0037     connect(core, &AtCore::atcoreMessage, logWidget, &LogWidget::appendLog);
0038     logWidget->appendLog(tr("Attempting to locate Serial Ports"));
0039     core->setSerialTimerInterval(1000);
0040 
0041     connect(core, &AtCore::stateChanged, this, &MainWindow::printerStateChanged);
0042     connect(core, &AtCore::portsChanged, this, &MainWindow::locateSerialPort);
0043     connect(core, &AtCore::sdCardFileListChanged, sdWidget, &SdWidget::updateFilelist);
0044     connect(core, &AtCore::autoTemperatureReportChanged, this, &MainWindow::updateAutoTemperatureReport);
0045 
0046     comboPort->setFocus(Qt::OtherFocusReason);
0047 
0048     if (comboProfile->count() == 0) {
0049         QMessageBox::information(this, tr("AtCore First Run"), tr("No Profiles Detected, use the Profile Manager to create one."));
0050         profileDock->setVisible(true);
0051         move(profileDock->geometry().center());
0052         profileDock->move(geometry().center());
0053         profileDock->activateWindow();
0054     }
0055 }
0056 
0057 void MainWindow::initMenu()
0058 {
0059     QMenu *menuFile = new QMenu(tr("File"));
0060     QAction *actionQuit = new QAction(style()->standardIcon(QStyle::SP_DialogCloseButton), tr("Quit"));
0061     connect(actionQuit, &QAction::triggered, this, &MainWindow::close);
0062     menuFile->addAction(actionQuit);
0063 
0064     menuView = new QMenu(tr("View"));
0065     QAction *actionShowDockTitles = new QAction(tr("Show Dock Titles"));
0066     actionShowDockTitles->setCheckable(true);
0067     actionShowDockTitles->setChecked(true);
0068     connect(actionShowDockTitles, &QAction::toggled, this, &MainWindow::toggleDockTitles);
0069     menuView->addAction(actionShowDockTitles);
0070 
0071     QMenu *menuHelp = new QMenu(tr("Help"));
0072     QAction *actionAbout = new QAction(tr("About"));
0073     actionAbout->setShortcut(QKeySequence(Qt::Key_F1));
0074     connect(actionAbout, &QAction::triggered, this, [] {
0075         auto *dialog = new About;
0076         dialog->exec();
0077     });
0078     menuHelp->addAction(actionAbout);
0079 
0080     menuBar()->addMenu(menuFile);
0081     menuBar()->addMenu(menuView);
0082     menuBar()->addMenu(menuHelp);
0083 }
0084 
0085 void MainWindow::initStatusBar()
0086 {
0087     statusWidget = new StatusWidget;
0088     connect(statusWidget, &StatusWidget::stopPressed, core, &AtCore::stop);
0089     connect(core, &AtCore::printProgressChanged, statusWidget, &StatusWidget::updatePrintProgress);
0090     connect(core, &AtCore::sdMountChanged, statusWidget, &StatusWidget::setSD);
0091     statusBar()->addPermanentWidget(statusWidget, 100);
0092 }
0093 
0094 void MainWindow::initWidgets()
0095 {
0096     // Make the Docks
0097     makeCommandDock();
0098     makePrintDock();
0099     makeTempTimelineDock();
0100     makeLogDock();
0101     makeConnectDock();
0102     makeMoveDock();
0103     makeTempControlsDock();
0104     makeSdDock();
0105     makeProfileDock();
0106 
0107     setDangeriousDocksDisabled(true);
0108 
0109     setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::North);
0110     setTabPosition(Qt::RightDockWidgetArea, QTabWidget::North);
0111     tabifyDockWidget(moveDock, tempControlsDock);
0112     tabifyDockWidget(moveDock, sdDock);
0113     moveDock->raise();
0114 
0115     tabifyDockWidget(connectDock, printDock);
0116     tabifyDockWidget(connectDock, commandDock);
0117     connectDock->raise();
0118 
0119     tabifyDockWidget(logDock, profileDock);
0120     logDock->raise();
0121     setCentralWidget(nullptr);
0122 
0123     // More Gui stuff
0124     // hide the printing progress bar.
0125     statusWidget->showPrintArea(false);
0126 }
0127 
0128 void MainWindow::makeCommandDock()
0129 {
0130     commandWidget = new CommandWidget;
0131     // Connect the commandPressed signal
0132     connect(commandWidget, &CommandWidget::commandPressed, core, &AtCore::pushCommand);
0133     // Connect the messagePressed signal
0134     connect(commandWidget, &CommandWidget::messagePressed, core, &AtCore::showMessage);
0135     // Create the dock, and set the Widget.
0136     commandDock = new QDockWidget(tr("Commands"), this);
0137     commandDock->setWidget(commandWidget);
0138 
0139     // Push the toggle view action into our view menu
0140     menuView->insertAction(nullptr, commandDock->toggleViewAction());
0141 
0142     // Place the Dock into a DockWidget Area.
0143     // Failure todo this will create some odd side effects at runtime
0144     addDockWidget(Qt::LeftDockWidgetArea, commandDock);
0145 }
0146 
0147 void MainWindow::makePrintDock()
0148 {
0149     printWidget = new PrintWidget;
0150     connect(printWidget, &PrintWidget::printPressed, this, &MainWindow::printPBClicked);
0151     connect(printWidget, &PrintWidget::emergencyStopPressed, core, &AtCore::emergencyStop);
0152     connect(printWidget, &PrintWidget::fanSpeedChanged, core, &AtCore::setFanSpeed);
0153     connect(printWidget, &PrintWidget::printSpeedChanged, core, &AtCore::setPrinterSpeed);
0154     connect(printWidget, &PrintWidget::flowRateChanged, core, &AtCore::setFlowRate);
0155 
0156     printDock = new QDockWidget(tr("Print"), this);
0157     printDock->setWidget(printWidget);
0158 
0159     menuView->insertAction(nullptr, printDock->toggleViewAction());
0160     addDockWidget(Qt::LeftDockWidgetArea, printDock);
0161 }
0162 
0163 void MainWindow::makeTempTimelineDock()
0164 {
0165     plotWidget = new PlotWidget;
0166     // make and connect our plots in the widget.
0167     plotWidget->addPlot(tr("Actual Bed"));
0168     connect(core->temperature(), &Temperature::bedTemperatureChanged, this, [this] {
0169         float temp = core->temperature()->bedTemperature();
0170         checkTemperature(0x00, 0, temp);
0171         plotWidget->appendPoint(tr("Actual Bed"), temp);
0172     });
0173 
0174     plotWidget->addPlot(tr("Target Bed"));
0175     connect(core->temperature(), &Temperature::bedTargetTemperatureChanged, this, [this] {
0176         float temp = core->temperature()->bedTargetTemperature();
0177         checkTemperature(0x01, 0, temp);
0178         plotWidget->appendPoint(tr("Target Bed"), temp);
0179     });
0180 
0181     plotWidget->addPlot(tr("Actual Ext.1"));
0182     connect(core->temperature(), &Temperature::extruderTemperatureChanged, this, [this] {
0183         float temp = core->temperature()->extruderTemperature();
0184         checkTemperature(0x02, 0, temp);
0185         plotWidget->appendPoint(tr("Actual Ext.1"), temp);
0186     });
0187 
0188     plotWidget->addPlot(tr("Target Ext.1"));
0189     connect(core->temperature(), &Temperature::extruderTargetTemperatureChanged, this, [this] {
0190         float temp = core->temperature()->extruderTargetTemperature();
0191         checkTemperature(0x03, 0, temp);
0192         plotWidget->appendPoint(tr("Target Ext.1"), temp);
0193     });
0194 
0195     auto timerLayout = new QHBoxLayout;
0196     auto lblTimer = new QLabel(tr("Seconds Between Temperature Checks"), this);
0197     sbTemperatureTimer = new QSpinBox(this);
0198     sbTemperatureTimer->setRange(0, 90);
0199     connect(sbTemperatureTimer, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int value) { core->setTemperatureTimerInterval(value * 1000); });
0200     connect(core, &AtCore::temperatureTimerIntervalChanged, this, [this](int value) {
0201         if (value != sbTemperatureTimer->value()) {
0202             sbTemperatureTimer->blockSignals(true);
0203             sbTemperatureTimer->setValue(value / 1000);
0204             sbTemperatureTimer->blockSignals(false);
0205         }
0206     });
0207 
0208     timerLayout->addWidget(lblTimer);
0209     timerLayout->addWidget(sbTemperatureTimer);
0210 
0211     auto tempDockLayout = new QVBoxLayout;
0212     tempDockLayout->addWidget(plotWidget);
0213     tempDockLayout->addLayout(timerLayout);
0214 
0215     auto tempDockMainWidget = new QWidget(this);
0216     tempDockMainWidget->setLayout(tempDockLayout);
0217 
0218     tempTimelineDock = new QDockWidget(tr("Temperature Timeline"), this);
0219     tempTimelineDock->setWidget(tempDockMainWidget);
0220     menuView->insertAction(nullptr, tempTimelineDock->toggleViewAction());
0221     addDockWidget(Qt::RightDockWidgetArea, tempTimelineDock);
0222 }
0223 
0224 void MainWindow::makeLogDock()
0225 {
0226     logWidget = new LogWidget(new QTemporaryFile(QDir::tempPath() + QStringLiteral("/AtCore_")));
0227     logDock = new QDockWidget(tr("Session Log"), this);
0228     logDock->setWidget(logWidget);
0229 
0230     menuView->insertAction(nullptr, logDock->toggleViewAction());
0231     addDockWidget(Qt::RightDockWidgetArea, logDock);
0232 }
0233 
0234 void MainWindow::makeConnectDock()
0235 {
0236     auto *mainLayout = new QVBoxLayout;
0237     auto *newLabel = new QLabel(tr("Port:"));
0238 
0239     comboPort = new QComboBox;
0240     comboPort->setEditable(true);
0241 
0242     auto *hBoxLayout = new QHBoxLayout;
0243     hBoxLayout->addWidget(newLabel);
0244     hBoxLayout->addWidget(comboPort, 75);
0245     mainLayout->addLayout(hBoxLayout);
0246 
0247     comboProfile = new QComboBox();
0248     comboProfile->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
0249     comboProfile->addItems(MachineInfo::instance()->profileNames());
0250 
0251     connect(MachineInfo::instance(), &MachineInfo::profilesChanged, this, [this] {
0252         int index = comboProfile->currentIndex();
0253         comboProfile->clear();
0254         comboProfile->addItems(MachineInfo::instance()->profileNames());
0255         comboProfile->setCurrentIndex(std::min<int>(index, comboProfile->count() - 1));
0256     });
0257 
0258     newLabel = new QLabel(tr("Profile:"));
0259     auto profileLayout = new QHBoxLayout();
0260     profileLayout->addWidget(newLabel);
0261     profileLayout->addWidget(comboProfile);
0262     mainLayout->addLayout(profileLayout);
0263 
0264     cbReset = new QCheckBox(tr("Attempt to stop Reset on connect"));
0265     if (MachineInfo::instance()->profileNames().isEmpty()) {
0266         cbReset->setHidden(true);
0267     } else {
0268         cbReset->setHidden(MachineInfo::instance()->readKey(comboProfile->currentText(), MachineInfo::KEY::FIRMWARE).toString().contains(QStringLiteral("Auto-Detect")));
0269     }
0270     mainLayout->addWidget(cbReset);
0271 
0272     connect(comboProfile, &QComboBox::currentTextChanged, this, [this](const QString &currentText) {
0273         cbReset->setHidden(MachineInfo::instance()->readKey(currentText, MachineInfo::KEY::FIRMWARE).toString().contains(QStringLiteral("Auto-Detect")));
0274     });
0275 
0276     buttonConnect = new QPushButton(tr("Connect"));
0277     connect(buttonConnect, &QPushButton::clicked, this, &MainWindow::connectPBClicked);
0278 
0279     connectionTimer = new QTimer(this);
0280     connectionTimer->setInterval(20000);
0281     connectionTimer->setSingleShot(true);
0282     connect(connectionTimer, &QTimer::timeout, this, [this] {
0283         Q_EMIT buttonConnect->clicked();
0284         QMessageBox::critical(this, tr("Connection Error"), tr("Your machine did not respond after 20 seconds.\n\nBefore connecting again check that your printer is on and your are connecting using the correct BAUD Rate for your device."));
0285     });
0286 
0287     mainLayout->addWidget(buttonConnect);
0288 
0289     auto *dockContents = new QWidget;
0290     dockContents->setLayout(mainLayout);
0291 
0292     connectDock = new QDockWidget(tr("Connect"), this);
0293     connectDock->setWidget(dockContents);
0294 
0295     menuView->insertAction(nullptr, connectDock->toggleViewAction());
0296     addDockWidget(Qt::LeftDockWidgetArea, connectDock);
0297 }
0298 
0299 void MainWindow::makeMoveDock()
0300 {
0301     movementWidget = new MovementWidget(this);
0302 
0303     connect(movementWidget, &MovementWidget::homeAllPressed, this, [this] {
0304         logWidget->appendLog(tr("Home All"));
0305         core->home();
0306     });
0307 
0308     connect(movementWidget, &MovementWidget::homeXPressed, this, [this] {
0309         logWidget->appendLog(tr("Home X"));
0310         core->home(AtCore::X);
0311     });
0312 
0313     connect(movementWidget, &MovementWidget::homeYPressed, this, [this] {
0314         logWidget->appendLog(tr("Home Y"));
0315         core->home(AtCore::Y);
0316     });
0317 
0318     connect(movementWidget, &MovementWidget::homeZPressed, this, [this] {
0319         logWidget->appendLog(tr("Home Z"));
0320         core->home(AtCore::Z);
0321     });
0322 
0323     connect(movementWidget, &MovementWidget::absoluteMove, this, [this](const QLatin1Char &axis, const double &value) {
0324         logWidget->appendLog(GCode::description(GCode::G1));
0325         core->move(axis, value);
0326     });
0327 
0328     connect(movementWidget, &MovementWidget::disableMotorsPressed, this, [this] { core->disableMotors(0); });
0329 
0330     connect(movementWidget, &MovementWidget::relativeMove, this, [this](const QLatin1Char &axis, const double &value) {
0331         core->setRelativePosition();
0332         core->move(axis, value);
0333         core->setAbsolutePosition();
0334     });
0335 
0336     connect(movementWidget, &MovementWidget::unitsChanged, this, [this](int units) {
0337         auto selection = static_cast<AtCore::UNITS>(units);
0338         core->setUnits(selection);
0339     });
0340 
0341     moveDock = new QDockWidget(tr("Movement"), this);
0342     moveDock->setWidget(movementWidget);
0343 
0344     menuView->insertAction(nullptr, moveDock->toggleViewAction());
0345     addDockWidget(Qt::LeftDockWidgetArea, moveDock);
0346 }
0347 
0348 void MainWindow::makeTempControlsDock()
0349 {
0350     temperatureWidget = new TemperatureWidget;
0351     connect(temperatureWidget, &TemperatureWidget::bedTempChanged, core, &AtCore::setBedTemp);
0352     connect(temperatureWidget, &TemperatureWidget::extTempChanged, core, &AtCore::setExtruderTemp);
0353 
0354     tempControlsDock = new QDockWidget(tr("Temperatures"), this);
0355     tempControlsDock->setWidget(temperatureWidget);
0356     menuView->insertAction(nullptr, tempControlsDock->toggleViewAction());
0357     addDockWidget(Qt::LeftDockWidgetArea, tempControlsDock);
0358 }
0359 
0360 void MainWindow::makeSdDock()
0361 {
0362     sdWidget = new SdWidget;
0363     connect(sdWidget, &SdWidget::requestSdList, core, &AtCore::sdFileList);
0364 
0365     connect(sdWidget, &SdWidget::printSdFile, this, [this](const QString &fileName) {
0366         if (fileName.isEmpty()) {
0367             QMessageBox::information(this, tr("Print Error"), tr("You must Select a file from the list"));
0368         } else {
0369             core->print(fileName, true);
0370         }
0371     });
0372 
0373     connect(sdWidget, &SdWidget::deleteSdFile, this, [this](const QString &fileName) {
0374         if (fileName.isEmpty()) {
0375             QMessageBox::information(this, tr("Delete Error"), tr("You must Select a file from the list"));
0376         } else {
0377             core->sdDelete(fileName);
0378         }
0379     });
0380 
0381     sdDock = new QDockWidget(tr("Sd Card"), this);
0382     sdDock->setWidget(sdWidget);
0383     menuView->insertAction(nullptr, sdDock->toggleViewAction());
0384     addDockWidget(Qt::LeftDockWidgetArea, sdDock);
0385 }
0386 
0387 void MainWindow::makeProfileDock()
0388 {
0389     profileManager = new ProfileManager();
0390     profileDock = new QDockWidget(tr("Profile Manager"), this);
0391     profileDock->setWidget(profileManager);
0392     menuView->insertAction(nullptr, profileDock->toggleViewAction());
0393     addDockWidget(Qt::RightDockWidgetArea, profileDock);
0394     profileDock->setFloating(true);
0395     profileDock->setVisible(false);
0396 }
0397 
0398 void MainWindow::closeEvent(QCloseEvent *event)
0399 {
0400     core->close();
0401     event->accept();
0402 }
0403 
0404 void MainWindow::checkTemperature(uint sensorType, uint number, float temp)
0405 {
0406     QString msg;
0407     switch (sensorType) {
0408     case 0x00: // bed
0409         msg = QString::fromLatin1("Bed Temperature");
0410         break;
0411 
0412     case 0x01: // bed target
0413         msg = QString::fromLatin1("Bed Target Temperature");
0414         break;
0415 
0416     case 0x02: // extruder
0417         msg = QString::fromLatin1("Extruder[%1] Temperature").arg(QString::number(number));
0418         break;
0419 
0420     case 0x03: // extruder target
0421         msg = QString::fromLatin1("Extruder[%1] Target Temperature").arg(QString::number(number));
0422         break;
0423 
0424     case 0x04: // enclosure
0425         msg = QString::fromLatin1("Enclosure Temperature");
0426         break;
0427 
0428     case 0x05: // enclosure target
0429         msg = QString::fromLatin1("Enclosure Target Temperature");
0430         break;
0431     }
0432 
0433     msg.append(QString::fromLatin1(": %1").arg(QString::number(double(temp), 'f', 2)));
0434     logWidget->appendLog(msg);
0435 }
0436 /**
0437  * @brief MainWindow::locateSerialPort
0438  * Locate all active serial ports on the computer and add to the list
0439  * of serial ports
0440  */
0441 void MainWindow::locateSerialPort(const QStringList &ports)
0442 {
0443     comboPort->clear();
0444     if (!ports.isEmpty()) {
0445         comboPort->addItems(ports);
0446         logWidget->appendLog(tr("Found %1 Ports").arg(QString::number(ports.count())));
0447     } else {
0448         QString portError(tr("No available ports! Please connect a serial device to continue!"));
0449         if (!logWidget->endsWith(portError)) {
0450             logWidget->appendLog(portError);
0451         }
0452     }
0453 }
0454 
0455 void MainWindow::connectPBClicked()
0456 {
0457     if (core->state() == AtCore::DISCONNECTED) {
0458         int baud = MachineInfo::instance()->readKey(comboProfile->currentText(), MachineInfo::KEY::BAUDRATE).toInt();
0459         int xMax = MachineInfo::instance()->readKey(comboProfile->currentText(), MachineInfo::KEY::XMAX).toInt();
0460         int yMax = MachineInfo::instance()->readKey(comboProfile->currentText(), MachineInfo::KEY::YMAX).toInt();
0461         int zMax = MachineInfo::instance()->readKey(comboProfile->currentText(), MachineInfo::KEY::ZMAX).toInt();
0462         QString plugin = MachineInfo::instance()->readKey(comboProfile->currentText(), MachineInfo::KEY::FIRMWARE).toString();
0463         if (core->newConnection(comboPort->currentText(), baud, plugin, cbReset->isChecked())) {
0464             connect(core, &AtCore::receivedMessage, logWidget, &LogWidget::appendRLog);
0465             connect(core, &AtCore::pushedCommand, logWidget, &LogWidget::appendSLog);
0466             logWidget->appendLog(tr("Serial connected"));
0467             movementWidget->setAxisMax(xMax, yMax, zMax);
0468             if ((!plugin.contains(QStringLiteral("Auto-Detect"))) && cbReset->isChecked()) {
0469                 // Wait a few seconds after connect to avoid the normal errors
0470                 QTimer::singleShot(5000, core, &AtCore::sdCardPrintStatus);
0471             }
0472         }
0473     } else {
0474         disconnect(core, &AtCore::receivedMessage, logWidget, &LogWidget::appendRLog);
0475         disconnect(core, &AtCore::pushedCommand, logWidget, &LogWidget::appendSLog);
0476         core->closeConnection();
0477         core->setState(AtCore::DISCONNECTED);
0478         logWidget->appendLog(tr("Disconnected"));
0479     }
0480 }
0481 
0482 void MainWindow::printPBClicked()
0483 {
0484     QString fileName;
0485     switch (core->state()) {
0486     case AtCore::DISCONNECTED:
0487         QMessageBox::information(this, tr("Error"), tr("Not Connected To a Printer"));
0488         break;
0489 
0490     case AtCore::CONNECTING:
0491         QMessageBox::information(
0492             this,
0493             tr("Error"),
0494             tr(" A Firmware Plugin was not loaded!\n  Please send the command M115 and let us know what your firmware returns, so we can improve our firmware detection. Edit your profile to use \"marlin\" and try again."));
0495         break;
0496 
0497     case AtCore::IDLE:
0498         fileName = QFileDialog::getOpenFileName(this, tr("Select a file to print"), QDir::homePath(), tr("*.gcode"));
0499         if (fileName.isNull()) {
0500             logWidget->appendLog(tr("No File Selected"));
0501         } else {
0502             logWidget->appendLog(tr("Print: %1").arg(fileName));
0503             core->print(fileName);
0504         }
0505         break;
0506 
0507     case AtCore::BUSY:
0508         core->pause(MachineInfo::instance()->readKey(comboProfile->currentText(), MachineInfo::KEY::POSTPAUSE).toString());
0509         break;
0510 
0511     case AtCore::PAUSE:
0512         core->resume();
0513         break;
0514 
0515     default:
0516         qCDebug(TESTCLIENT_MAINWINDOW) << "ERROR / STOP unhandled.";
0517     }
0518 }
0519 
0520 void MainWindow::printerStateChanged(AtCore::STATES state)
0521 {
0522     QString stateString;
0523     switch (state) {
0524     case AtCore::IDLE:
0525         if (connectionTimer->isActive()) {
0526             core->setAutoTemperatureReport(MachineInfo::instance()->readKey(comboProfile->currentText(), MachineInfo::KEY::AUTOTEMPREPORT).toBool());
0527             connectionTimer->stop();
0528         }
0529         buttonConnect->setText(tr("Disconnect"));
0530         printWidget->setPrintText(tr("Print File"));
0531         stateString = tr("Connected to ") + core->connectedPort();
0532         sdDock->setVisible(core->firmwarePlugin()->isSdSupported());
0533         break;
0534 
0535     case AtCore::STARTPRINT:
0536         stateString = tr("START PRINT");
0537         printWidget->setPrintText(tr("Pause Print"));
0538         statusWidget->showPrintArea(true);
0539         break;
0540 
0541     case AtCore::FINISHEDPRINT:
0542         stateString = tr("Finished Print");
0543         printWidget->setPrintText(tr("Print File"));
0544         statusWidget->showPrintArea(false);
0545         break;
0546 
0547     case AtCore::PAUSE:
0548         stateString = tr("Paused");
0549         printWidget->setPrintText(tr("Resume Print"));
0550         break;
0551 
0552     case AtCore::BUSY:
0553         stateString = tr("Printing");
0554         printWidget->setPrintText(tr("Pause Print"));
0555         break;
0556 
0557     case AtCore::DISCONNECTED:
0558         if (connectionTimer->isActive()) {
0559             connectionTimer->stop();
0560         }
0561         sbTemperatureTimer->setValue(0);
0562         stateString = QStringLiteral("Not Connected");
0563         buttonConnect->setText(tr("Connect"));
0564         setConnectionWidgetsEnabled(true);
0565         setDangeriousDocksDisabled(true);
0566         break;
0567 
0568     case AtCore::CONNECTING:
0569         stateString = QStringLiteral("Connecting");
0570         buttonConnect->setText(tr("Abort"));
0571         connectionTimer->start();
0572         setConnectionWidgetsEnabled(false);
0573         setDangeriousDocksDisabled(false);
0574         break;
0575 
0576     case AtCore::STOP:
0577         stateString = tr("Stopping Print");
0578         break;
0579 
0580     case AtCore::ERRORSTATE:
0581         stateString = tr("Command ERROR");
0582         break;
0583     }
0584     statusWidget->setState(stateString);
0585 }
0586 
0587 void MainWindow::toggleDockTitles(bool checked)
0588 {
0589     if (checked) {
0590         delete connectDock->titleBarWidget();
0591         connectDock->setTitleBarWidget(nullptr);
0592         delete logDock->titleBarWidget();
0593         logDock->setTitleBarWidget(nullptr);
0594         delete tempTimelineDock->titleBarWidget();
0595         tempTimelineDock->setTitleBarWidget(nullptr);
0596         delete commandDock->titleBarWidget();
0597         commandDock->setTitleBarWidget(nullptr);
0598         delete moveDock->titleBarWidget();
0599         moveDock->setTitleBarWidget(nullptr);
0600         delete tempControlsDock->titleBarWidget();
0601         tempControlsDock->setTitleBarWidget(nullptr);
0602         delete printDock->titleBarWidget();
0603         printDock->setTitleBarWidget(nullptr);
0604         delete sdDock->titleBarWidget();
0605         sdDock->setTitleBarWidget(nullptr);
0606         delete profileDock->titleBarWidget();
0607         profileDock->setTitleBarWidget(nullptr);
0608     } else {
0609         if (!connectDock->isFloating()) {
0610             connectDock->setTitleBarWidget(new QWidget());
0611         }
0612         if (!logDock->isFloating()) {
0613             logDock->setTitleBarWidget(new QWidget());
0614         }
0615         if (!tempTimelineDock->isFloating()) {
0616             tempTimelineDock->setTitleBarWidget(new QWidget());
0617         }
0618         if (!commandDock->isFloating()) {
0619             commandDock->setTitleBarWidget(new QWidget());
0620         }
0621         if (!moveDock->isFloating()) {
0622             moveDock->setTitleBarWidget(new QWidget());
0623         }
0624         if (!tempControlsDock->isFloating()) {
0625             tempControlsDock->setTitleBarWidget(new QWidget());
0626         }
0627         if (!printDock->isFloating()) {
0628             printDock->setTitleBarWidget(new QWidget());
0629         }
0630         if (!sdDock->isFloating()) {
0631             sdDock->setTitleBarWidget(new QWidget());
0632         }
0633         if (!profileDock->isFloating()) {
0634             profileDock->setTitleBarWidget(new QWidget());
0635         }
0636     }
0637 }
0638 
0639 void MainWindow::setDangeriousDocksDisabled(bool disabled)
0640 {
0641     commandDock->widget()->setDisabled(disabled);
0642     moveDock->widget()->setDisabled(disabled);
0643     tempControlsDock->widget()->setDisabled(disabled);
0644     printDock->widget()->setDisabled(disabled);
0645     sdDock->widget()->setDisabled(disabled);
0646 
0647     if (!disabled) {
0648         temperatureWidget->updateExtruderCount(core->extruderCount());
0649         printWidget->updateFanCount(fanCount);
0650     } else {
0651         printWidget->setPrintText(tr("Print File"));
0652         statusWidget->showPrintArea(false);
0653     }
0654 }
0655 
0656 void MainWindow::setConnectionWidgetsEnabled(bool enabled)
0657 {
0658     comboProfile->setEnabled(enabled);
0659     comboPort->setEnabled(enabled);
0660 }
0661 
0662 void MainWindow::updateAutoTemperatureReport(bool autoReport)
0663 {
0664     disconnect(sbTemperatureTimer, QOverload<int>::of(&QSpinBox::valueChanged), this, {});
0665     disconnect(core, &AtCore::temperatureTimerIntervalChanged, this, {});
0666     disconnect(core, &AtCore::autoCheckTemperatureIntervalChanged, this, {});
0667 
0668     if (autoReport) {
0669         connect(sbTemperatureTimer, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int value) { core->setAutoCheckTemperatureInterval(value); });
0670         connect(core, &AtCore::autoCheckTemperatureIntervalChanged, this, [this](int value) {
0671             if (value != sbTemperatureTimer->value()) {
0672                 sbTemperatureTimer->blockSignals(true);
0673                 sbTemperatureTimer->setValue(value);
0674                 sbTemperatureTimer->blockSignals(false);
0675             }
0676         });
0677     } else {
0678         connect(sbTemperatureTimer, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int value) { core->setTemperatureTimerInterval(value * 1000); });
0679         connect(core, &AtCore::temperatureTimerIntervalChanged, this, [this](int value) {
0680             if (value != sbTemperatureTimer->value()) {
0681                 sbTemperatureTimer->blockSignals(true);
0682                 sbTemperatureTimer->setValue(value / 1000);
0683                 sbTemperatureTimer->blockSignals(false);
0684             }
0685         });
0686     }
0687 }