File indexing completed on 2024-05-19 05:51:03

0001 /* Atelier KDE Printer Host for 3D Printing
0002     Copyright (C) <2017>
0003     Author: Lays Rodrigues - lays.rodrigues@kde.org
0004             Chris Rizzitello - rizzitello@kde.org
0005 
0006     This program is free software: you can redistribute it and/or modify
0007     it under the terms of the GNU General Public License as published by
0008     the Free Software Foundation, either version 3 of the License, or
0009     (at your option) any later version.
0010 
0011     This program is distributed in the hope that it will be useful,
0012     but WITHOUT ANY WARRANTY; without even the implied warranty of
0013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014     GNU General Public License for more details.
0015 
0016     You should have received a copy of the GNU General Public License
0017     along with this program.  If not, see <http://www.gnu.org/licenses/>.
0018 */
0019 
0020 #include "atcoreinstancewidget.h"
0021 #include "machineinfo.h"
0022 #include <GCodeCommands>
0023 #include <KLocalizedString>
0024 #include <QToolBar>
0025 #include <SerialLayer>
0026 
0027 const QMap<MachineInfo::KEY, QString> AtCoreInstanceWidget::keyString = {{MachineInfo::KEY::NAME, QStringLiteral("Name")},
0028                                                                          {MachineInfo::KEY::BAUDRATE, QStringLiteral("bps")},
0029                                                                          {MachineInfo::KEY::FIRMWARE, QStringLiteral("firmware")},
0030                                                                          {MachineInfo::KEY::MAXBEDTEMP, QStringLiteral("maximumTemperatureBed")},
0031                                                                          {MachineInfo::KEY::MAXEXTTEMP, QStringLiteral("maximumTemperatureExtruder")},
0032                                                                          {MachineInfo::KEY::POSTPAUSE, QStringLiteral("postPause")},
0033                                                                          {MachineInfo::KEY::ISCARTESIAN, QStringLiteral("isCartesian")},
0034                                                                          {MachineInfo::KEY::XMAX, QStringLiteral("dimensionX")},
0035                                                                          {MachineInfo::KEY::YMAX, QStringLiteral("dimensionY")},
0036                                                                          {MachineInfo::KEY::ZMAX, QStringLiteral("dimensionZ")},
0037                                                                          {MachineInfo::KEY::AUTOTEMPREPORT, QStringLiteral("autoReportTemp")}};
0038 
0039 AtCoreInstanceWidget::AtCoreInstanceWidget(QWidget *parent)
0040     : QWidget(parent)
0041     , m_bedSize(200, 200)
0042 {
0043     m_theme = palette().text().color().value() >= QColor(Qt::lightGray).value() ? QString("dark") : QString("light");
0044     m_iconSize = QSize(fontMetrics().lineSpacing(), fontMetrics().lineSpacing());
0045     auto HLayout = new QHBoxLayout;
0046     m_bedExtWidget = new BedExtruderWidget(this);
0047     HLayout->addWidget(m_bedExtWidget);
0048 
0049     m_movementWidget = new MovementWidget(this);
0050     m_movementWidget->setDisableMotorsButtonVisible(false);
0051     m_movementWidget->setHomeButtonsVisible(false);
0052     m_movementWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
0053     HLayout->addWidget(m_movementWidget);
0054 
0055     auto VLayout = new QVBoxLayout;
0056     VLayout->addLayout(HLayout);
0057 
0058     m_plotWidget = new PlotWidget(this);
0059     VLayout->addWidget(m_plotWidget, 80);
0060 
0061     auto controlTab = new QWidget(this);
0062     controlTab->setLayout(VLayout);
0063 
0064     // AdvancedTab
0065     VLayout = new QVBoxLayout;
0066     m_printWidget = new PrintWidget(false, this);
0067     VLayout->addWidget(m_printWidget);
0068 
0069     m_commandWidget = new CommandWidget(this);
0070     VLayout->addWidget(m_commandWidget);
0071 
0072     m_logWidget = new LogWidget(new QTemporaryFile(QDir::tempPath() + QStringLiteral("/Atelier_")), this);
0073     VLayout->addWidget(m_logWidget);
0074 
0075     m_advancedTab = new QWidget(this);
0076     m_advancedTab->setLayout(VLayout);
0077 
0078     m_sdWidget = new SdWidget(this);
0079 
0080     VLayout = new QVBoxLayout();
0081     buildToolbar();
0082     buildConnectionToolbar();
0083     HLayout = new QHBoxLayout;
0084     HLayout->addWidget(m_toolBar);
0085     HLayout->addWidget(m_connectToolBar);
0086     HLayout->addWidget(m_connectButton);
0087     VLayout->addLayout(HLayout);
0088     m_toolBar->setHidden(true);
0089 
0090     m_tabWidget = new QTabWidget(this);
0091     m_tabWidget->addTab(controlTab, i18n("Controls"));
0092     m_tabWidget->addTab(m_advancedTab, i18n("Advanced"));
0093     m_tabWidget->addTab(m_sdWidget, i18n("Sd Card"));
0094     VLayout->addWidget(m_tabWidget);
0095 
0096     m_statusWidget = new StatusWidget(false, this);
0097     m_statusWidget->showPrintArea(false);
0098     VLayout->addWidget(m_statusWidget);
0099     setLayout(VLayout);
0100 
0101     enableControls(false);
0102     updateProfileData();
0103     initConnectsToAtCore();
0104 }
0105 AtCoreInstanceWidget::~AtCoreInstanceWidget()
0106 {
0107     m_core.close();
0108 }
0109 
0110 void AtCoreInstanceWidget::buildToolbar()
0111 {
0112     m_toolBar = new QToolBar(this);
0113     m_toolBar->setIconSize(m_iconSize);
0114     m_toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0115 
0116     auto lb = new QLabel(this);
0117     QIcon icon = QIcon::fromTheme("go-home", QIcon(QString(":/%1/home").arg(m_theme)));
0118     lb->setPixmap(icon.pixmap(m_iconSize));
0119     m_toolBar->addWidget(lb);
0120     lb = new QLabel(i18n("Home:"), this);
0121     m_toolBar->addWidget(lb);
0122 
0123     auto homeAll = new QAction(i18n("All"));
0124     connect(homeAll, &QAction::triggered, this, [this] { m_core.home(); });
0125     m_toolBar->addAction(homeAll);
0126 
0127     for (const auto &homes : std::map<QString, int> {{"X", AtCore::X}, {"Y", AtCore::Y}, {"Z", AtCore::Z}}) {
0128         auto home = new QAction(homes.first, this);
0129         connect(home, &QAction::triggered, this, [this, homes] { m_core.home(uchar(homes.second)); });
0130         m_toolBar->addAction(home);
0131     }
0132 
0133     m_toolBar->addSeparator();
0134 
0135     m_printAction = new QAction(QIcon::fromTheme("media-playback-start", style()->standardIcon(QStyle::SP_MediaPlay)), i18n("Print"), this);
0136     connect(m_printAction, &QAction::triggered, this, [this] {
0137         if (m_core.state() == AtCore::BUSY) {
0138             m_logWidget->appendLog(i18n("Pause Print"));
0139             pausePrint();
0140             return;
0141         }
0142         if (m_core.state() == AtCore::IDLE) {
0143             print();
0144         } else if (m_core.state() == AtCore::PAUSE) {
0145             m_logWidget->appendLog(i18n("Resume Print"));
0146             m_core.resume();
0147         }
0148     });
0149     m_toolBar->addAction(m_printAction);
0150 
0151     m_stopAction = new QAction(QIcon::fromTheme("media-playback-stop", QIcon(QStringLiteral(":/%1/stop").arg(m_theme))), i18n("Stop"), this);
0152     connect(m_stopAction, &QAction::triggered, this, &AtCoreInstanceWidget::stopPrint);
0153     connect(m_stopAction, &QAction::triggered, this, [this] {
0154         m_printAction->setText(i18n("Print"));
0155         m_printAction->setIcon(QIcon::fromTheme("media-playback-start", QIcon(QStringLiteral(":/%1/start").arg(m_theme))));
0156     });
0157     m_toolBar->addAction(m_stopAction);
0158 
0159     auto disableMotorsAction = new QAction(style()->standardIcon(QStyle::SP_MediaStop), i18n("Disable Motors"), this);
0160     connect(disableMotorsAction, &QAction::triggered, this, &AtCoreInstanceWidget::disableMotors);
0161     m_toolBar->addAction(disableMotorsAction);
0162 
0163     togglePrintButtons(m_fileCount);
0164 }
0165 
0166 void AtCoreInstanceWidget::buildConnectionToolbar()
0167 {
0168     m_connectToolBar = new QToolBar(this);
0169     m_comboPort = new QComboBox(this);
0170     m_comboPort->setEditable(true);
0171     auto deviceLabel = new QLabel(i18n("Device"), this);
0172     auto deviceLayout = new QHBoxLayout;
0173     deviceLayout->addWidget(deviceLabel);
0174     deviceLayout->addWidget(m_comboPort, 100);
0175 
0176     m_comboProfile = new QComboBox(this);
0177     m_comboProfile->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
0178     auto profileLayout = new QHBoxLayout;
0179     auto profileLabel = new QLabel(i18n("Profile"), this);
0180     profileLayout->addWidget(profileLabel);
0181     profileLayout->addWidget(m_comboProfile, 100);
0182 
0183     auto connectLayout = new QHBoxLayout;
0184     connectLayout->addLayout(deviceLayout, 50);
0185     connectLayout->addLayout(profileLayout, 50);
0186 
0187     m_connectWidget = new QWidget(this);
0188     m_connectWidget->setLayout(connectLayout);
0189     m_connectToolBar->addWidget(m_connectWidget);
0190 
0191     m_connectButton = new QPushButton(QIcon::fromTheme("network-connect", QIcon(QString(":/%1/connect").arg(m_theme))), i18n("Connect"), this);
0192     m_connectButton->setIconSize(m_iconSize);
0193     m_connectButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0194     connect(this, &AtCoreInstanceWidget::disableDisconnect, m_connectButton, &QPushButton::setDisabled);
0195     connect(m_connectButton, &QPushButton::clicked, this, &AtCoreInstanceWidget::connectButtonClicked);
0196 
0197     m_connectionTimer = new QTimer(this);
0198     m_connectionTimer->setInterval(20000);
0199     m_connectionTimer->setSingleShot(true);
0200     connect(m_connectionTimer, &QTimer::timeout, this, [this] {
0201         m_connectButton->clicked();
0202         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."));
0203     });
0204 }
0205 
0206 void AtCoreInstanceWidget::connectButtonClicked()
0207 {
0208     if (m_core.state() == AtCore::DISCONNECTED) {
0209         if (m_comboProfile->currentText().isEmpty()) {
0210             QMessageBox::information(this, i18n("No Profiles!"), i18n("Connecting requires creating a profile for your printer. Create a profile in the next dialog then try again."));
0211             emit(requestProfileDialog());
0212             return;
0213         }
0214 
0215         if (m_comboPort->currentText().isEmpty()) {
0216             QMessageBox::critical(this, i18n("Error"), i18n("Please, connect a serial device to continue!"));
0217             return;
0218         }
0219         // Get profile data before connecting.
0220         m_profileData = MachineInfo::instance()->readProfile(m_comboProfile->currentText());
0221         // then connect
0222         if (m_core.newConnection(m_comboPort->currentText(), m_profileData[keyString[MachineInfo::KEY::BAUDRATE]].toInt(), m_profileData[keyString[MachineInfo::KEY::FIRMWARE]].toString())) {
0223             emit(connectionChanged(QStringLiteral("%1 @ %2").arg(m_profileData[keyString[MachineInfo::KEY::NAME]].toString(), m_comboPort->currentText())));
0224             m_profileData[keyString[MachineInfo::KEY::MAXBEDTEMP]].toBool() ? m_bedExtWidget->setBedMaxTemperature(m_profileData[keyString[MachineInfo::KEY::MAXBEDTEMP]].toInt()) : m_bedExtWidget->setBedThermoHidden(true);
0225 
0226             m_bedExtWidget->setExtruderMaxTemperature(m_profileData[keyString[MachineInfo::KEY::MAXEXTTEMP]].toInt());
0227 
0228             int xMax = m_profileData[keyString[MachineInfo::KEY::XMAX]].toInt();
0229             int yMax = m_profileData[keyString[MachineInfo::KEY::YMAX]].toInt();
0230             int zMax = m_profileData[keyString[MachineInfo::KEY::ZMAX]].toInt();
0231             m_movementWidget->setAxisMax(xMax, yMax, zMax);
0232             // AddFan Support to profile
0233             m_printWidget->updateFanCount(2);
0234             // Adjust bed size
0235             QSize newSize;
0236             if (m_profileData[keyString[MachineInfo::KEY::ISCARTESIAN]].toBool()) {
0237                 newSize = QSize(xMax, yMax);
0238             } else {
0239                 newSize = QSize(xMax, 0);
0240             }
0241             if (newSize != m_bedSize) {
0242                 m_bedSize = newSize;
0243                 emit bedSizeChanged(m_bedSize);
0244             }
0245         }
0246     } else {
0247         m_core.closeConnection();
0248         emit(connectionChanged(i18n("Connect a Printer")));
0249     }
0250 }
0251 
0252 void AtCoreInstanceWidget::initConnectsToAtCore()
0253 {
0254     // connect log to atcoreMessages
0255     connect(&m_core, &AtCore::atcoreMessage, m_logWidget, &LogWidget::appendLog);
0256     m_core.setSerialTimerInterval(100);
0257     // Handle device changes
0258     connect(&m_core, &AtCore::portsChanged, this, &AtCoreInstanceWidget::updateSerialPort);
0259 
0260     connect(&m_core, &AtCore::autoTemperatureReportChanged, this, [this](const bool autoReport) {
0261         if (m_profileData[keyString[MachineInfo::KEY::AUTOTEMPREPORT]].toBool() != autoReport) {
0262             m_profileData[keyString[MachineInfo::KEY::AUTOTEMPREPORT]] = autoReport;
0263             saveProfile();
0264         }
0265     });
0266     // Handle AtCore status change
0267     connect(&m_core, &AtCore::stateChanged, this, &AtCoreInstanceWidget::handlePrinterStatusChanged);
0268     // If the number of extruders from the printer change, we need to update the radiobuttons on the widget
0269     connect(this, &AtCoreInstanceWidget::extruderCountChanged, m_bedExtWidget, &BedExtruderWidget::setExtruderCount);
0270     // Bed and Extruder temperatures management
0271     connect(m_bedExtWidget, &BedExtruderWidget::bedTemperatureChanged, &m_core, &AtCore::setBedTemp);
0272     connect(m_bedExtWidget, &BedExtruderWidget::extTemperatureChanged, &m_core, &AtCore::setExtruderTemp);
0273     // command Widget
0274     connect(m_commandWidget, &CommandWidget::commandPressed, this, [this](const QString &command) {
0275         m_logWidget->appendLog(i18n("Push: %1", command));
0276         m_core.pushCommand(command);
0277     });
0278 
0279     connect(m_commandWidget, &CommandWidget::messagePressed, [this](const QString &message) {
0280         m_logWidget->appendLog(i18n("Display: %1", message));
0281         m_core.showMessage(message);
0282     });
0283 
0284     // Fan, Flow and Speed management
0285     connect(m_printWidget, &PrintWidget::fanSpeedChanged, &m_core, &AtCore::setFanSpeed);
0286     connect(m_printWidget, &PrintWidget::flowRateChanged, &m_core, &AtCore::setFlowRate);
0287     connect(m_printWidget, &PrintWidget::printSpeedChanged, &m_core, &AtCore::setPrinterSpeed);
0288     // Movement Widget
0289     connect(m_movementWidget, &MovementWidget::absoluteMove, this, [this](const QLatin1Char &axis, const double value) {
0290         m_logWidget->appendLog(GCode::description(GCode::G1));
0291         m_core.move(axis, value);
0292     });
0293 
0294     connect(m_movementWidget, &MovementWidget::unitsChanged, this, [this](int units) {
0295         auto selection = static_cast<AtCore::UNITS>(units);
0296         m_core.setUnits(selection);
0297     });
0298 
0299     connect(m_movementWidget, &MovementWidget::relativeMove, this, [this](const QLatin1Char &axis, const double value) {
0300         m_logWidget->appendLog(i18n("Relative Move: %1, %2", axis, QString::number(value)));
0301         m_core.setRelativePosition();
0302         m_core.move(axis, value);
0303         m_core.setAbsolutePosition();
0304     });
0305 
0306     // Sd Card Stuff
0307     connect(&m_core, &AtCore::sdCardFileListChanged, m_sdWidget, &SdWidget::updateFilelist);
0308     connect(m_sdWidget, &SdWidget::requestSdList, &m_core, &AtCore::sdFileList);
0309     connect(&m_core, &AtCore::sdMountChanged, m_statusWidget, &StatusWidget::setSD);
0310 
0311     connect(m_sdWidget, &SdWidget::printSdFile, this, [this](const QString &fileName) {
0312         if (fileName.isEmpty()) {
0313             QMessageBox::information(this, i18n("Print Error"), i18n("You must select a file from the list"));
0314         } else {
0315             m_core.print(fileName, true);
0316             togglePrintButtons(true);
0317         }
0318     });
0319 
0320     connect(m_sdWidget, &SdWidget::deleteSdFile, this, [this](const QString &fileName) {
0321         if (fileName.isEmpty()) {
0322             QMessageBox::information(this, i18n("Delete Error"), i18n("You must select a file from the list"));
0323         } else {
0324             m_core.sdDelete(fileName);
0325         }
0326     });
0327 }
0328 
0329 void AtCoreInstanceWidget::printFile(const QUrl &fileName)
0330 {
0331     if (fileName.isEmpty()) {
0332         QMessageBox::critical(this, i18n("Filename Empty"), i18n("No filename sent from calling method, please check and try again."));
0333         return;
0334     }
0335     if (!QFileInfo(fileName.toLocalFile()).isReadable()) {
0336         QMessageBox::critical(this, i18n("File not found"), i18n("%1 \nIs not readable, please check and try again.", fileName.toLocalFile()));
0337         return;
0338     }
0339     if (m_core.state() == AtCore::IDLE) {
0340         m_logWidget->appendLog(i18n("Printing: %1", fileName.toLocalFile()));
0341         m_core.print(fileName.toLocalFile());
0342     }
0343 }
0344 
0345 void AtCoreInstanceWidget::print()
0346 {
0347     emit(requestFileChooser());
0348 }
0349 
0350 void AtCoreInstanceWidget::pausePrint()
0351 {
0352     if (m_core.state() == AtCore::BUSY) {
0353         m_core.pause(m_profileData[keyString[MachineInfo::KEY::POSTPAUSE]].toString());
0354     } else if (m_core.state() == AtCore::PAUSE) {
0355         m_core.resume();
0356     }
0357 }
0358 
0359 void AtCoreInstanceWidget::stopPrint()
0360 {
0361     m_core.stop();
0362 }
0363 
0364 void AtCoreInstanceWidget::disableMotors()
0365 {
0366     m_core.disableMotors(0);
0367 }
0368 
0369 void AtCoreInstanceWidget::handlePrinterStatusChanged(AtCore::STATES newState)
0370 {
0371     static QString stateString;
0372     switch (newState) {
0373     case AtCore::CONNECTING: {
0374         m_connectionTimer->start();
0375         m_core.setSerialTimerInterval(0);
0376         m_connectButton->setText(i18n("Disconnect"));
0377         m_connectButton->setIcon(QIcon::fromTheme("network-disconnect", QIcon(QString(":/%1/disconnect").arg(m_theme))));
0378         m_connectToolBar->setHidden(true);
0379         m_toolBar->setHidden(false);
0380         stateString = i18n("Connecting...");
0381         m_logWidget->appendLog(i18n("Attempting to Connect"));
0382         connect(&m_core, &AtCore::receivedMessage, m_logWidget, &LogWidget::appendRLog);
0383         connect(&m_core, &AtCore::pushedCommand, m_logWidget, &LogWidget::appendSLog);
0384     } break;
0385     case AtCore::IDLE: {
0386         if (m_connectionTimer->isActive()) {
0387             m_core.setAutoTemperatureReport(m_profileData[keyString[MachineInfo::KEY::AUTOTEMPREPORT]].toBool());
0388             m_connectionTimer->stop();
0389         }
0390         stateString = i18n("Connected to %1", m_core.connectedPort());
0391         emit extruderCountChanged(m_core.extruderCount());
0392         m_logWidget->appendLog(stateString);
0393         emit disableDisconnect(false);
0394         enableControls(true);
0395         connectExtruderTemperatureData(true);
0396         if (m_profileData[keyString[MachineInfo::KEY::MAXBEDTEMP]].toBool()) {
0397             connectBedTemperatureData(true);
0398         }
0399         if (!m_core.availableFirmwarePlugins().contains(m_profileData[keyString[MachineInfo::KEY::FIRMWARE]].toString())) {
0400             m_profileData[keyString[MachineInfo::KEY::FIRMWARE]] = m_core.firmwarePlugin()->name().toLower();
0401             saveProfile();
0402         }
0403     } break;
0404     case AtCore::DISCONNECTED: {
0405         if (m_connectionTimer->isActive()) {
0406             m_connectionTimer->stop();
0407         }
0408         stateString = i18n("Not Connected");
0409         disconnect(&m_core, &AtCore::receivedMessage, m_logWidget, &LogWidget::appendRLog);
0410         disconnect(&m_core, &AtCore::pushedCommand, m_logWidget, &LogWidget::appendSLog);
0411         m_logWidget->appendLog(i18n("Serial disconnected"));
0412         m_core.setSerialTimerInterval(100);
0413         m_connectButton->setText(i18n("Connect"));
0414         m_connectButton->setIcon(QIcon::fromTheme("network-connect", QIcon(QString(":/%1/connect").arg(m_theme))));
0415         m_connectToolBar->setHidden(false);
0416         m_toolBar->setHidden(true);
0417         enableControls(false);
0418         connectExtruderTemperatureData(false);
0419         connectBedTemperatureData(false);
0420     } break;
0421     case AtCore::STARTPRINT: {
0422         stateString = i18n("Starting Print");
0423         m_statusWidget->showPrintArea(true);
0424         connect(&m_core, &AtCore::printProgressChanged, m_statusWidget, &StatusWidget::updatePrintProgress);
0425     } break;
0426     case AtCore::FINISHEDPRINT: {
0427         stateString = i18n("Finished Print");
0428         m_statusWidget->showPrintArea(false);
0429         disconnect(&m_core, &AtCore::printProgressChanged, m_statusWidget, &StatusWidget::updatePrintProgress);
0430         m_printAction->setText(i18n("Print"));
0431         m_printAction->setIcon(QIcon::fromTheme("media-playback-start", QIcon(QString(":/%1/start").arg(m_theme))));
0432         m_logWidget->appendLog(i18n("Finished Print Job"));
0433     } break;
0434     case AtCore::BUSY: {
0435         stateString = i18n("Printing");
0436         emit disableDisconnect(true);
0437         m_printAction->setText(i18n("Pause"));
0438         m_printAction->setIcon(QIcon::fromTheme("media-playback-pause", QIcon(QString(":/%1/pause").arg(m_theme))));
0439     } break;
0440     case AtCore::PAUSE: {
0441         stateString = i18n("Paused");
0442         m_printAction->setText(i18n("Resume"));
0443         m_printAction->setIcon(QIcon::fromTheme("media-playback-start", QIcon(QString(":/%1/start").arg(m_theme))));
0444     } break;
0445     case AtCore::STOP: {
0446         stateString = i18n("Stopping Print");
0447         m_logWidget->appendLog(stateString);
0448     } break;
0449     case AtCore::ERRORSTATE: {
0450         stateString = i18n("Error");
0451     } break;
0452     default:
0453         m_logWidget->appendLog(i18n("Unknown AtCore State, %1", newState));
0454         break;
0455     }
0456     m_statusWidget->setState(stateString);
0457 }
0458 
0459 void AtCoreInstanceWidget::checkTemperature(uint sensorType, uint number, float temp)
0460 {
0461     static QString msg;
0462     switch (sensorType) {
0463     case 0x00: // bed
0464         msg = QString::fromLatin1("Bed Temperature");
0465         break;
0466 
0467     case 0x01: // bed target
0468         msg = QString::fromLatin1("Bed Target Temperature");
0469         break;
0470 
0471     case 0x02: // extruder
0472         msg = QString::fromLatin1("Extruder[%1] Temperature").arg(QString::number(number));
0473         break;
0474 
0475     case 0x03: // extruder target
0476         msg = QString::fromLatin1("Extruder[%1] Target Temperature").arg(QString::number(number));
0477         break;
0478 
0479     case 0x04: // enclosure
0480         msg = QString::fromLatin1("Enclosure Temperature");
0481         break;
0482 
0483     case 0x05: // enclosure target
0484         msg = QString::fromLatin1("Enclosure Target Temperature");
0485         break;
0486     }
0487 
0488     msg.append(QString::fromLatin1(": %1").arg(QString::number(double(temp), 'f', 2)));
0489     m_logWidget->appendLog(msg);
0490 }
0491 
0492 void AtCoreInstanceWidget::enableControls(bool b)
0493 {
0494     if (b) {
0495         layout()->removeWidget(m_logWidget);
0496         layout()->removeWidget(m_statusWidget);
0497         layout()->addWidget(m_statusWidget);
0498         m_advancedTab->layout()->addWidget(m_logWidget);
0499     } else {
0500         m_advancedTab->layout()->removeWidget(m_logWidget);
0501         layout()->addWidget(m_logWidget);
0502         layout()->removeWidget(m_statusWidget);
0503         layout()->addWidget(m_statusWidget);
0504     }
0505     m_tabWidget->setHidden(!b);
0506     m_toolBar->setEnabled(b);
0507 }
0508 
0509 bool AtCoreInstanceWidget::connected()
0510 {
0511     return (m_core.state() != AtCore::DISCONNECTED);
0512 }
0513 
0514 void AtCoreInstanceWidget::setFileCount(int count)
0515 {
0516     m_fileCount = count;
0517     togglePrintButtons(m_fileCount);
0518 }
0519 
0520 void AtCoreInstanceWidget::updateSerialPort(QStringList ports)
0521 {
0522     m_comboPort->clear();
0523     // Remove any strings that match ttyS## from the port list.
0524     ports = ports.filter(QRegularExpression("^((?!ttyS\\d+).)*$"));
0525     if (!ports.isEmpty()) {
0526         m_comboPort->addItems(ports);
0527         m_logWidget->appendLog(i18n("Found %1 Ports", QString::number(ports.count())));
0528     } else {
0529         QString portError(i18n("No available ports! Please connect a serial device to continue!"));
0530         if (!m_logWidget->endsWith(portError)) {
0531             m_logWidget->appendLog(portError);
0532         }
0533     }
0534 }
0535 
0536 void AtCoreInstanceWidget::updateProfileData()
0537 {
0538     m_comboProfile->clear();
0539     m_comboProfile->addItems(MachineInfo::instance()->profileNames());
0540 
0541     if (m_core.state() != AtCore::DISCONNECTED) {
0542         m_profileData = MachineInfo::instance()->readProfile(m_comboProfile->currentText());
0543         bool hBed = MachineInfo::instance()->readKey(m_comboProfile->currentText(), MachineInfo::KEY::MAXBEDTEMP).toInt() > 0;
0544         m_bedExtWidget->setBedThermoHidden(!hBed);
0545         connectBedTemperatureData(hBed);
0546         m_bedExtWidget->setBedMaxTemperature(MachineInfo::instance()->readKey(m_comboProfile->currentText(), MachineInfo::KEY::MAXBEDTEMP).toInt());
0547         m_bedExtWidget->setExtruderMaxTemperature(MachineInfo::instance()->readKey(m_comboProfile->currentText(), MachineInfo::KEY::MAXEXTTEMP).toInt());
0548     }
0549 }
0550 
0551 void AtCoreInstanceWidget::togglePrintButtons(bool shown)
0552 {
0553     m_printAction->setVisible(shown);
0554     m_stopAction->setVisible(shown);
0555 }
0556 
0557 bool AtCoreInstanceWidget::isPrinting()
0558 {
0559     return (m_core.state() == AtCore::BUSY);
0560 }
0561 
0562 void AtCoreInstanceWidget::saveProfile()
0563 {
0564     QString profile = m_comboProfile->currentText();
0565     MachineInfo::instance()->storeKey(profile, MachineInfo::KEY::FIRMWARE, m_profileData[keyString[MachineInfo::KEY::FIRMWARE]]);
0566     MachineInfo::instance()->storeKey(profile, MachineInfo::KEY::AUTOTEMPREPORT, m_profileData[keyString[MachineInfo::KEY::AUTOTEMPREPORT]]);
0567 }
0568 
0569 void AtCoreInstanceWidget::connectBedTemperatureData(bool connected)
0570 {
0571     if (connected) {
0572         if (m_plotWidget->plots().contains(i18n("Actual Bed"))) {
0573             return;
0574         }
0575         m_plotWidget->addPlot(i18n("Actual Bed"));
0576         connect(m_core.temperature(), &Temperature::bedTemperatureChanged, [this] {
0577             const float temp = m_core.temperature()->bedTemperature();
0578             checkTemperature(0x00, 0, temp);
0579             m_plotWidget->appendPoint(i18n("Actual Bed"), temp);
0580             m_bedExtWidget->updateBedTemp(temp);
0581         });
0582         m_plotWidget->addPlot(i18n("Target Bed"));
0583         connect(m_core.temperature(), &Temperature::bedTargetTemperatureChanged, [this] {
0584             const float temp = m_core.temperature()->bedTargetTemperature();
0585             checkTemperature(0x01, 0, temp);
0586             m_plotWidget->appendPoint(i18n("Target Bed"), temp);
0587             m_bedExtWidget->updateBedTargetTemp(int(temp));
0588         });
0589     } else {
0590         if (m_plotWidget->plots().contains(i18n("Actual Bed"))) {
0591             m_plotWidget->removePlot(i18n("Actual Bed"));
0592             disconnect(m_core.temperature(), &Temperature::bedTemperatureChanged, nullptr, nullptr);
0593             m_plotWidget->removePlot(i18n("Target Bed"));
0594             disconnect(m_core.temperature(), &Temperature::bedTargetTemperatureChanged, nullptr, nullptr);
0595         }
0596     }
0597 }
0598 
0599 void AtCoreInstanceWidget::connectExtruderTemperatureData(bool connected)
0600 {
0601     if (connected) {
0602         if (m_plotWidget->plots().contains((i18n("Actual Ext.1")))) {
0603             return;
0604         }
0605         // Add Extruder.
0606         m_plotWidget->addPlot(i18n("Actual Ext.1"));
0607         connect(m_core.temperature(), &Temperature::extruderTemperatureChanged, this, [this] {
0608             const float temp = m_core.temperature()->extruderTemperature();
0609             checkTemperature(0x02, 0, temp);
0610             m_plotWidget->appendPoint(i18n("Actual Ext.1"), temp);
0611             m_bedExtWidget->updateExtTemp(temp);
0612         });
0613         m_plotWidget->addPlot(i18n("Target Ext.1"));
0614         connect(m_core.temperature(), &Temperature::extruderTargetTemperatureChanged, this, [this] {
0615             const float temp = m_core.temperature()->extruderTargetTemperature();
0616             checkTemperature(0x03, 0, temp);
0617             m_plotWidget->appendPoint(i18n("Target Ext.1"), temp);
0618             m_bedExtWidget->updateExtTargetTemp(int(temp));
0619         });
0620     } else {
0621         if (m_plotWidget->plots().contains(i18n("Actual Ext.1"))) {
0622             m_plotWidget->removePlot(i18n("Actual Ext.1"));
0623             disconnect(m_core.temperature(), &Temperature::extruderTemperatureChanged, nullptr, nullptr);
0624             m_plotWidget->removePlot(i18n("Target Ext.1"));
0625             disconnect(m_core.temperature(), &Temperature::extruderTargetTemperatureChanged, nullptr, nullptr);
0626         }
0627     }
0628 }
0629 
0630 QSize AtCoreInstanceWidget::bedSize()
0631 {
0632     return m_bedSize;
0633 }