File indexing completed on 2024-05-12 04:41:09
0001 /* AtCore KDE Libary for 3D Printers 0002 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0003 SPDX-FileCopyrightText: 2016, 2018 Tomaz Canabrava <tcanabrava@kde.org> 0004 SPDX-FileCopyrightText: 2016-2019, 2021 Chris Rizzitello <rizzitello@kde.org> 0005 SPDX-FileCopyrightText: 2016-2019 Patrick José Pereira <patrickjp@kde.org> 0006 SPDX-FileCopyrightText: 2016, 2019 Lays Rodrigues <lays.rodrigues@kde.org> 0007 SPDX-FileCopyrightText: 2018 Leandro Santiago <leandrosansilva@gmail.com> 0008 SPDX-FileCopyrightText: 2018 Caio Jordão Carvalho <caiojcarvalho@gmail.com> 0009 */ 0010 0011 #include <QCoreApplication> 0012 #include <QDir> 0013 #include <QLoggingCategory> 0014 #include <QMetaEnum> 0015 #include <QPluginLoader> 0016 #include <QProcess> 0017 #include <QSerialPortInfo> 0018 #include <QThread> 0019 #include <QTime> 0020 #include <QTimer> 0021 #include <QVariant> 0022 0023 #include "atcore.h" 0024 #include "atcore_default_folders.h" 0025 #include "atcore_version.h" 0026 #include "gcodecommands.h" 0027 #include "printthread.h" 0028 #include "seriallayer.h" 0029 0030 Q_LOGGING_CATEGORY(ATCORE_PLUGIN, "org.kde.atelier.core.plugin") 0031 Q_LOGGING_CATEGORY(ATCORE_CORE, "org.kde.atelier.core") 0032 /** 0033 * @brief The AtCorePrivate struct 0034 * Provides a private data set for atcore. 0035 */ 0036 struct AtCore::AtCorePrivate { 0037 /** firmwarePlugin: pointer to firmware plugin */ 0038 IFirmware *firmwarePlugin = nullptr; 0039 /** serial: pointer to the serial layer */ 0040 SerialLayer *serial = nullptr; 0041 /** pluginLoader: QPluginLoader */ 0042 QPluginLoader pluginLoader; 0043 /** plugins: Map of plugins name / path */ 0044 QMap<QString, QString> plugins; 0045 /** lastMessage: lastMessage from the printer */ 0046 QByteArray lastMessage; 0047 /** lastCommand: the last command sent to the printer */ 0048 QString lastCommand; 0049 /** extruderCount: extruder count */ 0050 int extruderCount = 1; 0051 /** temperature: Temperature object */ 0052 std::shared_ptr<Temperature> temperature = nullptr; 0053 /** autoTemperatureReport: True if using auto Temperature Reporting*/ 0054 bool autoTemperatureReport = false; 0055 /** bedDeform: BedDeform object */ 0056 std::shared_ptr<BedDeform> bedDeform = nullptr; 0057 /** commandQueue: the list of commands to send to the printer */ 0058 QStringList commandQueue; 0059 /** ready: True if printer is ready for a command */ 0060 bool ready = false; 0061 /** temperatureTimer: timer connected to the checkTemperature function */ 0062 QTimer temperatureTimer; 0063 /** sdPrintProgressTimer: timer used to check in on sdJobs */ 0064 QTimer sdPrintProgressTimer; 0065 /** percentage: print job percent */ 0066 float percentage = 0.0; 0067 /** posString: stored string from last M114 return */ 0068 QByteArray posString; 0069 /** printerState: State of the Printer */ 0070 AtCore::STATES printerState = AtCore::DISCONNECTED; 0071 /** seralPorts: Detected serial Ports */ 0072 QStringList serialPorts; 0073 /** serialTimer: Timer connected to locateSerialPorts */ 0074 QTimer serialTimer; 0075 /** sdCardMounted: True if Sd Card is mounted. */ 0076 bool sdCardMounted = false; 0077 /** sdCardReadingFileList: True while getting file names from sd card */ 0078 bool sdCardReadingFileList = false; 0079 /** sdCardPrinting: True if currently printing from sd card. */ 0080 bool sdCardPrinting = false; 0081 /** sdCardFileName: name of file being used from sd card. */ 0082 QString sdCardFileName; 0083 /** sdCardFileList: List of files on sd card. */ 0084 QStringList sdCardFileList; 0085 /** tempMultiString: Hold temp returns for multiline returns when needed */ 0086 QStringList tempMultiString; 0087 }; 0088 0089 AtCore::AtCore(QObject *parent) 0090 : QObject(parent) 0091 , d(new AtCorePrivate) 0092 { 0093 d->temperature.reset(new Temperature); 0094 d->bedDeform.reset(new BedDeform); 0095 // Register MetaTypes 0096 qRegisterMetaType<AtCore::STATES>("AtCore::STATES"); 0097 setState(AtCore::STATES::DISCONNECTED); 0098 // Connect our Timers 0099 connect(&d->sdPrintProgressTimer, &QTimer::timeout, this, &AtCore::sdCardPrintStatus); 0100 connect(&d->serialTimer, &QTimer::timeout, this, &AtCore::locateSerialPort); 0101 connect(&d->temperatureTimer, &QTimer::timeout, this, &AtCore::checkTemperature); 0102 0103 updateFWPlugins(); 0104 setState(AtCore::STATES::DISCONNECTED); 0105 } 0106 0107 AtCore::~AtCore() 0108 { 0109 /*Needed Allow for Unique AtCorePrivate Pointer*/ 0110 } 0111 0112 QString AtCore::version() const 0113 { 0114 QString versionString = QString::fromLatin1(ATCORE_VERSION_STRING); 0115 #if defined GIT_REVISION 0116 if (!QStringLiteral(GIT_REVISION).isEmpty()) { 0117 versionString.append(QString::fromLatin1("-%1").arg(QStringLiteral(GIT_REVISION))); 0118 } 0119 #endif 0120 return versionString; 0121 } 0122 0123 IFirmware *AtCore::firmwarePlugin() const 0124 { 0125 return d->firmwarePlugin; 0126 } 0127 0128 void AtCore::close() 0129 { 0130 exit(0); 0131 } 0132 0133 Temperature *AtCore::temperature() 0134 { 0135 return d->temperature.get(); 0136 } 0137 0138 void AtCore::findFirmware(const QByteArray &message) 0139 { 0140 Q_EMIT receivedMessage(message); 0141 Q_EMIT atcoreMessage(tr("Waiting for firmware detect.")); 0142 qCDebug(ATCORE_CORE) << "Find Firmware: " << message; 0143 if (!message.contains("FIRMWARE_NAME:")) { 0144 qCDebug(ATCORE_CORE) << "No firmware yet."; 0145 return; 0146 } 0147 0148 qCDebug(ATCORE_CORE) << "Found firmware string, Looking for Firmware Name."; 0149 0150 QString fwName = QString::fromLocal8Bit(message); 0151 fwName = fwName.split(QChar::fromLatin1(':')).at(1); 0152 if (fwName.indexOf(QChar::fromLatin1(' ')) == 0) { 0153 // remove leading space 0154 fwName.remove(0, 1); 0155 } 0156 if (fwName.contains(QChar::fromLatin1(' '))) { 0157 // check there is a space or dont' resize 0158 fwName.resize(fwName.indexOf(QChar::fromLatin1(' '))); 0159 } 0160 fwName = fwName.toLower().simplified(); 0161 if (fwName.contains(QChar::fromLatin1('_'))) { 0162 fwName.resize(fwName.indexOf(QChar::fromLatin1('_'))); 0163 } 0164 qCDebug(ATCORE_CORE) << "Firmware Name:" << fwName; 0165 0166 if (message.contains("EXTRUDER_COUNT:")) { 0167 // this code is broken if more then 9 extruders are detected. since only one char is returned 0168 setExtruderCount(message.at(message.indexOf("EXTRUDER_COUNT:") + 15) - '0'); 0169 } 0170 loadFirmwarePlugin(fwName); 0171 } 0172 0173 void AtCore::loadFirmwarePlugin(const QString &fwName) 0174 { 0175 qCDebug(ATCORE_CORE) << "Loading plugin: " << d->plugins[fwName]; 0176 if (d->plugins.contains(fwName)) { 0177 d->pluginLoader.setFileName(d->plugins[fwName]); 0178 if (!d->pluginLoader.load()) { 0179 // Plugin was not loaded, Provide some debug info. 0180 qCDebug(ATCORE_CORE) << "Plugin Loading: Failed."; 0181 qCDebug(ATCORE_CORE) << d->pluginLoader.errorString(); 0182 setState(AtCore::STATES::CONNECTING); 0183 } else { 0184 // Plugin was loaded successfully. 0185 d->firmwarePlugin = qobject_cast<IFirmware *>(d->pluginLoader.instance()); 0186 firmwarePlugin()->init(this); 0187 disconnect(d->serial, &SerialLayer::receivedCommand, this, nullptr); 0188 connect(d->serial, &SerialLayer::receivedCommand, this, &AtCore::newMessage); 0189 connect(firmwarePlugin(), &IFirmware::readyForCommand, this, &AtCore::processQueue); 0190 d->ready = true; // ready on new firmware load 0191 if (firmwarePlugin()->name() != QStringLiteral("Grbl")) { 0192 setTemperatureTimerInterval(5000); 0193 } 0194 setState(IDLE); 0195 Q_EMIT atcoreMessage(tr("Connected to printer using %1 plugin").arg(d->firmwarePlugin->name())); 0196 } 0197 } else { 0198 qCDebug(ATCORE_CORE) << "Plugin:" << fwName << ": Not found."; 0199 Q_EMIT atcoreMessage(tr("No plugin found for %1.").arg(fwName)); 0200 } 0201 } 0202 0203 void AtCore::waitForPrinterReboot(const QByteArray &message, const QString &fwName) 0204 { 0205 Q_EMIT receivedMessage(message); 0206 if (message.isEmpty()) { 0207 return; 0208 } 0209 0210 if (message.contains("Grbl")) { 0211 loadFirmwarePlugin(QString::fromLatin1("grbl")); 0212 } else if (message.contains("Smoothie")) { 0213 loadFirmwarePlugin(QString::fromLatin1("smoothie")); 0214 } else if (message.contains("start")) { 0215 if (!d->plugins.contains(fwName)) { 0216 disconnect(d->serial, &SerialLayer::receivedCommand, this, {}); 0217 connect(d->serial, &SerialLayer::receivedCommand, this, &AtCore::findFirmware); 0218 QTimer::singleShot(500, this, &AtCore::requestFirmware); 0219 } else { 0220 loadFirmwarePlugin(fwName); 0221 } 0222 } 0223 } 0224 0225 bool AtCore::newConnection(const QString &port, int baud, const QString &fwName, bool disableROC) 0226 { 0227 if (disableROC) { 0228 disableResetOnConnect(port); 0229 } 0230 0231 d->serial = new SerialLayer(port, baud, this); 0232 connect(d->serial, &SerialLayer::serialError, this, &AtCore::handleSerialError); 0233 if (serialInitialized() && d->serial->isWritable()) { 0234 setState(AtCore::STATES::CONNECTING); 0235 connect(d->serial, &SerialLayer::pushedCommand, this, &AtCore::newCommand); 0236 0237 if (!disableROC) { 0238 Q_EMIT atcoreMessage(tr("Waiting for machine restart")); 0239 connect(d->serial, &SerialLayer::receivedCommand, this, [this, fwName](const QByteArray &message) { waitForPrinterReboot(message, fwName); }); 0240 } else { 0241 loadFirmwarePlugin(fwName); 0242 } 0243 if (d->serialTimer.isActive()) { 0244 d->serialTimer.stop(); 0245 } 0246 return true; 0247 } 0248 qCDebug(ATCORE_CORE) << "Failed to open device for Read / Write."; 0249 Q_EMIT atcoreMessage(tr("Failed to open device in read/write mode.")); 0250 return false; 0251 } 0252 0253 bool AtCore::serialInitialized() const 0254 { 0255 if (!d->serial) { 0256 return false; 0257 } 0258 return d->serial->isOpen(); 0259 } 0260 0261 QString AtCore::connectedPort() const 0262 { 0263 return d->serial->portName(); 0264 } 0265 0266 QStringList AtCore::serialPorts() const 0267 { 0268 QStringList ports; 0269 const QList<QSerialPortInfo> serialPortInfoList = QSerialPortInfo::availablePorts(); 0270 if (!serialPortInfoList.isEmpty()) { 0271 for (const QSerialPortInfo &serialPortInfo : serialPortInfoList) { 0272 #ifdef Q_OS_MAC 0273 // Mac OS has callout serial ports starting with cu these devices are read only. 0274 // It is necessary to filter them out to help prevent user error. 0275 if (!serialPortInfo.portName().startsWith(QStringLiteral("cu."), Qt::CaseInsensitive)) { 0276 ports.append(serialPortInfo.portName()); 0277 } 0278 #else 0279 ports.append(serialPortInfo.portName()); 0280 #endif 0281 } 0282 } 0283 return ports; 0284 } 0285 0286 void AtCore::locateSerialPort() 0287 { 0288 QStringList ports = serialPorts(); 0289 if (d->serialPorts != ports) { 0290 d->serialPorts = ports; 0291 Q_EMIT portsChanged(d->serialPorts); 0292 } 0293 } 0294 0295 int AtCore::serialTimerInterval() const 0296 { 0297 return d->serialTimer.interval(); 0298 } 0299 0300 void AtCore::setSerialTimerInterval(int newTime) 0301 { 0302 newTime = std::max(newTime, 0); 0303 if (newTime != d->serialTimer.interval()) { 0304 d->serialTimer.setInterval(newTime); 0305 Q_EMIT serialTimerIntervalChanged(newTime); 0306 } 0307 if (newTime == 0 && d->serialTimer.isActive()) { 0308 d->serialTimer.stop(); 0309 } else { 0310 d->serialTimer.start(newTime); 0311 } 0312 } 0313 0314 int AtCore::temperatureTimerInterval() const 0315 { 0316 return d->temperatureTimer.interval(); 0317 } 0318 0319 void AtCore::setTemperatureTimerInterval(int newTime) 0320 { 0321 newTime = std::max(newTime, 0); 0322 if (newTime != d->temperatureTimer.interval()) { 0323 d->temperatureTimer.setInterval(newTime); 0324 Q_EMIT temperatureTimerIntervalChanged(newTime); 0325 } 0326 if (newTime == 0 && d->temperatureTimer.isActive()) { 0327 d->temperatureTimer.stop(); 0328 } else { 0329 d->temperatureTimer.start(newTime); 0330 } 0331 } 0332 0333 void AtCore::setAutoTemperatureReport(bool autoReport) 0334 { 0335 if (autoReport == d->autoTemperatureReport) { 0336 return; 0337 } 0338 0339 d->autoTemperatureReport = autoReport; 0340 Q_EMIT autoTemperatureReportChanged(autoReport); 0341 0342 if (autoReport) { 0343 setTemperatureTimerInterval(0); 0344 d->commandQueue.removeAll(GCode::toCommand(GCode::M105)); 0345 setAutoCheckTemperatureInterval(5); 0346 } else { 0347 setAutoCheckTemperatureInterval(0); 0348 setTemperatureTimerInterval(5000); 0349 } 0350 } 0351 0352 void AtCore::setAutoCheckTemperatureInterval(int newTime) 0353 { 0354 if (state() >= 2 && state() != AtCore::ERRORSTATE) { 0355 pushCommand(GCode::toCommand(GCode::M155, QString::number(newTime))); 0356 } 0357 Q_EMIT autoCheckTemperatureIntervalChanged(newTime); 0358 } 0359 0360 bool AtCore::autoTemperatureReport() const 0361 { 0362 return d->autoTemperatureReport; 0363 } 0364 0365 void AtCore::newMessage(const QByteArray &message) 0366 { 0367 d->lastMessage = message; 0368 if (d->lastMessage.contains(QString::fromLatin1("Cap:AUTOREPORT_TEMP:1").toLocal8Bit())) { 0369 setAutoTemperatureReport(true); 0370 } 0371 0372 if (d->lastCommand.startsWith(GCode::toCommand(GCode::GCode::G29))) { 0373 if (d->lastMessage.contains("ok")) { 0374 d->bedDeform.get()->decodeDeform(d->tempMultiString); 0375 } 0376 d->tempMultiString.append(QString::fromLatin1(d->lastMessage)); 0377 } 0378 // Check if the message has current coordinates. 0379 if (d->lastCommand.startsWith(GCode::toCommand(GCode::MCommands::M114)) && d->lastMessage.startsWith(QString::fromLatin1("X:").toLocal8Bit())) { 0380 d->posString = message; 0381 d->posString.resize(d->posString.indexOf('E')); 0382 d->posString.replace(':', ""); 0383 } 0384 0385 // Check if have temperature info and decode it 0386 if (d->lastMessage.contains("T:") || d->lastMessage.contains("B:")) { 0387 temperature()->decodeTemp(d->lastMessage); 0388 } 0389 Q_EMIT receivedMessage(d->lastMessage); 0390 } 0391 0392 void AtCore::newCommand(const QByteArray &command) 0393 { 0394 Q_EMIT pushedCommand(command); 0395 } 0396 0397 void AtCore::setRelativePosition() 0398 { 0399 pushCommand(GCode::toCommand(GCode::GCommands::G91)); 0400 } 0401 0402 void AtCore::setAbsolutePosition() 0403 { 0404 pushCommand(GCode::toCommand(GCode::GCommands::G90)); 0405 } 0406 0407 float AtCore::percentagePrinted() const 0408 { 0409 return d->percentage; 0410 } 0411 0412 void AtCore::print(const QString &fileName, bool sdPrint) 0413 { 0414 if (state() == AtCore::STATES::CONNECTING) { 0415 qCDebug(ATCORE_CORE) << "Load a firmware plugin to print."; 0416 return; 0417 } 0418 // Start a print job. 0419 setState(AtCore::STATES::STARTPRINT); 0420 // Only try to print from Sd if the firmware has support for sd cards 0421 if (firmwarePlugin()->isSdSupported()) { 0422 if (sdPrint) { 0423 // Printing from the sd card requires us to send some M commands. 0424 pushCommand(GCode::toCommand(GCode::MCommands::M23, fileName)); 0425 d->sdCardFileName = fileName; 0426 pushCommand(GCode::toCommand(GCode::MCommands::M24)); 0427 setState(AtCore::STATES::BUSY); 0428 d->sdCardPrinting = true; 0429 d->sdPrintProgressTimer.start(5000); 0430 return; 0431 } 0432 } 0433 // Process the gcode with a printThread. 0434 // The Thread processes the gcode without freezing the libary. 0435 // Only sends a command back when the printer is ready, avoiding buffer overflow in the printer. 0436 auto thread = new QThread(this); 0437 auto printThread = new PrintThread(this, fileName); 0438 printThread->moveToThread(thread); 0439 0440 connect(printThread, &PrintThread::printProgressChanged, this, &AtCore::printProgressChanged, Qt::QueuedConnection); 0441 connect(thread, &QThread::started, printThread, &PrintThread::start); 0442 connect(printThread, &PrintThread::finished, thread, &QThread::quit); 0443 connect(thread, &QThread::finished, printThread, &PrintThread::deleteLater); 0444 if (!thread->isRunning()) { 0445 thread->start(); 0446 } 0447 } 0448 0449 void AtCore::pushCommand(const QString &comm) 0450 { 0451 // Be sure our M112 is first in the queue. 0452 if (comm == GCode::toCommand(GCode::MCommands::M112)) { 0453 d->commandQueue.prepend(comm); 0454 } else { 0455 d->commandQueue.append(comm); 0456 } 0457 if (d->ready) { 0458 // The printer is ready for a command now so push one. 0459 processQueue(); 0460 } 0461 } 0462 0463 void AtCore::closeConnection() 0464 { 0465 if (serialInitialized()) { 0466 if (AtCore::state() == AtCore::STATES::BUSY && !d->sdCardPrinting) { 0467 // We have to clean up the print job if printing from the host. 0468 // However disconnecting while printing from sd card should not affect the print job. 0469 setState(AtCore::STATES::STOP); 0470 } 0471 if (firmwarePluginLoaded()) { 0472 disconnect(firmwarePlugin(), &IFirmware::readyForCommand, this, &AtCore::processQueue); 0473 disconnect(d->serial, &SerialLayer::receivedCommand, this, &AtCore::newMessage); 0474 if (d->autoTemperatureReport) { 0475 blockSignals(true); 0476 setAutoTemperatureReport(false); 0477 blockSignals(false); 0478 } 0479 setTemperatureTimerInterval(0); 0480 // Attempt to unload the firmware plugin. 0481 QString name = firmwarePlugin()->name(); 0482 QString msg = d->pluginLoader.unload() ? QStringLiteral("closed.") : QStringLiteral("Failed to close."); 0483 qCDebug(ATCORE_CORE) << QStringLiteral("Firmware plugin %1 %2").arg(name, msg); 0484 d->firmwarePlugin = nullptr; 0485 } 0486 // Do not reset the connect on disconnect when closing this will cause a reset on connect for the next connection. 0487 disconnect(d->serial, &SerialLayer::serialError, this, &AtCore::handleSerialError); 0488 disconnect(d->serial, &SerialLayer::pushedCommand, this, &AtCore::newMessage); 0489 d->serial->close(); 0490 // Clear our copy of the sdcard filelist 0491 clearSdCardFileList(); 0492 setState(AtCore::STATES::DISCONNECTED); 0493 d->serialTimer.start(); 0494 } 0495 } 0496 0497 AtCore::STATES AtCore::state() 0498 { 0499 return d->printerState; 0500 } 0501 0502 void AtCore::setState(AtCore::STATES state) 0503 { 0504 if (state != d->printerState) { 0505 qCDebug(ATCORE_CORE) << QStringLiteral("Atcore state changed from [%1] to [%2]").arg(QVariant::fromValue(d->printerState).toString(), QVariant::fromValue(state).toString()); 0506 d->printerState = state; 0507 if (state == AtCore::STATES::FINISHEDPRINT && d->sdCardPrinting) { 0508 // Clean up the sd card print 0509 d->sdCardPrinting = false; 0510 if (d->sdPrintProgressTimer.isActive()) { 0511 d->sdPrintProgressTimer.stop(); 0512 } 0513 } 0514 Q_EMIT stateChanged(d->printerState); 0515 } 0516 } 0517 0518 void AtCore::stop() 0519 { 0520 // Stop a print job 0521 setState(AtCore::STATES::STOP); 0522 d->commandQueue.clear(); 0523 if (d->sdCardPrinting) { 0524 stopSdPrint(); 0525 } 0526 setExtruderTemp(0, 0); 0527 setBedTemp(0); 0528 home(AtCore::AXES::X); 0529 } 0530 0531 void AtCore::emergencyStop() 0532 { 0533 // Emergency Stop. Stops the machine 0534 // Clear the queue, and any print job 0535 // Before sending the command to ensure 0536 // Less chance of movement after the restart. 0537 d->commandQueue.clear(); 0538 if (AtCore::state() == AtCore::STATES::BUSY) { 0539 if (!d->sdCardPrinting) { 0540 // Stop our running print thread 0541 setState(AtCore::STATES::STOP); 0542 } 0543 } 0544 pushCommand(GCode::toCommand(GCode::MCommands::M112)); 0545 } 0546 0547 void AtCore::stopSdPrint() 0548 { 0549 // Stop an SdCard Print. 0550 pushCommand(GCode::toCommand(GCode::MCommands::M25)); 0551 d->sdCardFileName = QString(); 0552 pushCommand(GCode::toCommand(GCode::MCommands::M23, d->sdCardFileName)); 0553 AtCore::setState(AtCore::STATES::FINISHEDPRINT); 0554 AtCore::setState(AtCore::STATES::IDLE); 0555 } 0556 0557 void AtCore::requestFirmware() 0558 { 0559 if (serialInitialized()) { 0560 // ensure M115 is sent on cold connect. 0561 d->commandQueue.clear(); 0562 d->ready = true; 0563 qCDebug(ATCORE_CORE) << "Sending " << GCode::description(GCode::MCommands::M115); 0564 pushCommand(GCode::toCommand(GCode::MCommands::M115)); 0565 } else { 0566 qCDebug(ATCORE_CORE) << "There is no open device to send commands"; 0567 } 0568 } 0569 0570 bool AtCore::firmwarePluginLoaded() const 0571 { 0572 return firmwarePlugin(); 0573 } 0574 0575 QStringList AtCore::availableFirmwarePlugins() const 0576 { 0577 return d->plugins.keys(); 0578 } 0579 0580 void AtCore::pause(const QString &pauseActions) 0581 { 0582 if (d->sdCardPrinting) { 0583 pushCommand(GCode::toCommand(GCode::MCommands::M25)); 0584 } 0585 // Push the command to request current coordinates. 0586 // This will be read by AtCore::newMessage and stored for use on resume. 0587 pushCommand(GCode::toCommand(GCode::MCommands::M114)); 0588 if (!pauseActions.isEmpty()) { 0589 QStringList temp = pauseActions.split(QChar::fromLatin1(',')); 0590 for (int i = 0; i < temp.length(); i++) { 0591 pushCommand(temp.at(i)); 0592 } 0593 } 0594 setState(AtCore::PAUSE); 0595 } 0596 0597 void AtCore::resume() 0598 { 0599 if (d->sdCardPrinting) { 0600 pushCommand(GCode::toCommand(GCode::MCommands::M24)); 0601 } else { 0602 // Move back to previous coordinates. 0603 pushCommand(GCode::toCommand(GCode::GCommands::G0, QString::fromLatin1(d->posString))); 0604 } 0605 setState(AtCore::BUSY); 0606 } 0607 0608 /*~~~~~Control Slots ~~~~~~~~*/ 0609 0610 void AtCore::home() 0611 { 0612 pushCommand(GCode::toCommand(GCode::GCommands::G28)); 0613 } 0614 0615 void AtCore::home(uchar axis) 0616 { 0617 QString args; 0618 0619 if (axis & AtCore::AXES::X) { 0620 args.append(QStringLiteral("X0 ")); 0621 } 0622 0623 if (axis & AtCore::AXES::Y) { 0624 args.append(QStringLiteral("Y0 ")); 0625 } 0626 0627 if (axis & AtCore::AXES::Z) { 0628 args.append(QStringLiteral("Z0")); 0629 } 0630 pushCommand(GCode::toCommand(GCode::GCommands::G28, args)); 0631 } 0632 0633 void AtCore::setExtruderTemp(uint temp, uint extruder, bool andWait) 0634 { 0635 temp = std::min<uint>(temp, 10000); 0636 extruder = std::min<uint>(extruder, 10000); 0637 0638 if (andWait) { 0639 pushCommand(GCode::toCommand(GCode::MCommands::M109, QString::number(temp), QString::number(extruder))); 0640 } else { 0641 pushCommand(GCode::toCommand(GCode::MCommands::M104, QString::number(extruder), QString::number(temp))); 0642 } 0643 } 0644 0645 void AtCore::setBedTemp(uint temp, bool andWait) 0646 { 0647 temp = std::min<uint>(temp, 10000); 0648 0649 if (andWait) { 0650 pushCommand(GCode::toCommand(GCode::MCommands::M190, QString::number(temp))); 0651 } else { 0652 pushCommand(GCode::toCommand(GCode::MCommands::M140, QString::number(temp))); 0653 } 0654 } 0655 0656 void AtCore::setFanSpeed(uint speed, uint fanNumber) 0657 { 0658 speed = std::max<uint>(0, std::min<uint>(speed, 10000)); 0659 fanNumber = std::min<uint>(fanNumber, 10000); 0660 0661 pushCommand(GCode::toCommand(GCode::MCommands::M106, QString::number(fanNumber), QString::number(speed))); 0662 } 0663 0664 void AtCore::setPrinterSpeed(uint speed) 0665 { 0666 speed = std::max<uint>(0, std::min<uint>(speed, 10000)); 0667 pushCommand(GCode::toCommand(GCode::MCommands::M220, QString::number(speed))); 0668 } 0669 0670 void AtCore::setFlowRate(uint speed) 0671 { 0672 speed = std::max<uint>(0, std::min<uint>(speed, 10000)); 0673 pushCommand(GCode::toCommand(GCode::MCommands::M221, QString::number(speed))); 0674 } 0675 0676 void AtCore::move(AtCore::AXES axis, double arg) 0677 { 0678 const auto axisAsString = QMetaEnum::fromType<AtCore::AXES>().valueToKey(axis); 0679 move(QLatin1Char(axisAsString[0]), arg); 0680 } 0681 0682 void AtCore::move(QLatin1Char axis, double arg) 0683 { 0684 // Using QString::number(double, format, precision) 0685 // f = 'format as [-]9.9' 0686 // 3 = use 3 decimal precision 0687 pushCommand(GCode::toCommand(GCode::GCommands::G1, QStringLiteral("%1 %2").arg(axis).arg(QString::number(arg, 'f', 3)))); 0688 } 0689 0690 int AtCore::extruderCount() const 0691 { 0692 return d->extruderCount; 0693 } 0694 0695 void AtCore::setExtruderCount(int newCount) 0696 { 0697 if (d->extruderCount != newCount && newCount >= 1) { 0698 d->extruderCount = newCount; 0699 Q_EMIT extruderCountChanged(newCount); 0700 qCDebug(ATCORE_CORE) << "Extruder Count:" << QString::number(extruderCount()); 0701 } 0702 } 0703 void AtCore::processQueue() 0704 { 0705 d->ready = true; 0706 0707 if (d->commandQueue.isEmpty()) { 0708 return; 0709 } 0710 0711 if (!serialInitialized()) { 0712 qCDebug(ATCORE_PLUGIN) << "Can't process queue ! Serial not initialized."; 0713 return; 0714 } 0715 0716 d->lastCommand = d->commandQueue.takeAt(0); 0717 0718 if (firmwarePluginLoaded()) { 0719 d->serial->pushCommand(firmwarePlugin()->translate(d->lastCommand)); 0720 } else { 0721 d->serial->pushCommand(d->lastCommand.toLocal8Bit()); 0722 } 0723 d->ready = false; 0724 } 0725 0726 void AtCore::checkTemperature() 0727 { 0728 // One request for the temperature in the queue at a time. 0729 if (d->commandQueue.contains(GCode::toCommand(GCode::MCommands::M105))) { 0730 return; 0731 } 0732 pushCommand(GCode::toCommand(GCode::MCommands::M105)); 0733 } 0734 0735 void AtCore::showMessage(const QString &message) 0736 { 0737 if (!message.isEmpty()) { 0738 pushCommand(GCode::toCommand((GCode::MCommands::M117), message)); 0739 } 0740 } 0741 0742 void AtCore::setUnits(AtCore::UNITS units) 0743 { 0744 switch (units) { 0745 case AtCore::UNITS::METRIC: 0746 pushCommand(GCode::toCommand(GCode::GCommands::G21)); 0747 break; 0748 case AtCore::UNITS::IMPERIAL: 0749 pushCommand(GCode::toCommand(GCode::GCommands::G20)); 0750 break; 0751 } 0752 } 0753 0754 QStringList AtCore::portSpeeds() const 0755 { 0756 return d->serial->validBaudRates(); 0757 } 0758 0759 void AtCore::disableMotors(uint delay) 0760 { 0761 // Disables motors 0762 if (delay) { 0763 pushCommand(GCode::toCommand(GCode::MCommands::M84, QString::number(delay))); 0764 } else { 0765 pushCommand(GCode::toCommand(GCode::MCommands::M84)); 0766 } 0767 } 0768 // Most firmwares will not report if an sdcard is mounted on boot. 0769 bool AtCore::isSdMounted() const 0770 { 0771 return d->sdCardMounted; 0772 } 0773 0774 void AtCore::setSdMounted(bool mounted) 0775 { 0776 if (mounted != isSdMounted()) { 0777 d->sdCardMounted = mounted; 0778 Q_EMIT sdMountChanged(d->sdCardMounted); 0779 } 0780 } 0781 0782 void AtCore::getSDFileList() 0783 { 0784 pushCommand(GCode::toCommand(GCode::MCommands::M20)); 0785 } 0786 0787 QStringList AtCore::sdFileList() 0788 { 0789 if (!d->sdCardReadingFileList) { 0790 getSDFileList(); 0791 } 0792 return d->sdCardFileList; 0793 } 0794 0795 void AtCore::appendSdCardFileList(const QString &fileName) 0796 { 0797 d->sdCardFileList.append(fileName); 0798 Q_EMIT sdCardFileListChanged(d->sdCardFileList); 0799 } 0800 0801 void AtCore::clearSdCardFileList() 0802 { 0803 d->sdCardFileList.clear(); 0804 Q_EMIT sdCardFileListChanged(d->sdCardFileList); 0805 } 0806 0807 void AtCore::sdDelete(const QString &fileName) 0808 { 0809 if (d->sdCardFileList.contains(fileName)) { 0810 pushCommand(GCode::toCommand(GCode::MCommands::M30, fileName)); 0811 getSDFileList(); 0812 } else { 0813 qCDebug(ATCORE_CORE) << "Delete failed file not found:" << fileName; 0814 } 0815 } 0816 0817 void AtCore::mountSd(uint slot) 0818 { 0819 pushCommand(GCode::toCommand(GCode::MCommands::M21, QString::number(slot))); 0820 } 0821 0822 void AtCore::umountSd(uint slot) 0823 { 0824 pushCommand(GCode::toCommand(GCode::MCommands::M22, QString::number(slot))); 0825 } 0826 0827 bool AtCore::isReadingSdCardList() const 0828 { 0829 return d->sdCardReadingFileList; 0830 } 0831 0832 void AtCore::setReadingSdCardList(bool readingList) 0833 { 0834 d->sdCardReadingFileList = readingList; 0835 } 0836 0837 void AtCore::sdCardPrintStatus() 0838 { 0839 // One request for the Sd Job status in the queue at a time. 0840 if (d->commandQueue.contains(GCode::toCommand(GCode::MCommands::M27))) { 0841 return; 0842 } 0843 pushCommand(GCode::toCommand(GCode::MCommands::M27)); 0844 } 0845 0846 void AtCore::disableResetOnConnect(const QString &port) 0847 { 0848 #if defined(Q_OS_UNIX) 0849 // should work on all unix' 0850 QProcess process(this); 0851 QStringList args({QStringLiteral("-F/dev/%1").arg(port), QStringLiteral("-hupcl")}); 0852 process.start(QStringLiteral("stty"), args); 0853 process.waitForFinished(500); 0854 0855 connect(&process, &QProcess::errorOccurred, this, [&process] { qCDebug(ATCORE_CORE) << "Stty Error:" << process.errorString(); }); 0856 0857 #elif defined(Q_OS_WIN) 0858 // TODO: Disable hangup on windows. 0859 #endif 0860 } 0861 0862 void AtCore::handleSerialError(QSerialPort::SerialPortError error) 0863 { 0864 QString errorString; 0865 0866 switch (error) { 0867 case (QSerialPort::SerialPortError::DeviceNotFoundError): 0868 errorString = tr("Device not found"); 0869 break; 0870 case (QSerialPort::SerialPortError::WriteError): 0871 errorString = tr("Unable to write to device"); 0872 break; 0873 case (QSerialPort::SerialPortError::ReadError): 0874 errorString = tr("Unable to read from device"); 0875 break; 0876 case (QSerialPort::SerialPortError::ResourceError): 0877 case (QSerialPort::SerialPortError::TimeoutError): 0878 errorString = tr("The device no longer available"); 0879 closeConnection(); 0880 break; 0881 case (QSerialPort::SerialPortError::UnsupportedOperationError): 0882 errorString = tr("Device does not support the operation"); 0883 break; 0884 case (QSerialPort::SerialPortError::UnknownError): 0885 errorString = tr("Unknown Error"); 0886 break; 0887 default: 0888 // Not Directly processed errors 0889 // QSerialPort::NoError, No error has happened 0890 // QSerialPort::PermissionError), Already handled. 0891 // QSerialPort::OpenError), Already handled. 0892 // QSerialPort::NotOpenError, SerialLayer destroyed if not connected. 0893 // QSerialPort::ParityError, Obsolete. Qt Docs "We strongly advise against using it in new code." 0894 // QSerialPort::FramingError, Obsolete. Qt Docs "We strongly advise against using it in new code." 0895 // QSerialPort::BreakConditionError, Obsolete. Qt Docs "We strongly advise against using it in new code." 0896 return; 0897 }; // End of Switch 0898 qCDebug(ATCORE_CORE) << "SerialError:" << errorString; 0899 Q_EMIT atcoreMessage(QStringLiteral("SerialError: %1").arg(errorString)); 0900 } 0901 0902 void AtCore::updateFWPlugins() 0903 { 0904 // Attempt to find our plugins 0905 qCDebug(ATCORE_PLUGIN) << "Detecting Plugin path"; 0906 QStringList paths = AtCoreDirectories::pluginDir; 0907 qCDebug(ATCORE_PLUGIN) << "Paths" << paths; 0908 for (const auto &path : AtCoreDirectories::pluginDir) { 0909 qCDebug(ATCORE_PLUGIN) << "Checking: " << path; 0910 QMap<QString, QString> detectedPlugins; 0911 const auto pluginList = QDir(path).entryList({AtCoreDirectories::pluginExtFilter}, QDir::Files); 0912 for (const QString &f : pluginList) { 0913 QString file = f; 0914 file = file.split(QStringLiteral(".")).at(0).toLower().simplified(); 0915 if (file.startsWith(QStringLiteral("lib"))) 0916 file = file.remove(QStringLiteral("lib")); 0917 QString pluginString = QStringLiteral("%1/%2").arg(path, f); 0918 detectedPlugins.insert(file, pluginString); 0919 qCDebug(ATCORE_PLUGIN) << QStringLiteral("Plugin:[%1]=%2").arg(file, pluginString); 0920 } 0921 if (!detectedPlugins.isEmpty()) { 0922 d->plugins = detectedPlugins; 0923 Q_EMIT availableFirmwarePluginsChanged(); 0924 return; 0925 } 0926 } 0927 } 0928 0929 std::shared_ptr<BedDeform> AtCore::bedDeform() 0930 { 0931 return d->bedDeform; 0932 }