diff --git a/src/core/atcore.cpp b/src/core/atcore.cpp index 1f84746..058123c 100644 --- a/src/core/atcore.cpp +++ b/src/core/atcore.cpp @@ -1,881 +1,890 @@ /* AtCore - Copyright (C) <2016 - 2018> + Copyright (C) <2016 - 2019> Authors: Tomaz Canabrava Chris Rizzitello Patrick José Pereira Lays Rodrigues This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include "atcore.h" #include "atcore_version.h" #include "seriallayer.h" #include "gcodecommands.h" #include "printthread.h" #include "atcore_default_folders.h" Q_LOGGING_CATEGORY(ATCORE_PLUGIN, "org.kde.atelier.core.plugin") Q_LOGGING_CATEGORY(ATCORE_CORE, "org.kde.atelier.core") /** * @brief The AtCorePrivate struct * Provides a private data set for atcore. */ struct AtCore::AtCorePrivate { /** firmwarePlugin: pointer to firmware plugin */ IFirmware *firmwarePlugin = nullptr; /** serial: pointer to the serial layer */ SerialLayer *serial = nullptr; /** pluginLoader: QPluginLoader */ QPluginLoader pluginLoader; /** plugins: Map of plugins name / path */ QMap plugins; /** lastMessage: lastMessage from the printer */ QByteArray lastMessage; /** extruderCount: extruder count */ int extruderCount = 1; /** temperature: Temperature object */ Temperature temperature; /** commandQueue: the list of commands to send to the printer */ QStringList commandQueue; /** ready: True if printer is ready for a command */ bool ready = false; /** temperatureTimer: timer connected to the checkTemperature function */ QTimer temperatureTimer; /** sdPrintProgressTimer: timer used to check in on sdJobs */ QTimer sdPrintProgressTimer; /** percentage: print job percent */ float percentage = 0.0; /** posString: stored string from last M114 return */ QByteArray posString; /** printerState: State of the Printer */ AtCore::STATES printerState = AtCore::DISCONNECTED; /** seralPorts: Detected serial Ports */ QStringList serialPorts; /** serialTimer: Timer connected to locateSerialPorts */ QTimer serialTimer; /** sdCardMounted: True if Sd Card is mounted. */ bool sdCardMounted = false; /** sdCardReadingFileList: True while getting file names from sd card */ bool sdCardReadingFileList = false; /** sdCardPrinting: True if currently printing from sd card. */ bool sdCardPrinting = false; /** sdCardFileName: name of file being used from sd card. */ QString sdCardFileName; /** sdCardFileList: List of files on sd card. */ QStringList sdCardFileList; }; AtCore::AtCore(QObject *parent) : QObject(parent), d(new AtCorePrivate) { //Register MetaTypes qRegisterMetaType("AtCore::STATES"); setState(AtCore::STATES::DISCONNECTED); //Connect our Timers connect(&d->sdPrintProgressTimer, &QTimer::timeout, this, &AtCore::sdCardPrintStatus); connect(&d->serialTimer, &QTimer::timeout, this, &AtCore::locateSerialPort); connect(&d->temperatureTimer, &QTimer::timeout, this, &AtCore::checkTemperature); //Attempt to find our plugins qCDebug(ATCORE_PLUGIN) << "Detecting Plugin path"; QStringList paths = AtCoreDirectories::pluginDir; //add our current runtime path paths.prepend(qApp->applicationDirPath() + QStringLiteral("/../Plugins/AtCore")); paths.prepend(qApp->applicationDirPath() + QStringLiteral("/AtCore")); paths.prepend(qApp->applicationDirPath() + QStringLiteral("/plugins")); for (const auto &path : paths) { qCDebug(ATCORE_PLUGIN) << "Checking: " << path; QMap tempMap = findFirmwarePlugins(path); if (!tempMap.isEmpty()) { d->plugins = tempMap; return; } } setState(AtCore::STATES::DISCONNECTED); } QString AtCore::version() const { QString versionString = QString::fromLatin1(ATCORE_VERSION_STRING); #if defined GIT_REVISION if (!QStringLiteral(GIT_REVISION).isEmpty()) { versionString.append(QString::fromLatin1("-%1").arg(QStringLiteral(GIT_REVISION))); } #endif return versionString; } IFirmware *AtCore::firmwarePlugin() const { return d->firmwarePlugin; } void AtCore::close() { exit(0); } Temperature &AtCore::temperature() const { return d->temperature; } 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."; return; } qCDebug(ATCORE_CORE) << "Found firmware string, Looking for Firmware Name."; QString fwName = QString::fromLocal8Bit(message); fwName = fwName.split(QChar::fromLatin1(':')).at(1); if (fwName.indexOf(QChar::fromLatin1(' ')) == 0) { //remove leading space fwName.remove(0, 1); } if (fwName.contains(QChar::fromLatin1(' '))) { //check there is a space or dont' resize fwName.resize(fwName.indexOf(QChar::fromLatin1(' '))); } fwName = fwName.toLower().simplified(); if (fwName.contains(QChar::fromLatin1('_'))) { fwName.resize(fwName.indexOf(QChar::fromLatin1('_'))); } qCDebug(ATCORE_CORE) << "Firmware Name:" << fwName; if (message.contains("EXTRUDER_COUNT:")) { //this code is broken if more then 9 extruders are detected. since only one char is returned setExtruderCount(message.at(message.indexOf("EXTRUDER_COUNT:") + 15) - '0'); } loadFirmwarePlugin(fwName); } void AtCore::loadFirmwarePlugin(const QString &fwName) { qCDebug(ATCORE_CORE) << "Loading plugin: " << d->plugins[fwName]; if (d->plugins.contains(fwName)) { d->pluginLoader.setFileName(d->plugins[fwName]); if (!d->pluginLoader.load()) { //Plugin was not loaded, Provide some debug info. qCDebug(ATCORE_CORE) << "Plugin Loading: Failed."; qCDebug(ATCORE_CORE) << d->pluginLoader.errorString(); setState(AtCore::STATES::CONNECTING); } else { //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 { if (!d->serial) { return false; } return d->serial->isOpen(); } QString AtCore::connectedPort() const { return d->serial->portName(); } QStringList AtCore::serialPorts() const { QStringList ports; QList serialPortInfoList = QSerialPortInfo::availablePorts(); if (!serialPortInfoList.isEmpty()) { for (const QSerialPortInfo &serialPortInfo : serialPortInfoList) { #ifdef Q_OS_MAC //Mac OS has callout serial ports starting with cu these devices are read only. //It is necessary to filter them out to help prevent user error. if (!serialPortInfo.portName().startsWith(QStringLiteral("cu."), Qt::CaseInsensitive)) { ports.append(serialPortInfo.portName()); } #else ports.append(serialPortInfo.portName()); #endif } } return ports; } void AtCore::locateSerialPort() { QStringList ports = serialPorts(); if (d->serialPorts != ports) { d->serialPorts = ports; emit portsChanged(d->serialPorts); } } int AtCore::serialTimerInterval() const { return d->serialTimer.interval(); } void AtCore::setSerialTimerInterval(int newTime) { newTime = std::max(newTime, 0); if (newTime != d->serialTimer.interval()) { emit serialTimerIntervalChanged(newTime); } d->serialTimer.start(newTime); } int AtCore::temperatureTimerInterval() const { return d->temperatureTimer.interval(); } void AtCore::setTemperatureTimerInterval(int newTime) { newTime = std::max(newTime, 0); if (newTime != d->temperatureTimer.interval()) { emit temperatureTimerIntervalChanged(newTime); } d->temperatureTimer.start(newTime); } void AtCore::newMessage(const QByteArray &message) { //Evaluate the messages coming from the printer. d->lastMessage = message; //Check if the message has current coordinates. if (message.startsWith(QString::fromLatin1("X:").toLocal8Bit())) { d->posString = message; d->posString.resize(d->posString.indexOf('E')); d->posString.replace(':', ""); } //Check if have temperature info and decode it if (d->lastMessage.contains("T:") || d->lastMessage.contains("B:")) { temperature().decodeTemp(message); } emit receivedMessage(d->lastMessage); } void AtCore::newCommand(const QByteArray &command) { emit pushedCommand(command); } void AtCore::setRelativePosition() { pushCommand(GCode::toCommand(GCode::GCommands::G91)); } void AtCore::setAbsolutePosition() { pushCommand(GCode::toCommand(GCode::GCommands::G90)); } float AtCore::percentagePrinted() const { return d->percentage; } void AtCore::print(const QString &fileName, bool sdPrint) { if (state() == AtCore::STATES::CONNECTING) { qCDebug(ATCORE_CORE) << "Load a firmware plugin to print."; return; } //Start a print job. setState(AtCore::STATES::STARTPRINT); //Only try to print from Sd if the firmware has support for sd cards if (firmwarePlugin()->isSdSupported()) { if (sdPrint) { //Printing from the sd card requires us to send some M commands. pushCommand(GCode::toCommand(GCode::MCommands::M23, fileName)); d->sdCardFileName = fileName; pushCommand(GCode::toCommand(GCode::MCommands::M24)); setState(AtCore::STATES::BUSY); d->sdCardPrinting = true; d->sdPrintProgressTimer.start(5000); return; } } //Process the gcode with a printThread. //The Thread processes the gcode without freezing the libary. //Only sends a command back when the printer is ready, avoiding buffer overflow in the printer. auto thread = new QThread(this); auto printThread = new PrintThread(this, fileName); printThread->moveToThread(thread); connect(printThread, &PrintThread::printProgressChanged, this, &AtCore::printProgressChanged, Qt::QueuedConnection); connect(thread, &QThread::started, printThread, &PrintThread::start); connect(printThread, &PrintThread::finished, thread, &QThread::quit); connect(thread, &QThread::finished, printThread, &PrintThread::deleteLater); if (!thread->isRunning()) { thread->start(); } } void AtCore::pushCommand(const QString &comm) { //Be sure our M112 is first in the queue. if (comm == GCode::toCommand(GCode::MCommands::M112)) { d->commandQueue.prepend(comm); } else { d->commandQueue.append(comm); } if (d->ready) { //The printer is ready for a command now so push one. processQueue(); } } void AtCore::closeConnection() { if (serialInitialized()) { if (AtCore::state() == AtCore::STATES::BUSY && !d->sdCardPrinting) { //We have to clean up the print job if printing from the host. //However disconnecting while printing from sd card should not affect the print job. setState(AtCore::STATES::STOP); } if (firmwarePluginLoaded()) { disconnect(firmwarePlugin(), &IFirmware::readyForCommand, this, &AtCore::processQueue); disconnect(d->serial, &SerialLayer::receivedCommand, this, &AtCore::newMessage); if (firmwarePlugin()->name() != QStringLiteral("Grbl")) { setTemperatureTimerInterval(0); } //Attempt to unload the firmware plugin. QString name = firmwarePlugin()->name(); QString msg = d->pluginLoader.unload() ? QStringLiteral("closed.") : QStringLiteral("Failed to close."); qCDebug(ATCORE_CORE) << QStringLiteral("Firmware plugin %1 %2").arg(name, msg); d->firmwarePlugin = nullptr; } //Do not reset the connect on disconnect when closing this will cause a reset on connect for the next connection. disconnect(d->serial, &SerialLayer::serialError, this, &AtCore::handleSerialError); disconnect(d->serial, &SerialLayer::pushedCommand, this, &AtCore::newMessage); d->serial->close(); //Clear our copy of the sdcard filelist clearSdCardFileList(); setState(AtCore::STATES::DISCONNECTED); d->serialTimer.start(); } } AtCore::STATES AtCore::state() { return d->printerState; } void AtCore::setState(AtCore::STATES state) { if (state != d->printerState) { qCDebug(ATCORE_CORE) << QStringLiteral("Atcore state changed from [%1] to [%2]") .arg(QVariant::fromValue(d->printerState).toString(), QVariant::fromValue(state).toString()); d->printerState = state; if (state == AtCore::STATES::FINISHEDPRINT && d->sdCardPrinting) { //Clean up the sd card print d->sdCardPrinting = false; d->sdPrintProgressTimer.stop(); } emit stateChanged(d->printerState); } } void AtCore::stop() { //Stop a print job setState(AtCore::STATES::STOP); d->commandQueue.clear(); if (d->sdCardPrinting) { stopSdPrint(); } setExtruderTemp(0, 0); setBedTemp(0); home(AtCore::AXES::X); } void AtCore::emergencyStop() { //Emergency Stop. Stops the machine //Clear the queue, and any print job //Before sending the command to ensure //Less chance of movement after the restart. d->commandQueue.clear(); if (AtCore::state() == AtCore::STATES::BUSY) { if (!d->sdCardPrinting) { //Stop our running print thread setState(AtCore::STATES::STOP); } } pushCommand(GCode::toCommand(GCode::MCommands::M112)); } void AtCore::stopSdPrint() { //Stop an SdCard Print. pushCommand(GCode::toCommand(GCode::MCommands::M25)); d->sdCardFileName = QString(); pushCommand(GCode::toCommand(GCode::MCommands::M23, d->sdCardFileName)); AtCore::setState(AtCore::STATES::FINISHEDPRINT); AtCore::setState(AtCore::STATES::IDLE); } void AtCore::requestFirmware() { if (serialInitialized()) { //ensure M115 is sent on cold connect. d->ready = true; qCDebug(ATCORE_CORE) << "Sending " << GCode::description(GCode::MCommands::M115); pushCommand(GCode::toCommand(GCode::MCommands::M115)); } else { qCDebug(ATCORE_CORE) << "There is no open device to send commands"; } } bool AtCore::firmwarePluginLoaded() const { return firmwarePlugin(); } QMap AtCore::findFirmwarePlugins(const QString &path) { QMap detectedPlugins; QStringList files = QDir(path).entryList(QDir::Files); for (const QString &f : files) { QString file = f; #if defined(Q_OS_WIN) if (file.endsWith(QStringLiteral(".dll"))) #elif defined(Q_OS_MAC) if (file.endsWith(QStringLiteral(".dylib"))) #else if (file.endsWith(QStringLiteral(".so"))) #endif file = file.split(QChar::fromLatin1('.')).at(0); else { continue; } if (file.startsWith(QStringLiteral("lib"))) { file = file.remove(QStringLiteral("lib")); } file = file.toLower().simplified(); QString pluginString = path; pluginString.append(QChar::fromLatin1('/')); pluginString.append(f); detectedPlugins[file] = pluginString; qCDebug(ATCORE_PLUGIN) << QStringLiteral("Plugin:[%1]=%2").arg(file, pluginString); } return detectedPlugins; } QStringList AtCore::availableFirmwarePlugins() const { return d->plugins.keys(); } void AtCore::pause(const QString &pauseActions) { if (d->sdCardPrinting) { pushCommand(GCode::toCommand(GCode::MCommands::M25)); } //Push the command to request current coordinates. //This will be read by AtCore::newMessage and stored for use on resume. pushCommand(GCode::toCommand(GCode::MCommands::M114)); if (!pauseActions.isEmpty()) { QStringList temp = pauseActions.split(QChar::fromLatin1(',')); for (int i = 0; i < temp.length(); i++) { pushCommand(temp.at(i)); } } setState(AtCore::PAUSE); } void AtCore::resume() { if (d->sdCardPrinting) { pushCommand(GCode::toCommand(GCode::MCommands::M24)); } else { //Move back to previous coordinates. pushCommand(GCode::toCommand(GCode::GCommands::G0, QString::fromLatin1(d->posString))); } setState(AtCore::BUSY); } /*~~~~~Control Slots ~~~~~~~~*/ void AtCore::home() { pushCommand(GCode::toCommand(GCode::GCommands::G28)); } void AtCore::home(uchar axis) { QString args; if (axis & AtCore::AXES::X) { args.append(QStringLiteral("X0 ")); } if (axis & AtCore::AXES::Y) { args.append(QStringLiteral("Y0 ")); } if (axis & AtCore::AXES::Z) { args.append(QStringLiteral("Z0")); } pushCommand(GCode::toCommand(GCode::GCommands::G28, args)); } void AtCore::setExtruderTemp(uint temp, uint extruder, bool andWait) { temp = std::min(temp, 10000); extruder = std::min(extruder, 10000); if (andWait) { pushCommand(GCode::toCommand(GCode::MCommands::M109, QString::number(temp), QString::number(extruder))); } else { pushCommand(GCode::toCommand(GCode::MCommands::M104, QString::number(extruder), QString::number(temp))); } } void AtCore::setBedTemp(uint temp, bool andWait) { temp = std::min(temp, 10000); if (andWait) { pushCommand(GCode::toCommand(GCode::MCommands::M190, QString::number(temp))); } else { pushCommand(GCode::toCommand(GCode::MCommands::M140, QString::number(temp))); } } void AtCore::setFanSpeed(uint speed, uint fanNumber) { speed = std::min(speed, 10000); fanNumber = std::min(fanNumber, 10000); pushCommand(GCode::toCommand(GCode::MCommands::M106, QString::number(fanNumber), QString::number(speed))); } void AtCore::setPrinterSpeed(uint speed) { speed = std::min(speed, 10000); pushCommand(GCode::toCommand(GCode::MCommands::M220, QString::number(speed))); } void AtCore::setFlowRate(uint speed) { speed = std::min(speed, 10000); pushCommand(GCode::toCommand(GCode::MCommands::M221, QString::number(speed))); } void AtCore::move(AtCore::AXES axis, double arg) { const auto axisAsString = QMetaEnum::fromType().valueToKey(axis); move(QLatin1Char(axisAsString[0]), arg); } void AtCore::move(QLatin1Char axis, double arg) { //Using QString::number(double, format, precision) //f = 'format as [-]9.9' //3 = use 3 decimal precision pushCommand(GCode::toCommand(GCode::GCommands::G1, QStringLiteral("%1 %2").arg(axis).arg(QString::number(arg, 'f', 3)))); } int AtCore::extruderCount() const { return d->extruderCount; } void AtCore::setExtruderCount(int newCount) { if (d->extruderCount != newCount && newCount >= 1) { d->extruderCount = newCount; emit extruderCountChanged(newCount); qCDebug(ATCORE_CORE) << "Extruder Count:" << QString::number(extruderCount()); } } void AtCore::processQueue() { d->ready = true; if (d->commandQueue.isEmpty()) { return; } if (!serialInitialized()) { qCDebug(ATCORE_PLUGIN) << "Can't process queue ! Serial not initialized."; return; } QString text = d->commandQueue.takeAt(0); if (firmwarePluginLoaded()) { d->serial->pushCommand(firmwarePlugin()->translate(text)); } else { d->serial->pushCommand(text.toLocal8Bit()); } d->ready = false; } void AtCore::checkTemperature() { //One request for the temperature in the queue at a time. if (d->commandQueue.contains(GCode::toCommand(GCode::MCommands::M105))) { return; } pushCommand(GCode::toCommand(GCode::MCommands::M105)); } void AtCore::showMessage(const QString &message) { if (!message.isEmpty()) { pushCommand(GCode::toCommand((GCode::MCommands::M117), message)); } } void AtCore::setUnits(AtCore::UNITS units) { switch (units) { case AtCore::UNITS::METRIC: pushCommand(GCode::toCommand(GCode::GCommands::G21)); break; case AtCore::UNITS::IMPERIAL: pushCommand(GCode::toCommand(GCode::GCommands::G20)); break; } } QStringList AtCore::portSpeeds() const { return d->serial->validBaudRates(); } void AtCore::disableMotors(uint delay) { //Disables motors if (delay) { pushCommand(GCode::toCommand(GCode::MCommands::M84, QString::number(delay))); } else { pushCommand(GCode::toCommand(GCode::MCommands::M84)); } } //Most firmwares will not report if an sdcard is mounted on boot. bool AtCore::isSdMounted() const { return d->sdCardMounted; } void AtCore::setSdMounted(bool mounted) { if (mounted != isSdMounted()) { d->sdCardMounted = mounted; emit sdMountChanged(d->sdCardMounted); } } void AtCore::getSDFileList() { pushCommand(GCode::toCommand(GCode::MCommands::M20)); } QStringList AtCore::sdFileList() { if (!d->sdCardReadingFileList) { getSDFileList(); } return d->sdCardFileList; } void AtCore::appendSdCardFileList(const QString &fileName) { d->sdCardFileList.append(fileName); emit sdCardFileListChanged(d->sdCardFileList); } void AtCore::clearSdCardFileList() { d->sdCardFileList.clear(); emit sdCardFileListChanged(d->sdCardFileList); } void AtCore::sdDelete(const QString &fileName) { if (d->sdCardFileList.contains(fileName)) { pushCommand(GCode::toCommand(GCode::MCommands::M30, fileName)); getSDFileList(); } else { qCDebug(ATCORE_CORE) << "Delete failed file not found:" << fileName; } } void AtCore::mountSd(uint slot) { pushCommand(GCode::toCommand(GCode::MCommands::M21, QString::number(slot))); } void AtCore::umountSd(uint slot) { pushCommand(GCode::toCommand(GCode::MCommands::M22, QString::number(slot))); } bool AtCore::isReadingSdCardList() const { return d->sdCardReadingFileList; } void AtCore::setReadingSdCardList(bool readingList) { d->sdCardReadingFileList = readingList; } void AtCore::sdCardPrintStatus() { //One request for the Sd Job status in the queue at a time. if (d->commandQueue.contains(GCode::toCommand(GCode::MCommands::M27))) { return; } pushCommand(GCode::toCommand(GCode::MCommands::M27)); } void AtCore::disableResetOnConnect(const QString &port) { #if defined(Q_OS_UNIX) //should work on all unix' QProcess process(this); QStringList args({QStringLiteral("-F/dev/%1").arg(port), QStringLiteral("-hupcl")}); process.start(QStringLiteral("stty"), args); process.waitForFinished(500); connect(&process, &QProcess::errorOccurred, this, [&process] { qCDebug(ATCORE_CORE) << "Stty Error:" << process.errorString(); }); #elif defined(Q_OS_WIN) //TODO: Disable hangup on windows. #endif } void AtCore::handleSerialError(QSerialPort::SerialPortError error) { QString errorString; switch (error) { case (QSerialPort::SerialPortError::DeviceNotFoundError): errorString = tr("Device not found"); break; case (QSerialPort::SerialPortError::WriteError): errorString = tr("Unable to write to device"); break; case (QSerialPort::SerialPortError::ReadError): errorString = tr("Unable to read from device"); break; case (QSerialPort::SerialPortError::ResourceError): case (QSerialPort::SerialPortError::TimeoutError): errorString = tr("The device no longer available"); closeConnection(); break; case (QSerialPort::SerialPortError::UnsupportedOperationError): errorString = tr("Device does not support the operation"); break; case (QSerialPort::SerialPortError::UnknownError): errorString = tr("Unknown Error"); break; default: //Not Directly processed errors //QSerialPort::NoError, No error has happened //QSerialPort::PermissionError), Already handled. //QSerialPort::OpenError), Already handled. //QSerialPort::NotOpenError, SerialLayer destroyed if not connected. //QSerialPort::ParityError, Obsolete. Qt Docs "We strongly advise against using it in new code." //QSerialPort::FramingError, Obsolete. Qt Docs "We strongly advise against using it in new code." //QSerialPort::BreakConditionError, Obsolete. Qt Docs "We strongly advise against using it in new code." return; };//End of Switch qCDebug(ATCORE_CORE) << "SerialError:" << errorString; emit atcoreMessage(QStringLiteral("SerialError: %1").arg(errorString)); } diff --git a/src/core/atcore.h b/src/core/atcore.h index d183c3c..54b3990 100644 --- a/src/core/atcore.h +++ b/src/core/atcore.h @@ -1,615 +1,623 @@ /* AtCore - Copyright (C) <2016 - 2018> + Copyright (C) <2016 - 2019> Authors: Tomaz Canabrava Chris Rizzitello Patrick José Pereira Lays Rodrigues This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #pragma once #include #include #include #include "ifirmware.h" #include "temperature.h" #include "atcore_export.h" class SerialLayer; class IFirmware; class QTime; /** * @brief The AtCore class * 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. * AtCore will check each directory below for plugins. * 1. QApplication::applicationDirPath/plugins (runtime) * 2. QApplication::applicationDirPath/AtCore (runtime) * 3. QApplication::applicationDirPath/../PlugIns/AtCore (runtime) * 4. Fullpath of KDE_PLUGIN_DIR (buildtime) * 5. Qt Plugin path/AtCore (runtime) * 6. ECM set KDE PLUGIN DIR (buildtime) * 7. Build Dir/plugins (buildtime) */ class ATCORE_EXPORT AtCore : public QObject { Q_OBJECT Q_PROPERTY(QString version READ version) Q_PROPERTY(QStringList availableFirmwarePlugins READ availableFirmwarePlugins) Q_PROPERTY(int extruderCount READ extruderCount WRITE setExtruderCount NOTIFY extruderCountChanged) Q_PROPERTY(int temperatureTimerInterval READ temperatureTimerInterval WRITE setTemperatureTimerInterval NOTIFY temperatureTimerIntervalChanged); Q_PROPERTY(int serialTimerInterval READ serialTimerInterval WRITE setSerialTimerInterval NOTIFY serialTimerIntervalChanged) Q_PROPERTY(QStringList serialPorts READ serialPorts NOTIFY portsChanged) Q_PROPERTY(float percentagePrinted READ percentagePrinted NOTIFY printProgressChanged) Q_PROPERTY(QStringList portSpeeds READ portSpeeds) Q_PROPERTY(QString connectedPort READ connectedPort) Q_PROPERTY(AtCore::STATES state READ state WRITE setState NOTIFY stateChanged) Q_PROPERTY(bool sdMount READ isSdMounted WRITE setSdMounted NOTIFY sdMountChanged) Q_PROPERTY(QStringList sdFileList READ sdFileList NOTIFY sdCardFileListChanged) friend class AtCoreTests; //Add friends as Sd Card support is extended to more plugins. friend class RepetierPlugin; friend class MarlinPlugin; //friend class SmoothiePlugin; //friend class TeacupPlugin; //friend class AprinterPlugin; //friend class SprinterPlugin; public: /** * @brief STATES enum Possible states the printer can be in */ enum STATES { DISCONNECTED, //!< Not Connected to a printer, initial state CONNECTING, //! + * @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(); /** * @brief extruderCount * @return The number of detected Extruders Default is 1 * @sa setExtruderCount(int newCount), extruderCountChanged(int newCount) */ int extruderCount() const; /** * @brief Return printed percentage * @sa printProgressChanged() */ float percentagePrinted() const; /** * @brief The temperature of the current hotend as told by the Firmware. */ Temperature &temperature() const; /** * @brief Return the amount of miliseconds the serialTimer is set to. 0 = Disabled */ int serialTimerInterval() const; /** * @brief Return the amount of miliseconds the temperatureTimer is set to. 0 = Disabled */ int temperatureTimerInterval() const; /** * @brief Attempt to Mount an sd card * @param slot: Sd card Slot on machine (0 is default) */ Q_INVOKABLE void mountSd(uint slot = 0); /** * @brief Attempt to Unmount an sd card * @param slot: Sd card Slot on machine (0 is default) */ Q_INVOKABLE void umountSd(uint slot = 0); /** * @brief sdFileList * @return List of files on the sd card. */ QStringList sdFileList(); /** * @brief Check if an sd card is mounted on the printer * @return True if card mounted */ bool isSdMounted() const; signals: /** * @brief Message emit from atcore these should be displayed to the user for debug. * * Possable Messages Are: * - Waiting for firmware detect. * - No Plugin found for (detected FW) * - Failed to open device in Read / Write mode. * - Device Errors. * @param msg: the message. */ void atcoreMessage(const QString &msg); /** * @brief New number of extruders * @sa extruderCount(), setExtruderCount(int newCount) */ void extruderCountChanged(const int newCount); /** * @brief Print job's precentage changed. * @param newProgress : Message * @sa percentagePrinted() */ void printProgressChanged(const float newProgress); /** * @brief New message was received from the printer * @param message: Message that was received */ void receivedMessage(const QByteArray &message); /** * @brief New interval for serial timer * @sa setSerialTimerInterval() */ void serialTimerIntervalChanged(const int newTime); /** * @brief New interval for temperature timer * @sa setTemperatureTimerInterval() */ void temperatureTimerIntervalChanged(const int newTime); /** * @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); /** * @brief Available serialports Changed */ void portsChanged(const QStringList &portList); /** * @brief Sd Card Mount Changed */ void sdMountChanged(bool newState); /** * @brief The files on the sd card have changed. */ void sdCardFileListChanged(const QStringList &fileList); /** * @brief pushedCommand via serialLayer connect this to your log to see send commands * @param comm: the command sent. */ void pushedCommand(const QByteArray &comm); public slots: /** * @brief Set the printers state * @param state : printer state. - * @sa state(),stateChanged(),AtCore::STATES + * @sa state(), stateChanged(), AtCore::STATES */ void setState(AtCore::STATES state); /** * @brief Push a command into the command queue * * @param comm : Command */ Q_INVOKABLE void pushCommand(const QString &comm); /** * @brief Public Interface for printing a file * @param fileName: the gcode file to print. * @param sdPrint: set true to print fileName from Sd card */ Q_INVOKABLE void print(const QString &fileName, bool sdPrint = false); /** * @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(); /** * @brief pause an in process print job * * 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(); /** * @brief Send home \p axis command * @param axis: the axis(es) to home (use X Y Z or any combo of) * @sa home(), move() */ Q_INVOKABLE void home(uchar axis); /** * @brief Send home all command * @sa home(uchar axis), move() */ Q_INVOKABLE void home(); /** * @brief Set extruder temperature * @param temp : new temperature * @param extruder : extruder number * @param andWait: True for heat and ignore commands until temperature is reached */ Q_INVOKABLE void setExtruderTemp(uint temp = 0, uint extruder = 0, bool andWait = false); /** * @brief move an axis of the printer * @param axis the axis to move AXES (X Y Z E ) * @param arg the distance to move the axis or the place to move to depending on printer mode * @sa home(), home(uchar axis), move(QLatin1Char axis, int arg) */ Q_INVOKABLE void move(AtCore::AXES axis, double arg); /** * @brief move an axis of the printer * @param axis the axis to move AXES (X Y Z E ) * @param arg the distance to move the axis or the place to move to depending on printer mode * @sa home(), home(uchar axis), move(AtCore::AXES, int arg) */ Q_INVOKABLE void move(QLatin1Char axis, double arg); /** * @brief Set the bed temperature * @param temp : new temperature * @param andWait: True for heat and ignore commands until temperature is reached * @sa setExtruderTemp() */ Q_INVOKABLE void setBedTemp(uint temp = 0, bool andWait = false); /** * @brief setFanSpeed set the fan speed * @param fanNumber: fan number * @param speed: new speed of the fan 0-100 */ Q_INVOKABLE void setFanSpeed(uint speed = 0, uint fanNumber = 0); /** * @brief Set printer to absolute position mode * @sa setRelativePosition() */ Q_INVOKABLE void setAbsolutePosition(); /** * @brief Set printer to relative position mode * @sa setAbsolutePosition() */ Q_INVOKABLE void setRelativePosition(); /** * @brief Disable motors after a delay * @param delay: Seconds until motors are disabled. 0= No delay */ Q_INVOKABLE void disableMotors(uint delay = 0); /** * @brief set the Printers speed * @param speed: speed in % (default is 100); */ Q_INVOKABLE void setPrinterSpeed(uint speed = 100); /** * @brief set extruder Flow rate * @param rate: flow rate in % (default is 100) */ Q_INVOKABLE void setFlowRate(uint rate = 100); /** * @brief close any open items. * You should call this on close events to force any stuck jobs to close * @sa closeConnection() */ Q_INVOKABLE void close(); /** * @brief showMessage push a message to the printers LCD * @param message: message to show on the LCD */ Q_INVOKABLE void showMessage(const QString &message); /** * @brief setUnits sets the measurement units do be used * @param units : the measurement units to use(METRIC / IMPERIAL) * @sa AtCore::UNITS */ Q_INVOKABLE void setUnits(AtCore::UNITS units); /** * @brief Set the time between checks for new serialPorts (0 is default) * @param newTime: Milliseconds between checks. values <= 0 will Disable Checks. */ void setSerialTimerInterval(int newTime); /** * @brief Set the time between checks for new Temperature (5000 is default on new connections) * @param newTime: Milliseconds between checks. values <= 0 will Disable Checks. */ void setTemperatureTimerInterval(int newTime); /** * @brief delete file from sd card */ Q_INVOKABLE void sdDelete(const QString &fileName); /** * @brief Queue the Printer for status of sd card print */ void sdCardPrintStatus(); private slots: /** * @brief processQueue send commands from the queue. */ void processQueue(); /** * @brief Send M105 to the printer if one is not in the Queue */ void checkTemperature(); /** * @brief Connect to SerialLayer::receivedCommand * @param message: new message. */ void newMessage(const QByteArray &message); /** * @brief Connect to SerialLayer::pushedCommand * @param command: newCommand. */ void newCommand(const QByteArray &command); /** * @brief Search for firmware string in message. * A Helper function for Firmware detection * @param message */ void findFirmware(const QByteArray &message); /** * @brief Search for new serial ports */ void locateSerialPort(); /** * @brief Attempts to disableResetOnConnect for the selected port. * @param port: the port. */ void disableResetOnConnect(const QString &port); /** * @brief Send request to the printer for the sd card file list. */ void getSDFileList(); /** * @brief Handle serial Errors. */ 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 */ bool firmwarePluginLoaded() const; /** * @brief True if a serial port is initialized */ bool serialInitialized() const; /** * @brief send firmware request to the printer */ void requestFirmware(); /** * @brief Search for atcore firmware plugins * @param path: the path to check * @return QMap of the plugings found */ QMap findFirmwarePlugins(const QString &path); /** * @brief returns AtCorePrivate::sdCardReadingFileList * @return True if printer is returning sd card file list */ bool isReadingSdCardList() const; /** * @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. */ struct AtCorePrivate; AtCorePrivate *d; protected: /** * @brief Set the number of extruders on the machine. * @param newCount * @sa extruderCount(), extruderCountChanged(int newCount) */ void setExtruderCount(int newCount); /** * @brief Append a file to AtCorePrivate::sdCardFileList. * @param fileName: new FileName */ void appendSdCardFileList(const QString &fileName); /** * @brief Clear AtCorePrivate::sdCardFileList. */ void clearSdCardFileList(); /** * @brief Set if the sd card is mounted by the printer * @param mounted: True is mounted */ void setSdMounted(const bool mounted); /** * @brief set AtCorePrivate::sdCardReadingFileList * @param readingList set true if reading file list */ void setReadingSdCardList(bool readingList); }; diff --git a/testclient/mainwindow.cpp b/testclient/mainwindow.cpp index 29bcaad..e5f264b 100644 --- a/testclient/mainwindow.cpp +++ b/testclient/mainwindow.cpp @@ -1,628 +1,625 @@ /* AtCore Test Client - Copyright (C) <2016 - 2018> + Copyright (C) <2016 - 2019> Authors: Patrick José Pereira Lays Rodrigues Chris Rizzitello Tomaz Canabrava 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 the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include "mainwindow.h" #include "seriallayer.h" #include "gcodecommands.h" #include "about.h" Q_LOGGING_CATEGORY(TESTCLIENT_MAINWINDOW, "org.kde.atelier.core") int MainWindow::fanCount = 4; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), core(new AtCore(this)) { setWindowTitle(tr("AtCore - Test Client")); setWindowIcon(QIcon(QStringLiteral(":/icon/windowIcon"))); QCoreApplication::setApplicationVersion(core->version()); initMenu(); initStatusBar(); initWidgets(); connect(core, &AtCore::atcoreMessage, logWidget, &LogWidget::appendLog); logWidget->appendLog(tr("Attempting to locate Serial Ports")); core->setSerialTimerInterval(1000); connect(core, &AtCore::stateChanged, this, &MainWindow::printerStateChanged); connect(core, &AtCore::portsChanged, this, &MainWindow::locateSerialPort); connect(core, &AtCore::sdCardFileListChanged, sdWidget, &SdWidget::updateFilelist); comboPort->setFocus(Qt::OtherFocusReason); } void MainWindow::initMenu() { QMenu *menuFile = new QMenu(tr("File")); QAction *actionQuit = new QAction(style()->standardIcon(QStyle::SP_DialogCloseButton), tr("Quit")); connect(actionQuit, &QAction::triggered, this, &MainWindow::close); menuFile->addAction(actionQuit); menuView = new QMenu(tr("View")); QAction *actionShowDockTitles = new QAction(tr("Show Dock Titles")); actionShowDockTitles->setCheckable(true); actionShowDockTitles->setChecked(true); connect(actionShowDockTitles, &QAction::toggled, this, &MainWindow::toggleDockTitles); menuView->addAction(actionShowDockTitles); QMenu *menuHelp = new QMenu(tr("Help")); QAction *actionAbout = new QAction(tr("About")); actionAbout->setShortcut(QKeySequence(Qt::Key_F1)); connect(actionAbout, &QAction::triggered, this, [] { auto *dialog = new About; dialog->exec(); }); menuHelp->addAction(actionAbout); menuBar()->addMenu(menuFile); menuBar()->addMenu(menuView); menuBar()->addMenu(menuHelp); } void MainWindow::initStatusBar() { statusWidget = new StatusWidget; connect(statusWidget, &StatusWidget::stopPressed, core, &AtCore::stop); connect(core, &AtCore::printProgressChanged, statusWidget, &StatusWidget::updatePrintProgress); connect(core, &AtCore::sdMountChanged, statusWidget, &StatusWidget::setSD); statusBar()->addPermanentWidget(statusWidget, 100); } void MainWindow::initWidgets() { //Make the Docks makeCommandDock(); makePrintDock(); makeTempTimelineDock(); makeLogDock(); makeConnectDock(); makeMoveDock(); makeTempControlsDock(); makeSdDock(); setDangeriousDocksDisabled(true); setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::North); setTabPosition(Qt::RightDockWidgetArea, QTabWidget::North); tabifyDockWidget(moveDock, tempControlsDock); tabifyDockWidget(moveDock, sdDock); moveDock->raise(); tabifyDockWidget(connectDock, printDock); tabifyDockWidget(connectDock, commandDock); connectDock->raise(); setCentralWidget(nullptr); //More Gui stuff //hide the printing progress bar. statusWidget->showPrintArea(false); } void MainWindow::makeCommandDock() { commandWidget = new CommandWidget; //Connect the commandPressed signal connect(commandWidget, &CommandWidget::commandPressed, [this](const QString & command) { core->pushCommand(command.toUpper()); }); //Connect the messagePressed signal connect(commandWidget, &CommandWidget::messagePressed, [this](const QString & message) { core->showMessage(message); }); //Create the dock, and set the Widget. commandDock = new QDockWidget(tr("Commands"), this); commandDock->setWidget(commandWidget); //Push the toggle view action into our view menu menuView->insertAction(nullptr, commandDock->toggleViewAction()); //Place the Dock into a DockWidget Area. //Failure todo this will create some odd side effects at runtime addDockWidget(Qt::LeftDockWidgetArea, commandDock); } void MainWindow::makePrintDock() { printWidget = new PrintWidget; connect(printWidget, &PrintWidget::printPressed, this, &MainWindow::printPBClicked); connect(printWidget, &PrintWidget::emergencyStopPressed, core, &AtCore::emergencyStop); connect(printWidget, &PrintWidget::fanSpeedChanged, core, &AtCore::setFanSpeed); connect(printWidget, &PrintWidget::printSpeedChanged, this, [this](const int speed) { core->setPrinterSpeed(uint(std::max(1, speed))); }); connect(printWidget, &PrintWidget::flowRateChanged, [this](const int rate) { core->setFlowRate(uint(std::max(1, rate))); }); printDock = new QDockWidget(tr("Print"), this); printDock->setWidget(printWidget); menuView->insertAction(nullptr, printDock->toggleViewAction()); addDockWidget(Qt::LeftDockWidgetArea, printDock); } void MainWindow::makeTempTimelineDock() { plotWidget = new PlotWidget; //make and connect our plots in the widget. plotWidget->addPlot(tr("Actual Bed")); connect(&core->temperature(), &Temperature::bedTemperatureChanged, this, [this](float temp) { checkTemperature(0x00, 0, temp); plotWidget->appendPoint(tr("Actual Bed"), temp); }); plotWidget->addPlot(tr("Target Bed")); connect(&core->temperature(), &Temperature::bedTargetTemperatureChanged, this, [this](float temp) { checkTemperature(0x01, 0, temp); plotWidget->appendPoint(tr("Target Bed"), temp); }); plotWidget->addPlot(tr("Actual Ext.1")); connect(&core->temperature(), &Temperature::extruderTemperatureChanged, this, [this](float temp) { checkTemperature(0x02, 0, temp); plotWidget->appendPoint(tr("Actual Ext.1"), temp); }); plotWidget->addPlot(tr("Target Ext.1")); connect(&core->temperature(), &Temperature::extruderTargetTemperatureChanged, this, [this](float temp) { checkTemperature(0x03, 0, temp); plotWidget->appendPoint(tr("Target Ext.1"), temp); }); auto timerLayout = new QHBoxLayout; auto lblTimer = new QLabel(tr("Seconds Between Temperature Checks"), this); auto sbTemperatureTimer = new QSpinBox(this); sbTemperatureTimer->setRange(0, 90); connect(sbTemperatureTimer, QOverload::of(&QSpinBox::valueChanged), this, [this](int value) { core->setTemperatureTimerInterval(value * 1000); }); connect(core, &AtCore::temperatureTimerIntervalChanged, this, [sbTemperatureTimer](int value) { if (value != sbTemperatureTimer->value()) { sbTemperatureTimer->blockSignals(true); sbTemperatureTimer->setValue(value / 1000); sbTemperatureTimer->blockSignals(false); } }); timerLayout->addWidget(lblTimer); timerLayout->addWidget(sbTemperatureTimer); auto tempDockLayout = new QVBoxLayout; tempDockLayout->addWidget(plotWidget); tempDockLayout->addLayout(timerLayout); auto tempDockMainWidget = new QWidget(this); tempDockMainWidget->setLayout(tempDockLayout); tempTimelineDock = new QDockWidget(tr("Temperature Timeline"), this); tempTimelineDock->setWidget(tempDockMainWidget); menuView->insertAction(nullptr, tempTimelineDock->toggleViewAction()); addDockWidget(Qt::RightDockWidgetArea, tempTimelineDock); } void MainWindow::makeLogDock() { logWidget = new LogWidget(new QTemporaryFile(QDir::tempPath() + QStringLiteral("/AtCore_"))); logDock = new QDockWidget(tr("Session Log"), this); logDock->setWidget(logWidget); menuView->insertAction(nullptr, logDock->toggleViewAction()); addDockWidget(Qt::RightDockWidgetArea, logDock); } void MainWindow::makeConnectDock() { auto *mainLayout = new QVBoxLayout; auto *newLabel = new QLabel(tr("Port:")); comboPort = new QComboBox; comboPort->setEditable(true); auto *hBoxLayout = new QHBoxLayout; hBoxLayout->addWidget(newLabel); hBoxLayout->addWidget(comboPort, 75); mainLayout->addLayout(hBoxLayout); newLabel = new QLabel(tr("Baud Rate:")); comboBAUD = new QComboBox; comboBAUD->addItems(core->portSpeeds()); comboBAUD->setCurrentIndex(9); hBoxLayout = new QHBoxLayout; hBoxLayout->addWidget(newLabel); hBoxLayout->addWidget(comboBAUD, 75); mainLayout->addLayout(hBoxLayout); newLabel = new QLabel(tr("Use Plugin:")); comboPlugin = new QComboBox; comboPlugin->addItem(tr("Autodetect")); comboPlugin->addItems(core->availableFirmwarePlugins()); - connect(comboPlugin, &QComboBox::currentTextChanged, this, &MainWindow::pluginCBChanged); hBoxLayout = new QHBoxLayout; hBoxLayout->addWidget(newLabel); hBoxLayout->addWidget(comboPlugin, 75); mainLayout->addLayout(hBoxLayout); cbReset = new QCheckBox(tr("Attempt to stop Reset on connect")); cbReset->setHidden(true); 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")); connect(buttonConnect, &QPushButton::clicked, this, &MainWindow::connectPBClicked); connectionTimer = new QTimer(this); connectionTimer->setInterval(20000); connectionTimer->setSingleShot(true); connect(connectionTimer, &QTimer::timeout, this, [this] { buttonConnect->clicked(); 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.")); }); mainLayout->addWidget(buttonConnect); auto *dockContents = new QWidget; dockContents->setLayout(mainLayout); connectDock = new QDockWidget(tr("Connect"), this); connectDock->setWidget(dockContents); menuView->insertAction(nullptr, connectDock->toggleViewAction()); addDockWidget(Qt::LeftDockWidgetArea, connectDock); } void MainWindow::makeMoveDock() { movementWidget = new MovementWidget(true, this); connect(movementWidget, &MovementWidget::homeAllPressed, this, [this] { logWidget->appendLog(tr("Home All")); core->home(); }); connect(movementWidget, &MovementWidget::homeXPressed, this, [this] { logWidget->appendLog(tr("Home X")); core->home(AtCore::X); }); connect(movementWidget, &MovementWidget::homeYPressed, this, [this] { logWidget->appendLog(tr("Home Y")); core->home(AtCore::Y); }); connect(movementWidget, &MovementWidget::homeZPressed, this, [this] { logWidget->appendLog(tr("Home Z")); core->home(AtCore::Z); }); connect(movementWidget, &MovementWidget::absoluteMove, this, [this](const QLatin1Char & axis, const double & value) { logWidget->appendLog(GCode::description(GCode::G1)); core->move(axis, value); }); connect(movementWidget, &MovementWidget::disableMotorsPressed, this, [this] { core->disableMotors(0); }); connect(movementWidget, &MovementWidget::relativeMove, this, [this](const QLatin1Char & axis, const double & value) { core->setRelativePosition(); core->move(axis, value); core->setAbsolutePosition(); }); connect(movementWidget, &MovementWidget::unitsChanged, this, [this](int units) { auto selection = static_cast(units); core->setUnits(selection); }); moveDock = new QDockWidget(tr("Movement"), this); moveDock->setWidget(movementWidget); menuView->insertAction(nullptr, moveDock->toggleViewAction()); addDockWidget(Qt::LeftDockWidgetArea, moveDock); } void MainWindow::makeTempControlsDock() { temperatureWidget = new TemperatureWidget; connect(temperatureWidget, &TemperatureWidget::bedTempChanged, core, &AtCore::setBedTemp); connect(temperatureWidget, &TemperatureWidget::extTempChanged, core, &AtCore::setExtruderTemp); tempControlsDock = new QDockWidget(tr("Temperatures"), this); tempControlsDock->setWidget(temperatureWidget); menuView->insertAction(nullptr, tempControlsDock->toggleViewAction()); addDockWidget(Qt::LeftDockWidgetArea, tempControlsDock); } void MainWindow::makeSdDock() { sdWidget = new SdWidget; connect(sdWidget, &SdWidget::requestSdList, core, &AtCore::sdFileList); connect(sdWidget, &SdWidget::printSdFile, this, [this](const QString & fileName) { if (fileName.isEmpty()) { QMessageBox::information(this, tr("Print Error"), tr("You must Select a file from the list")); } else { core->print(fileName, true); } }); connect(sdWidget, &SdWidget::deleteSdFile, this, [this](const QString & fileName) { if (fileName.isEmpty()) { QMessageBox::information(this, tr("Delete Error"), tr("You must Select a file from the list")); } else { core->sdDelete(fileName); } }); sdDock = new QDockWidget(tr("Sd Card"), this); sdDock->setWidget(sdWidget); menuView->insertAction(nullptr, sdDock->toggleViewAction()); addDockWidget(Qt::LeftDockWidgetArea, sdDock); } void MainWindow::closeEvent(QCloseEvent *event) { core->close(); event->accept(); } void MainWindow::checkTemperature(uint sensorType, uint number, float temp) { QString msg; switch (sensorType) { case 0x00: // bed msg = QString::fromLatin1("Bed Temperature"); break; case 0x01: // bed target msg = QString::fromLatin1("Bed Target Temperature"); break; case 0x02: // extruder msg = QString::fromLatin1("Extruder[%1] Temperature").arg(QString::number(number));; break; case 0x03: // extruder target msg = QString::fromLatin1("Extruder[%1] Target Temperature").arg(QString::number(number)); break; case 0x04: // enclosure msg = QString::fromLatin1("Enclosure Temperature"); break; case 0x05: // enclosure target msg = QString::fromLatin1("Enclosure Target Temperature"); break; } msg.append(QString::fromLatin1(": %1").arg(QString::number(double(temp), 'f', 2))); logWidget->appendLog(msg); } /** * @brief MainWindow::locateSerialPort * Locate all active serial ports on the computer and add to the list * of serial ports */ void MainWindow::locateSerialPort(const QStringList &ports) { comboPort->clear(); if (!ports.isEmpty()) { comboPort->addItems(ports); logWidget->appendLog(tr("Found %1 Ports").arg(QString::number(ports.count()))); } else { QString portError(tr("No available ports! Please connect a serial device to continue!")); if (! logWidget->endsWith(portError)) { logWidget->appendLog(portError); } } } 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); } } } } else { disconnect(core, &AtCore::receivedMessage, logWidget, &LogWidget::appendRLog); disconnect(core, &AtCore::pushedCommand, logWidget, &LogWidget::appendSLog); core->closeConnection(); core->setState(AtCore::DISCONNECTED); logWidget->appendLog(tr("Disconnected")); } } void MainWindow::printPBClicked() { QString fileName; switch (core->state()) { case AtCore::DISCONNECTED: QMessageBox::information(this, tr("Error"), tr("Not Connected To a Printer")); break; case AtCore::CONNECTING: QMessageBox::information(this, tr("Error"), 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. We have loaded the most common plugin \"repetier\" for you. You may try to print again after this message")); comboPlugin->setCurrentText(QStringLiteral("repetier")); break; case AtCore::IDLE: fileName = QFileDialog::getOpenFileName(this, tr("Select a file to print"), QDir::homePath(), tr("*.gcode")); if (fileName.isNull()) { logWidget->appendLog(tr("No File Selected")); } else { logWidget->appendLog(tr("Print: %1").arg(fileName)); core->print(fileName); } break; case AtCore::BUSY: core->pause(printWidget->postPauseCommand()); break; case AtCore::PAUSE: core->resume(); break; default: qCDebug(TESTCLIENT_MAINWINDOW) << "ERROR / STOP unhandled."; } } -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; switch (state) { case AtCore::IDLE: if (connectionTimer->isActive()) { connectionTimer->stop(); } buttonConnect->setText(tr("Disconnect")); printWidget->setPrintText(tr("Print File")); stateString = tr("Connected to ") + core->connectedPort(); sdDock->setVisible(core->firmwarePlugin()->isSdSupported()); break; case AtCore::STARTPRINT: stateString = tr("START PRINT"); printWidget->setPrintText(tr("Pause Print")); statusWidget->showPrintArea(true); break; case AtCore::FINISHEDPRINT: stateString = tr("Finished Print"); printWidget->setPrintText(tr("Print File")); statusWidget->showPrintArea(false); break; case AtCore::PAUSE: stateString = tr("Paused"); printWidget->setPrintText(tr("Resume Print")); break; case AtCore::BUSY: stateString = tr("Printing"); printWidget->setPrintText(tr("Pause Print")); break; case AtCore::DISCONNECTED: if (connectionTimer->isActive()) { connectionTimer->stop(); } 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; case AtCore::STOP: stateString = tr("Stopping Print"); break; case AtCore::ERRORSTATE: stateString = tr("Command ERROR"); break; } statusWidget->setState(stateString); } void MainWindow::toggleDockTitles(bool checked) { if (checked) { delete connectDock->titleBarWidget(); delete logDock->titleBarWidget(); delete tempTimelineDock->titleBarWidget(); delete commandDock->titleBarWidget(); delete moveDock->titleBarWidget(); delete tempControlsDock->titleBarWidget(); delete printDock->titleBarWidget(); delete sdDock->titleBarWidget(); } else { connectDock->setTitleBarWidget(new QWidget()); logDock->setTitleBarWidget(new QWidget()); tempTimelineDock->setTitleBarWidget(new QWidget()); commandDock->setTitleBarWidget(new QWidget()); moveDock->setTitleBarWidget(new QWidget()); tempControlsDock->setTitleBarWidget(new QWidget()); printDock->setTitleBarWidget(new QWidget()); sdDock->setTitleBarWidget(new QWidget()); } } void MainWindow::setDangeriousDocksDisabled(bool disabled) { commandDock->widget()->setDisabled(disabled); moveDock->widget()->setDisabled(disabled); tempControlsDock->widget()->setDisabled(disabled); printDock->widget()->setDisabled(disabled); sdDock->widget()->setDisabled(disabled); if (!disabled) { temperatureWidget->updateExtruderCount(core->extruderCount()); printWidget->updateFanCount(fanCount); } else { printWidget->setPrintText(tr("Print File")); statusWidget->showPrintArea(false); } } + +void MainWindow::setConnectionWidgetsEnabled(bool enabled) +{ + comboBAUD->setEnabled(enabled); + comboPlugin->setEnabled(enabled); + comboPort->setEnabled(enabled); +} diff --git a/testclient/mainwindow.h b/testclient/mainwindow.h index 8c890da..52a50f4 100644 --- a/testclient/mainwindow.h +++ b/testclient/mainwindow.h @@ -1,180 +1,180 @@ /* AtCore Test Client - Copyright (C) <2016> + Copyright (C) <2016 - 2019> Authors: Patrick José Pereira Lays Rodrigues Chris Rizzitello Tomaz Canabrava 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 the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #pragma once #include #include "atcore.h" #include "commandwidget.h" #include "logwidget.h" #include "movementwidget.h" #include "plotwidget.h" #include "printwidget.h" #include "sdwidget.h" #include "statuswidget.h" #include "temperaturewidget.h" class SerialLayer; class MainWindow: public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow() override = default; public slots: /** * @brief Check temperature * * @param sensorType : type of sensor * @param number : index of sensor * @param temp : temperature */ void checkTemperature(uint sensorType, uint number, float temp); private slots: //ButtonEvents /** * @brief Connect Button Clicked will connect or disconnect based on printer state */ void connectPBClicked(); /** * @brief Print Button Clicked, can also pause /resue print based on printer state */ void printPBClicked(); /** * @brief printerStateChanged Catch and proccess printer state commands * @param state: new printer state */ void printerStateChanged(AtCore::STATES state); /** * @brief show/hide dock titlebars * @param checked: True if shown */ 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 * @param disabled: True if items are disabled. */ void setDangeriousDocksDisabled(bool disabled); private: AtCore *core; // Define max number of fans static int fanCount; void closeEvent(QCloseEvent *event) override; /** * @brief Locate serial port * */ void locateSerialPort(const QStringList &ports); - /** - * @brief pluginCB index changed - */ - void pluginCBChanged(const QString ¤tText); - /** * @brief setupActions for KXMLGui */ void setupActions(); /** * @brief Populate comboboxes */ void populateCBs(); /** * @brief Gui Changes for when sd card mount status has changed. */ void sdChanged(bool mounted); //UI Functions /** * @brief Create The Menubar */ void initMenu(); /** * @brief Create StatusBar */ void initStatusBar(); /** * @brief Create Main Widgets. */ void initWidgets(); //Private GUI Items //menuView is global to allow for docks to be added / removed. QMenu *menuView = nullptr; //Status Bar Items StatusWidget *statusWidget = nullptr; //Docks void makeLogDock(); QDockWidget *logDock = nullptr; LogWidget *logWidget = nullptr; void makeTempTimelineDock(); QDockWidget *tempTimelineDock = nullptr; PlotWidget *plotWidget = nullptr; void makeCommandDock(); QDockWidget *commandDock = nullptr; CommandWidget *commandWidget = nullptr; void makePrintDock(); QDockWidget *printDock = nullptr; PrintWidget *printWidget = nullptr; void makeConnectDock(); QDockWidget *connectDock = nullptr; QComboBox *comboPort = nullptr; QComboBox *comboBAUD = nullptr; QComboBox *comboPlugin = nullptr; QPushButton *buttonConnect = nullptr; QCheckBox *cbReset = nullptr; QTimer *connectionTimer = nullptr; void makeMoveDock(); QDockWidget *moveDock = nullptr; MovementWidget *movementWidget = nullptr; void makeTempControlsDock(); QDockWidget *tempControlsDock = nullptr; TemperatureWidget *temperatureWidget = nullptr; void makeSdDock(); QDockWidget *sdDock = nullptr; SdWidget *sdWidget = nullptr; }; diff --git a/unittests/atcoretests.cpp b/unittests/atcoretests.cpp index 2e2fbb3..cf01c88 100644 --- a/unittests/atcoretests.cpp +++ b/unittests/atcoretests.cpp @@ -1,290 +1,290 @@ /* 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 the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include "atcoretests.h" void AtCoreTests::initTestCase() { core = new AtCore(); } void AtCoreTests::cleanupTestCase() { } void AtCoreTests::testInitState() { QVERIFY(core->state() == AtCore::DISCONNECTED); } void AtCoreTests::testPluginDetect() { QStringList fwPluginsFound = core->availableFirmwarePlugins(); QStringList fwPluginsActual = { QStringLiteral("aprinter"), QStringLiteral("grbl"), QStringLiteral("marlin"), QStringLiteral("repetier"), QStringLiteral("smoothie"), QStringLiteral("sprinter"), QStringLiteral("teacup") }; std::sort(fwPluginsFound.begin(), fwPluginsFound.end()); std::sort(fwPluginsActual.begin(), fwPluginsActual.end()); QVERIFY(fwPluginsFound == fwPluginsActual); } 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() { QList args; QSignalSpy sSpy(core, SIGNAL(stateChanged(AtCore::STATES))); QVERIFY(sSpy.isValid()); core->setState(AtCore::CONNECTING); args = sSpy.takeFirst(); QVERIFY(args.at(0).toInt() == AtCore::CONNECTING); core->setState(AtCore::IDLE); args = sSpy.takeFirst(); QVERIFY(args.at(0).toInt() == AtCore::IDLE); core->setState(AtCore::BUSY); args = sSpy.takeFirst(); QVERIFY(args.at(0).toInt() == AtCore::BUSY); core->setState(AtCore::PAUSE); args = sSpy.takeFirst(); QVERIFY(args.at(0).toInt() == AtCore::PAUSE); core->setState(AtCore::STOP); args = sSpy.takeFirst(); QVERIFY(args.at(0).toInt() == AtCore::STOP); core->setState(AtCore::ERRORSTATE); args = sSpy.takeFirst(); QVERIFY(args.at(0).toInt() == AtCore::ERRORSTATE); core->setState(AtCore::STARTPRINT); args = sSpy.takeFirst(); QVERIFY(args.at(0).toInt() == AtCore::STARTPRINT); core->setState(AtCore::FINISHEDPRINT); args = sSpy.takeFirst(); QVERIFY(args.at(0).toInt() == AtCore::FINISHEDPRINT); core->setState(AtCore::DISCONNECTED); args = sSpy.takeFirst(); QVERIFY(args.at(0).toInt() == AtCore::DISCONNECTED); } void AtCoreTests::testSdMountChanged() { QList args; QSignalSpy sSpy(core, SIGNAL(sdMountChanged(bool))); QVERIFY(sSpy.isValid()); core->setSdMounted(true); args = sSpy.takeFirst(); QVERIFY(args.at(0).toBool() == true); core->setSdMounted(false); args = sSpy.takeFirst(); QVERIFY(args.at(0).toBool() == false); } void AtCoreTests::testSdFileList() { QSignalSpy sSpy(core, SIGNAL(sdCardFileListChanged(QStringList))); QVERIFY(sSpy.isValid()); core->appendSdCardFileList(QStringLiteral("FILE1")); core->appendSdCardFileList(QStringLiteral("FILE2")); core->appendSdCardFileList(QStringLiteral("FILE3")); core->appendSdCardFileList(QStringLiteral("FILE4")); core->appendSdCardFileList(QStringLiteral("FILE5")); QList args = sSpy.takeLast(); QStringList fileList = { QStringLiteral("FILE1"), QStringLiteral("FILE2"), QStringLiteral("FILE3"), QStringLiteral("FILE4"), QStringLiteral("FILE5") }; QVERIFY(args.at(0).toStringList() == fileList); core->clearSdCardFileList(); args = sSpy.takeLast(); QVERIFY(args.at(0).toStringList().isEmpty()); } void AtCoreTests::testSerialTimerIntervalChanged() { QSignalSpy sSpy(core, SIGNAL(serialTimerIntervalChanged(int))); QVERIFY(sSpy.isValid()); core->setSerialTimerInterval(1000); core->setSerialTimerInterval(1000); core->setSerialTimerInterval(2000); core->setSerialTimerInterval(0); QVERIFY(sSpy.count() == 3); } void AtCoreTests::testExtruderCountChanged() { QSignalSpy sSpy(core, SIGNAL(extruderCountChanged(int))); QVERIFY(sSpy.isValid()); core->setExtruderCount(1); core->setExtruderCount(2); core->setExtruderCount(1); QVERIFY(sSpy.count() == 2); } void AtCoreTests::testPluginAprinter_load() { core->loadFirmwarePlugin(QStringLiteral("aprinter")); QVERIFY(core->firmwarePlugin()->name() == QStringLiteral("Aprinter")); } void AtCoreTests::testPluginAprinter_validate() { QSignalSpy sSpy(core->firmwarePlugin(), SIGNAL(readyForCommand())); QVERIFY(sSpy.isValid() == true); core->firmwarePlugin()->validateCommand(QStringLiteral("ok")); core->firmwarePlugin()->validateCommand(QStringLiteral("other text")); QVERIFY(sSpy.count() == 1); } void AtCoreTests::testPluginGrbl_load() { core->loadFirmwarePlugin(QStringLiteral("grbl")); QVERIFY(core->firmwarePlugin()->name() == QStringLiteral("Grbl")); } void AtCoreTests::testPluginGrbl_validate() { QSignalSpy sSpy(core->firmwarePlugin(), SIGNAL(readyForCommand())); QVERIFY(sSpy.isValid() == true); core->firmwarePlugin()->validateCommand(QStringLiteral("ok")); core->firmwarePlugin()->validateCommand(QStringLiteral("other text")); QVERIFY(sSpy.count() == 1); } void AtCoreTests::testPluginGrbl_translate() { QVERIFY(core->firmwarePlugin()->translate(QStringLiteral("G28")) == "G28"); QVERIFY(core->firmwarePlugin()->translate(QStringLiteral("M104 S50 G28 X")) == "M104 S50\r\nG28 X"); QVERIFY(core->firmwarePlugin()->translate(QStringLiteral("G00 G43 H0 Z0.1")) == "G00\r\nG43 H0 Z0.1"); QVERIFY(core->firmwarePlugin()->translate(QStringLiteral("M6 T0")) == "M6 T0"); QVERIFY(core->firmwarePlugin()->translate(QStringLiteral("G0 G49 G40 G17 G80 G50 G90")) == "G0\r\nG49\r\nG40\r\nG17\r\nG80\r\nG50\r\nG90"); QVERIFY(core->firmwarePlugin()->translate(QStringLiteral("G0 S49 XY G40")) == "G0 S49 XY\r\nG40"); } void AtCoreTests::testPluginMarlin_load() { core->loadFirmwarePlugin(QStringLiteral("marlin")); QVERIFY(core->firmwarePlugin()->name() == QStringLiteral("Marlin")); } void AtCoreTests::testPluginMarlin_validate() { QSignalSpy sSpy(core->firmwarePlugin(), SIGNAL(readyForCommand())); QVERIFY(sSpy.isValid() == true); core->firmwarePlugin()->validateCommand(QStringLiteral("ok")); core->firmwarePlugin()->validateCommand(QStringLiteral("other text")); QVERIFY(sSpy.count() == 1); } void AtCoreTests::testPluginRepetier_load() { core->loadFirmwarePlugin(QStringLiteral("repetier")); QVERIFY(core->firmwarePlugin()->name() == QStringLiteral("Repetier")); } void AtCoreTests::testPluginRepetier_validate() { QSignalSpy sSpy(core->firmwarePlugin(), SIGNAL(readyForCommand())); QVERIFY(sSpy.isValid() == true); core->firmwarePlugin()->validateCommand(QStringLiteral("ok")); core->firmwarePlugin()->validateCommand(QStringLiteral("other text")); QVERIFY(sSpy.count() == 1); } void AtCoreTests::testPluginSmoothie_load() { core->loadFirmwarePlugin(QStringLiteral("smoothie")); QVERIFY(core->firmwarePlugin()->name() == QStringLiteral("Smoothie")); } void AtCoreTests::testPluginSmoothie_validate() { QSignalSpy sSpy(core->firmwarePlugin(), SIGNAL(readyForCommand())); QVERIFY(sSpy.isValid() == true); core->firmwarePlugin()->validateCommand(QStringLiteral("ok")); core->firmwarePlugin()->validateCommand(QStringLiteral("other text")); QVERIFY(sSpy.count() == 1); } void AtCoreTests::testPluginSprinter_load() { core->loadFirmwarePlugin(QStringLiteral("sprinter")); QVERIFY(core->firmwarePlugin()->name() == QStringLiteral("Sprinter")); } void AtCoreTests::testPluginSprinter_validate() { QSignalSpy sSpy(core->firmwarePlugin(), SIGNAL(readyForCommand())); QVERIFY(sSpy.isValid() == true); core->firmwarePlugin()->validateCommand(QStringLiteral("ok")); core->firmwarePlugin()->validateCommand(QStringLiteral("other text")); QVERIFY(sSpy.count() == 1); } void AtCoreTests::testPluginTeacup_load() { core->loadFirmwarePlugin(QStringLiteral("teacup")); QVERIFY(core->firmwarePlugin()->name() == QStringLiteral("Teacup")); } void AtCoreTests::testPluginTeacup_validate() { QSignalSpy sSpy(core->firmwarePlugin(), SIGNAL(readyForCommand())); QVERIFY(sSpy.isValid() == true); core->firmwarePlugin()->validateCommand(QStringLiteral("ok")); core->firmwarePlugin()->validateCommand(QStringLiteral("other text")); QVERIFY(sSpy.count() == 1); } void AtCoreTests::testPluginTeacup_translate() { QVERIFY(core->firmwarePlugin()->translate(QStringLiteral("G28")) == "G28"); QVERIFY(core->firmwarePlugin()->translate(QStringLiteral("M109 S50")) == "M104 S50\r\nM116"); QVERIFY(core->firmwarePlugin()->translate(QStringLiteral("M190 S50")) == "M140 S50\r\nM116"); } QTEST_MAIN(AtCoreTests)