diff --git a/src/core/atcore.h b/src/core/atcore.h --- a/src/core/atcore.h +++ b/src/core/atcore.h @@ -1,5 +1,5 @@ /* AtCore - Copyright (C) <2016 - 2018> + Copyright (C) <2016 - 2019> Authors: Tomaz Canabrava @@ -42,9 +42,8 @@ * aims to provides a high level interface for serial based gcode devices
* * #### General Workflow - * - Connect to a serial port with initSerial() - * - Firmware will be auto detected. Use loadFirmwarePLugin() to force load a firmware. - * - Send commands to the device (pushCommand(),print(),...) + * - Connect to a serial port with newConnection() + * - Send commands to the device (pushCommand(), print(), ...) * - AtCore::close() when you are all done. * #### How AtCore Finds Plugins. @@ -132,7 +131,7 @@ /** * @brief Returns a List of detected serial ports * @return List of detected ports - * @sa initSerial(),serial(),closeConnection() + * @sa newConnection(), serial(), closeConnection() */ QStringList serialPorts() const; @@ -143,50 +142,44 @@ QString connectedPort() const; /** - * @brief Initialize a connection to \p port at a speed of \p baud
+ * @brief Connect to a device. * @param port: the port to initialize * @param baud: the baud of the port + * @param fwName: Firmware name of plugin to use (will try to autodetect if fwName is unknown) * @param disableROC: atcore will attempt to disable reset on connect for this device. * @return True is connection was successful - * @sa serialPorts(),serial(),closeConnection() + * @sa serialPorts(), serial(), closeConnection() */ - Q_INVOKABLE bool initSerial(const QString &port, int baud, bool disableROC = false); + Q_INVOKABLE bool newConnection(const QString &port, int baud, const QString &fwName, bool disableROC = false); /** * @brief Returns a list of valid baud speeds */ QStringList portSpeeds() const; /** * @brief Close the current serial connection - * @sa initSerial(),serial(),serialPorts(),AtCore::close() + * @sa newConnection(), serial(), serialPorts(), close() */ Q_INVOKABLE void closeConnection(); /** * @brief Main access to the loaded firmware plugin * @return IFirmware * to currently loaded plugin - * @sa availableFirmwarePlugins(),loadFirmwarePlugin() + * @sa availableFirmwarePlugins(), loadFirmwarePlugin() */ Q_INVOKABLE IFirmware *firmwarePlugin() const; /** * @brief List of available firmware plugins - * @sa loadFirmwarePlugin(),firmwarePlugin() + * @sa loadFirmwarePlugin(), firmwarePlugin() */ QStringList availableFirmwarePlugins() const; - /** - * @brief Load A firmware plugin - * @param fwName : name of the firmware - * @sa firmwarePlugin(),availableFirmwarePlugins() - */ - Q_INVOKABLE void loadFirmwarePlugin(const QString &fwName); - /** * @brief Get Printer state * @return State of the printer - * @sa setState(),stateChanged(),AtCore::STATES + * @sa setState(), stateChanged(), AtCore::STATES */ AtCore::STATES state(); @@ -290,7 +283,7 @@ /** * @brief The Printer's State Changed * @param newState : the new state of the printer - * @sa setState(),state(),AtCore::STATES + * @sa setState(), state(), AtCore::STATES */ void stateChanged(AtCore::STATES newState); @@ -320,7 +313,7 @@ /** * @brief Set the printers state * @param state : printer state. - * @sa state(),stateChanged(),AtCore::STATES + * @sa state(), stateChanged(), AtCore::STATES */ void setState(AtCore::STATES state); @@ -340,13 +333,13 @@ /** * @brief Stop the Printer by empting the queue and aborting the print job (if running) - * @sa emergencyStop(),pause(),resume() + * @sa emergencyStop(), pause(), resume() */ Q_INVOKABLE void stop(); /** * @brief stop the printer via the emergency stop Command (M112) - * @sa stop(),pause(),resume() + * @sa stop(), pause(), resume() */ Q_INVOKABLE void emergencyStop(); @@ -356,14 +349,14 @@ * Sends M114 on pause to store the location where the head stoped. * This is known to cause problems on fake printers * @param pauseActions: Gcode to run after pausing commands are ',' separated - * @sa resume(),stop(),emergencyStop() + * @sa resume(), stop(), emergencyStop() */ void pause(const QString &pauseActions); /** * @brief resume a paused print job. * After returning to location pause was triggered. - * @sa pause(),stop(),emergencyStop() + * @sa pause(), stop(), emergencyStop() */ Q_INVOKABLE void resume(); @@ -543,6 +536,14 @@ void handleSerialError(QSerialPort::SerialPortError error); private: + + /** + * @brief Load A firmware plugin + * @param fwName : name of the firmware + * @sa firmwarePlugin(), availableFirmwarePlugins() + */ + Q_INVOKABLE void loadFirmwarePlugin(const QString &fwName); + /** * @brief True if a firmware plugin is loaded */ @@ -573,10 +574,17 @@ /** * @brief stops print just for sd prints used internally - * @sa stop(),emergencyStop() + * @sa stop(), emergencyStop() */ void stopSdPrint(); + /** + * @brief New connections need to wait for the machine to be ready if it needs reboot + * @param message: message from the firmware + * @param fwName: fwName to load + */ + void waitForPrinterReboot(const QByteArray &message, const QString &fwName); + /** * @brief Hold private data of AtCore. */ diff --git a/src/core/atcore.cpp b/src/core/atcore.cpp --- a/src/core/atcore.cpp +++ b/src/core/atcore.cpp @@ -1,5 +1,5 @@ /* AtCore - Copyright (C) <2016 - 2018> + Copyright (C) <2016 - 2019> Authors: Tomaz Canabrava @@ -149,30 +149,8 @@ void AtCore::findFirmware(const QByteArray &message) { - if (state() == AtCore::STATES::DISCONNECTED) { - qCWarning(ATCORE_CORE) << tr("Cant find firwmware, serial not connected!"); - return; - } - - if (state() == AtCore::STATES::CONNECTING) { - //Most Firmwares will return "start" on connect, some return their firmware name. - if (message.contains("start")) { - qCDebug(ATCORE_CORE) << "Waiting requestFirmware."; - QTimer::singleShot(500, this, &AtCore::requestFirmware); - return; - } - if (message.contains("Grbl")) { - loadFirmwarePlugin(QString::fromLatin1("grbl")); - return; - } - if (message.contains("Smoothie")) { - loadFirmwarePlugin(QString::fromLatin1("smoothie")); - return; - } - - qCDebug(ATCORE_CORE) << "Waiting for firmware detect."; - emit atcoreMessage(tr("Waiting for firmware detect.")); - } + emit receivedMessage(message); + emit atcoreMessage(tr("Waiting for firmware detect.")); qCDebug(ATCORE_CORE) << "Find Firmware: " << message; if (!message.contains("FIRMWARE_NAME:")) { qCDebug(ATCORE_CORE) << "No firmware yet."; @@ -218,40 +196,71 @@ //Plugin was loaded successfully. d->firmwarePlugin = qobject_cast(d->pluginLoader.instance()); firmwarePlugin()->init(this); - disconnect(d->serial, &SerialLayer::receivedCommand, this, &AtCore::findFirmware); + disconnect(d->serial, &SerialLayer::receivedCommand, this, {}); connect(d->serial, &SerialLayer::receivedCommand, this, &AtCore::newMessage); connect(firmwarePlugin(), &IFirmware::readyForCommand, this, &AtCore::processQueue); d->ready = true; // ready on new firmware load if (firmwarePlugin()->name() != QStringLiteral("Grbl")) { setTemperatureTimerInterval(5000); } setState(IDLE); + emit atcoreMessage(tr("Connected to printer using %1 plugin").arg(d->firmwarePlugin->name())); } } else { qCDebug(ATCORE_CORE) << "Plugin:" << fwName << ": Not found."; emit atcoreMessage(tr("No plugin found for %1.").arg(fwName)); } } -bool AtCore::initSerial(const QString &port, int baud, bool disableROC) +void AtCore::waitForPrinterReboot(const QByteArray &message, const QString &fwName) +{ + emit receivedMessage(message); + if (message.isEmpty()) { + return; + } + + if (message.contains("Grbl")) { + loadFirmwarePlugin(QString::fromLatin1("grbl")); + } else if (message.contains("Smoothie")) { + loadFirmwarePlugin(QString::fromLatin1("smoothie")); + } else if (message.contains("start")) { + if (!d->plugins.contains(fwName)) { + disconnect(d->serial, &SerialLayer::receivedCommand, this, {}); + connect(d->serial, &SerialLayer::receivedCommand, this, &AtCore::findFirmware); + QTimer::singleShot(500, this, &AtCore::requestFirmware); + } else { + loadFirmwarePlugin(fwName); + } + } + +} + +bool AtCore::newConnection(const QString &port, int baud, const QString &fwName, bool disableROC) { if (disableROC) { disableResetOnConnect(port); } d->serial = new SerialLayer(port, baud, this); connect(d->serial, &SerialLayer::serialError, this, &AtCore::handleSerialError); if (serialInitialized() && d->serial->isWritable()) { - setState(AtCore::CONNECTING); + setState(AtCore::STATES::CONNECTING); connect(d->serial, &SerialLayer::pushedCommand, this, &AtCore::newCommand); - connect(d->serial, &SerialLayer::receivedCommand, this, &AtCore::findFirmware); + + if (!disableROC) { + emit atcoreMessage(tr("Waiting for machine restart")); + connect(d->serial, &SerialLayer::receivedCommand, this, [this, fwName](const QByteArray & message) { + waitForPrinterReboot(message, fwName); + }); + } else { + loadFirmwarePlugin(fwName); + } d->serialTimer.stop(); return true; } qCDebug(ATCORE_CORE) << "Failed to open device for Read / Write."; emit atcoreMessage(tr("Failed to open device in read/write mode.")); return false; - } bool AtCore::serialInitialized() const diff --git a/testclient/mainwindow.h b/testclient/mainwindow.h --- a/testclient/mainwindow.h +++ b/testclient/mainwindow.h @@ -1,5 +1,5 @@ /* AtCore Test Client - Copyright (C) <2016> + Copyright (C) <2016 - 2019> Authors: Patrick José Pereira @@ -79,6 +79,11 @@ */ void toggleDockTitles(bool checked); + /** + * @brief set enabled for connection option widgets (port, baud and fw selector) + * @param enbled: true if enabled. + */ + void setConnectionWidgetsEnabled(bool enbled); /** * @brief Toggle disable state of dangerous docks * Command, Sd Card, Movement, Temperature Controls and Printing docks @@ -98,11 +103,6 @@ */ void locateSerialPort(const QStringList &ports); - /** - * @brief pluginCB index changed - */ - void pluginCBChanged(const QString ¤tText); - /** * @brief setupActions for KXMLGui */ diff --git a/testclient/mainwindow.cpp b/testclient/mainwindow.cpp --- a/testclient/mainwindow.cpp +++ b/testclient/mainwindow.cpp @@ -1,5 +1,5 @@ /* AtCore Test Client - Copyright (C) <2016 - 2018> + Copyright (C) <2016 - 2019> Authors: Patrick José Pereira @@ -267,7 +267,6 @@ comboPlugin = new QComboBox; comboPlugin->addItem(tr("Autodetect")); comboPlugin->addItems(core->availableFirmwarePlugins()); - connect(comboPlugin, &QComboBox::currentTextChanged, this, &MainWindow::pluginCBChanged); hBoxLayout = new QHBoxLayout; hBoxLayout->addWidget(newLabel); @@ -279,7 +278,7 @@ mainLayout->addWidget(cbReset); connect(comboPlugin, &QComboBox::currentTextChanged, this, [this](const QString & currentText) { - cbReset->setHidden(currentText == tr("Autodetect")); + cbReset->setHidden(!core->availableFirmwarePlugins().contains(currentText)); }); buttonConnect = new QPushButton(tr("Connect")); @@ -456,12 +455,11 @@ void MainWindow::connectPBClicked() { if (core->state() == AtCore::DISCONNECTED) { - if (core->initSerial(comboPort->currentText(), comboBAUD->currentText().toInt(), cbReset->isChecked())) { + if (core->newConnection(comboPort->currentText(), comboBAUD->currentText().toInt(), comboPlugin->currentText(), cbReset->isChecked())) { connect(core, &AtCore::receivedMessage, logWidget, &LogWidget::appendRLog); connect(core, &AtCore::pushedCommand, logWidget, &LogWidget::appendSLog); logWidget->appendLog(tr("Serial connected")); - if (!comboPlugin->currentText().contains(tr("Autodetect"))) { - core->loadFirmwarePlugin(comboPlugin->currentText()); + if (core->availableFirmwarePlugins().contains(comboPlugin->currentText())) { if (cbReset->isChecked()) { //Wait a few seconds after connect to avoid the normal errors QTimer::singleShot(5000, core, &AtCore::sdCardPrintStatus); @@ -514,16 +512,6 @@ } } -void MainWindow::pluginCBChanged(const QString ¤tText) -{ - if (core->state() != AtCore::DISCONNECTED) { - if (!currentText.contains(tr("Autodetect"))) { - core->loadFirmwarePlugin(currentText); - sdDock->setVisible(core->firmwarePlugin()->isSdSupported()); - } - } -} - void MainWindow::printerStateChanged(AtCore::STATES state) { QString stateString; @@ -566,13 +554,15 @@ } stateString = QStringLiteral("Not Connected"); buttonConnect->setText(tr("Connect")); + setConnectionWidgetsEnabled(true); setDangeriousDocksDisabled(true); break; case AtCore::CONNECTING: stateString = QStringLiteral("Connecting"); buttonConnect->setText(tr("Abort")); connectionTimer->start(); + setConnectionWidgetsEnabled(false); setDangeriousDocksDisabled(false); break; @@ -626,3 +616,10 @@ statusWidget->showPrintArea(false); } } + +void MainWindow::setConnectionWidgetsEnabled(bool enabled) +{ + comboBAUD->setEnabled(enabled); + comboPlugin->setEnabled(enabled); + comboPort->setEnabled(enabled); +} diff --git a/unittests/atcoretests.cpp b/unittests/atcoretests.cpp --- a/unittests/atcoretests.cpp +++ b/unittests/atcoretests.cpp @@ -1,7 +1,7 @@ /* This file is part of the KDE project - Copyright (C) 2017 Chris Rizzitello + Copyright (C) 2017 - 2019 Chris Rizzitello This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -58,7 +58,7 @@ void AtCoreTests::testConnectInvalidDevice() { QEXPECT_FAIL("", "Invalid Device Attempt", Continue); - QVERIFY(core->initSerial(QStringLiteral("/dev/ptyp5"), 9600)); + QVERIFY(core->newConnection(QStringLiteral("/dev/ptyp5"), 9600, QStringLiteral("FirmwareNAME"))); } void AtCoreTests::testStateChange()