diff --git a/src/atcore.cpp b/src/atcore.cpp index c8886ae..7bea7e8 100644 --- a/src/atcore.cpp +++ b/src/atcore.cpp @@ -1,519 +1,529 @@ /* AtCore Copyright (C) <2016> Authors: Tomaz Canabrava Chris Rizzitello Patrick José Pereira Lays Rodrigues 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 2 of the License. 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 "atcore.h" #include "seriallayer.h" #include "gcodecommands.h" #include "printthread.h" #include "atcore_default_folders.h" #include #include #include #include #include #include #include #include #include Q_LOGGING_CATEGORY(ATCORE_PLUGIN, "org.kde.atelier.core.plugin"); Q_LOGGING_CATEGORY(ATCORE_CORE, "org.kde.atelier.core"); struct AtCorePrivate { IFirmware *fwPlugin = nullptr; SerialLayer *serial = nullptr; QPluginLoader pluginLoader; QDir pluginsDir; QMap plugins; QByteArray lastMessage; PrinterStatus printerStatus; int extruderCount = 1; Temperature temperature; QStringList commandQueue; bool ready = false; QTimer *tempTimer = nullptr; }; AtCore::AtCore(QObject *parent) : QObject(parent), d(new AtCorePrivate) { qRegisterMetaType("PrinterState"); setState(DISCONNECTED); d->tempTimer = new QTimer; d->tempTimer->setInterval(5000); d->tempTimer->setSingleShot(false); for (const auto &path : AtCoreDirectories::pluginDir) { qCDebug(ATCORE_PLUGIN) << "Lookin for plugins in " << path; if (QDir(path).exists()) { d->pluginsDir = QDir(path); qCDebug(ATCORE_PLUGIN) << "Valid path for plugins found !"; break; } } if (!d->pluginsDir.exists()) { qCritical() << "No valid path for plugin !"; } #if defined(Q_OS_WIN) || defined(Q_OS_MAC) d->pluginsDir = qApp->applicationDirPath() + QStringLiteral("/plugins"); #endif qCDebug(ATCORE_PLUGIN) << d->pluginsDir; findPlugins(); setState(DISCONNECTED); } void AtCore::setSerial(SerialLayer *serial) { d->serial = serial; } void AtCore::setPlugin(IFirmware *plugin) { d->fwPlugin = plugin; } SerialLayer *AtCore::serial() const { return d->serial; } IFirmware *AtCore::plugin() const { return d->fwPlugin; } void AtCore::close() { exit(0); } Temperature &AtCore::temperature() const { return d->temperature; } void AtCore::findFirmware(const QByteArray &message) { if (state() == DISCONNECTED) { qWarning() << "Cant find firwmware, serial not connected !"; return; } if (state() == CONNECTING) { if (message.contains("start")) { qCDebug(ATCORE_CORE) << "Waiting requestFirmware."; QTimer::singleShot(500, this, &AtCore::requestFirmware); return; } else if (message.contains("Grbl")) { loadFirmware(QString::fromLatin1("grbl")); return; } } qCDebug(ATCORE_CORE) << "Find Firmware Called" << 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 d->extruderCount = message.at(message.indexOf("EXTRUDER_COUNT:") + 15) - '0'; } qCDebug(ATCORE_CORE) << "Extruder Count:" << QString::number(extruderCount()); loadFirmware(fwName); } void AtCore::loadFirmware(const QString &fwName) { if (d->plugins.contains(fwName)) { d->pluginLoader.setFileName(d->plugins[fwName]); if (!d->pluginLoader.load()) { qCDebug(ATCORE_PLUGIN) << d->pluginLoader.errorString(); } else { qCDebug(ATCORE_PLUGIN) << "Loading plugin."; } setPlugin(qobject_cast(d->pluginLoader.instance())); if (!pluginLoaded()) { qCDebug(ATCORE_PLUGIN) << "No plugin loaded."; qCDebug(ATCORE_PLUGIN) << "Looking plugin in folder:" << d->pluginsDir; setState(CONNECTING); } else { qCDebug(ATCORE_PLUGIN) << "Connected to" << plugin()->name(); plugin()->init(this); disconnect(serial(), &SerialLayer::receivedCommand, this, &AtCore::findFirmware); connect(serial(), &SerialLayer::receivedCommand, this, &AtCore::newMessage); connect(plugin(), &IFirmware::readyForCommand, this, &AtCore::processQueue); d->ready = true; // ready on new firmware load connect(d->tempTimer, &QTimer::timeout, this, &AtCore::checkTemperature); d->tempTimer->start(); setState(IDLE); } } else { qCDebug(ATCORE_CORE) << "No Firmware Loaded"; } } void AtCore::initSerial(const QString &port, int baud) { setSerial(new SerialLayer(port, baud)); if (isInitialized()) { setState(CONNECTING); } connect(serial(), &SerialLayer::receivedCommand, this, &AtCore::findFirmware); } bool AtCore::isInitialized() { if (!d->serial) { return false; } return d->serial->isOpen(); } QList AtCore::serialPorts() const { return QList(); } void AtCore::newMessage(const QByteArray &message) { d->lastMessage = message; if (message.startsWith(QString::fromLatin1("X:").toLocal8Bit())) { d->printerStatus.posString = message; d->printerStatus.posString.resize(d->printerStatus.posString.indexOf('E')); d->printerStatus.posString.replace(':', ""); } //Check if have temperature info and decode it temperature().decodeTemp(message); emit(receivedMessage(d->lastMessage)); } void AtCore::setRelativePosition() { pushCommand(GCode::toCommand(GCode::G91)); } void AtCore::setAbsolutePosition() { pushCommand(GCode::toCommand(GCode::G90)); } float AtCore::percentagePrinted() { return d->printerStatus.percentage; } void AtCore::print(const QString &fileName) { if (state() == CONNECTING) { qCDebug(ATCORE_CORE) << "Load a firmware plugin to print."; return; } //START A THREAD AND CONNECT TO IT setState(STARTPRINT); QThread *thread = new QThread(); PrintThread *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) { d->commandQueue.append(comm); if (d->ready) { processQueue(); } } void AtCore::closeConnection() { if (isInitialized()) { if (state() == BUSY) { //we have to clean print if printing. setState(STOP); } if (pluginLoaded()) { disconnect(plugin(), &IFirmware::readyForCommand, this, &AtCore::processQueue); disconnect(d->tempTimer, &QTimer::timeout, this, &AtCore::checkTemperature); d->tempTimer->stop(); } serial()->close(); setState(DISCONNECTED); } } PrinterState AtCore::state(void) { return d->printerStatus.printerState; } void AtCore::setState(PrinterState state) { if (state != d->printerStatus.printerState) { qCDebug(ATCORE_CORE) << "Atcore state changed from [" \ << d->printerStatus.printerState << "] to [" << state << "]"; d->printerStatus.printerState = state; emit(stateChanged(d->printerStatus.printerState)); } } QByteArray AtCore::popCommand() { return serial()->popCommand(); } void AtCore::stop() { setState(STOP); d->commandQueue.clear(); setExtruderTemp(0, 0); setBedTemp(0); home('X'); } void AtCore::emergencyStop() { switch (state()) { case BUSY: setState(STOP); default: d->commandQueue.clear(); serial()->pushCommand(GCode::toCommand(GCode::M112).toLocal8Bit()); } } void AtCore::requestFirmware() { qCDebug(ATCORE_CORE) << "Sending " << GCode::toString(GCode::M115); if (isInitialized()) { serial()->pushCommand(GCode::toCommand(GCode::M115).toLocal8Bit()); } } bool AtCore::pluginLoaded() { if (plugin()) { return true; } else { return false; } } void AtCore::findPlugins() { d->plugins.clear(); qCDebug(ATCORE_PLUGIN) << "plugin dir:" << d->pluginsDir; QStringList files = d->pluginsDir.entryList(QDir::Files); foreach (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 { qCDebug(ATCORE_PLUGIN) << "File" << file << "not plugin."; continue; } qCDebug(ATCORE_CORE) << "Found plugin file" << f; if (file.startsWith(QStringLiteral("lib"))) { file = file.remove(QStringLiteral("lib")); } file = file.toLower().simplified(); QString pluginString; pluginString.append(d->pluginsDir.path()); pluginString.append(QChar::fromLatin1('/')); pluginString.append(f); d->plugins[file] = pluginString; qCDebug(ATCORE_CORE) << tr("plugins[%1]=%2").arg(file, pluginString); } } QStringList AtCore::availablePlugins() { return d->plugins.keys(); } void AtCore::detectFirmware() { connect(serial(), &SerialLayer::receivedCommand, this, &AtCore::findFirmware); requestFirmware(); } void AtCore::pause(const QString &pauseActions) { pushCommand(GCode::toCommand(GCode::M114)); setState(PAUSE); if (!pauseActions.isNull()) { QStringList temp = pauseActions.split(QChar::fromLatin1(',')); for (int i = 0; i < temp.length(); i++) { pushCommand(temp.at(i)); } } } void AtCore::resume() { pushCommand(GCode::toCommand(GCode::G0, QString::fromLatin1(d->printerStatus.posString))); setState(BUSY); } /*~~~~~Control Slots ~~~~~~~~*/ void AtCore::home() { pushCommand(GCode::toCommand(GCode::G28)); } void AtCore::home(uchar axis) { QString args; if (axis & X) { args.append(QStringLiteral("X0 ")); } if (axis & Y) { args.append(QStringLiteral("Y0 ")); } if (axis & Z) { args.append(QStringLiteral("Z0")); } pushCommand(GCode::toCommand(GCode::G28, args)); } void AtCore::setExtruderTemp(uint temp, uint extruder) { pushCommand(GCode::toCommand(GCode::M104, QString::number(extruder), QString::number(temp))); temperature().setExtruderTargetTemperature(temp); } void AtCore::setBedTemp(uint temp) { pushCommand(GCode::toCommand(GCode::M140, QString::number(temp))); temperature().setBedTargetTemperature(temp); } void AtCore::setFanSpeed(uint speed, uint fanNumber) { pushCommand(GCode::toCommand(GCode::M106, QString::number(fanNumber), QString::number(speed))); } void AtCore::setPrinterSpeed(uint speed) { pushCommand(GCode::toCommand(GCode::M220, QString::number(speed))); } void AtCore::setFlowRate(uint speed) { pushCommand(GCode::toCommand(GCode::M221, QString::number(speed))); } void AtCore::move(uchar axis, uint arg) { if (axis & X) { pushCommand(GCode::toCommand(GCode::G1, QStringLiteral("X %1").arg(QString::number(arg)))); } else if (axis & Y) { pushCommand(GCode::toCommand(GCode::G1, QStringLiteral("Y %1").arg(QString::number(arg)))); } else if (axis & Z) { pushCommand(GCode::toCommand(GCode::G1, QStringLiteral("Z %1").arg(QString::number(arg)))); } else if (axis & E) { pushCommand(GCode::toCommand(GCode::G1, QStringLiteral("E %1").arg(QString::number(arg)))); } } int AtCore::extruderCount() { return d->extruderCount; } void AtCore::processQueue() { d->ready = true; if (d->commandQueue.isEmpty()) { return; } if (!isInitialized()) { qCDebug(ATCORE_PLUGIN) << "Can't process queue ! Serial not initialized."; return; } QString text = d->commandQueue.takeAt(0); if (pluginLoaded()) { serial()->pushCommand(plugin()->translate(text)); } else { serial()->pushCommand(text.toLocal8Bit()); } d->ready = false; } void AtCore::checkTemperature() { if (d->commandQueue.contains(GCode::toCommand(GCode::M105))) { return; } pushCommand(GCode::toCommand(GCode::M105)); } void AtCore::showMessage(const QString &message) { if (!message.isEmpty()) { pushCommand(GCode::toCommand((GCode::M117), message)); } } + +void AtCore::setUnits(MeasurementUnits units) +{ + switch (units) { + case METRIC: + pushCommand(GCode::toCommand(GCode::G21)); + case IMPERIAL: + pushCommand(GCode::toCommand(GCode::G20)); + } +} diff --git a/src/atcore.h b/src/atcore.h index f6af24b..f7218f1 100644 --- a/src/atcore.h +++ b/src/atcore.h @@ -1,280 +1,290 @@ /* AtCore Copyright (C) <2016> Authors: Tomaz Canabrava Chris Rizzitello Patrick José Pereira Lays Rodrigues 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 2 of the License. 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 #include #include "ifirmware.h" #include "temperature.h" #include "katcore_export.h" class SerialLayer; class IFirmware; class QTime; enum PrinterState { DISCONNECTED, //Not Connected to a printer, initial state CONNECTING, //Attempting to connect, Fw not probed IDLE, //Connected to printer and ready for commands BUSY, //Printer is working PAUSE, //Printer is paused ERRORSTATE, // Printer Returned Error STOP, // Stop Printing and Clean Queue STARTPRINT, //Just Starting a print job FINISHEDPRINT, //Just Finished print job }; struct AtCorePrivate; enum AXIS { X = 1 << 0, Y = 1 << 1, Z = 1 << 2, E = 1 << 3, }; +enum MeasurementUnits { + METRIC, IMPERIAL +}; + //TODO: PrinterStatus should also move to d->pointer; struct KATCORE_EXPORT PrinterStatus { float percentage; QByteArray posString; Temperature temps; PrinterState printerState; }; class KATCORE_EXPORT AtCore : public QObject { Q_OBJECT public: AtCore(QObject *parent = nullptr); QList serialPorts() const; void initSerial(const QString &port, int baud); bool isInitialized(); void setSerial(SerialLayer *serial); SerialLayer *serial() const; void setPlugin(IFirmware *plugin); IFirmware *plugin() const; /** * @brief Get Printer state * @return State of the printer */ PrinterState state(void); /** * @brief setState: set Printer state * @param state : printer state. */ void setState(PrinterState state); /** * @brief extruderCount * @return The number of detected Extruders Default is 1 */ int extruderCount(); /** * @brief Return true if plugin is loaded */ bool pluginLoaded(); /** * @brief Request firmware sending M115 command */ void requestFirmware(); /** * @brief Return printed percentage */ float percentagePrinted(); /** * @brief Request a list of firmware plugins */ QStringList availablePlugins(); /** * @brief Load A firmware * * @param fwName : name of the firmware */ void loadFirmware(const QString &fwName); /** * @brief detectFirmware attempt to autodetect the firmware */ void detectFirmware(); /** * @brief Return FIFO command from printer * * @return QByteArray */ QByteArray popCommand(); /** * @brief Close the serial connection */ void closeConnection(); /** * @brief The temperature of the current hotend as told by the Firmware. */ Temperature &temperature() const; signals: /** * @brief Emit signal when the printing precentabe changes. * * @param msg : Message */ void printProgressChanged(const float &newProgress); /** * @brief Emit signal when message is received from the printer * * @param msg : Message */ void receivedMessage(const QByteArray &message); /** * @brief State Changed * @param newState : the new state of the printer */ void stateChanged(PrinterState newState); public slots: /** * @brief Translate command and push it * * @param msg : Command */ void pushCommand(const QString &comm); /** * @brief Public Interface for printing a file */ void print(const QString &fileName); /** * @brief Stop the Printer by empting the queue and aborting the print job (if running) */ void stop(); /** * @brief stop the printer via the emergency stop Command (M112) */ void emergencyStop(); /** * @brief pause an in process print job * @param Gcode to run after pausing commands are ',' seperated */ void pause(const QString &pauseActions); /** * @brief resume a paused print job. */ void resume(); /** * @brief Send home command * @param axis: the axis(es) to home (use X Y Z or any combo of) */ void home(uchar axis); /** * @brief Send home all command */ void home(); /** * @brief Set extruder temperature * @param temp : new temperature * @param extruder : extruder number */ void setExtruderTemp(uint temp = 0, uint extruder = 0); /** * @brief move an axis of the printer * @param axis the axis to move AXIS (X Y Z E ) * @param arg the distance to move the axis or the place to move to depending on printer mode */ void move(uchar axis, uint arg); /** * @brief Set the bed temperature * @param temp : new temperature */ void setBedTemp(uint temp = 0); /** * @brief setFanSpeed set the fan speed * @param fanNumber: fan number * @param speed: new speed of the fan 0-100 */ void setFanSpeed(uint speed = 0, uint fanNumber = 0); void setAbsolutePosition(); void setRelativePosition(); /** * @brief set the Printers speed * @param speed: speed in % (default is 100); */ void setPrinterSpeed(uint speed = 100); /** * @brief set extruder Flow rate * @parma rate: flow rate in % (default is 100) */ void setFlowRate(uint rate = 100); /** * @brief checkTemperature send M105 to the printer */ void checkTemperature(); /** * @brief close AtCore */ void close(); /** * @brief showMessage push a message to the printers LCD * @param message message to show on the LCD */ void showMessage(const QString &message); + /** + * @brief setUnits sets the measurement units do be used + * @param system : the measurement units (METRIC / IMPERIAL) + */ + void setUnits(MeasurementUnits units); + private slots: /** * @brief processQueue send commands from the queue. */ void processQueue(); private: void newMessage(const QByteArray &message); void findFirmware(const QByteArray &message); void findPlugins(); AtCorePrivate *d; };