diff --git a/src/core/atcore.cpp b/src/core/atcore.cpp index 25b55bd..18139d8 100644 --- a/src/core/atcore.cpp +++ b/src/core/atcore.cpp @@ -1,838 +1,841 @@ /* AtCore Copyright (C) <2016 - 2018> 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 { IFirmware *firmwarePlugin = nullptr;//!< @param firmwarePlugin: pointer to firmware plugin SerialLayer *serial = nullptr; //!< @param serial: pointer to the serial layer QPluginLoader pluginLoader; //!< @param pluginLoader: QPluginLoader QMap plugins; //!< @param plugins: Map of plugins name / path QByteArray lastMessage; //!< @param lastMessage: lastMessage from the printer int extruderCount = 1; //!< @param extruderCount: extruder count Temperature temperature; //!< @param temperature: Temperature object QStringList commandQueue; //!< @param commandQueue: the list of commands to send to the printer bool ready = false; //!< @param ready: True if printer is ready for a command QTimer *tempTimer = nullptr; //!< @param tempTimer: timer connected to the checkTemperature function float percentage; //!< @param percentage: print job percent QByteArray posString; //!< @param posString: stored string from last M114 return AtCore::STATES printerState; //!< @param printerState: State of the Printer QStringList serialPorts; //!< @param seralPorts: Detected serial Ports QTimer *serialTimer = nullptr; //!< @param serialTimer: Timer connected to locateSerialPorts bool sdCardMounted = false; //!< @param sdCardMounted: True if Sd Card is mounted. bool sdCardReadingFileList = false; //!< @param sdCardReadingFileList: True while getting file names from sd card bool sdCardPrinting = false; //!< @param sdCardPrinting: True if currently printing from sd card. QString sdCardFileName; //!< @param sdCardFileName: name of file being used from sd card. QStringList sdCardFileList; //!< @param sdCardFileList: List of files on sd card. }; AtCore::AtCore(QObject *parent) : QObject(parent), d(new AtCorePrivate) { //Register MetaTypes qRegisterMetaType("AtCore::STATES"); setState(AtCore::DISCONNECTED); //Create and start the timer that checks for temperature. d->tempTimer = new QTimer(this); d->tempTimer->setInterval(5000); d->tempTimer->setSingleShot(false); //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::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; } SerialLayer *AtCore::serial() const { return d->serial; } 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::DISCONNECTED) { qCWarning(ATCORE_CORE) << tr("Cant find firwmware, serial not connected!"); return; } if (state() == AtCore::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; } else if (message.contains("Grbl")) { loadFirmwarePlugin(QString::fromLatin1("grbl")); return; } else if (message.contains("Smoothie")) { loadFirmwarePlugin(QString::fromLatin1("smoothie")); return; } qCDebug(ATCORE_CORE) << "Waiting for firmware detect."; 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::CONNECTING); } else { //Plugin was loaded successfully. d->firmwarePlugin = qobject_cast(d->pluginLoader.instance()); firmwarePlugin()->init(this); disconnect(serial(), &SerialLayer::receivedCommand, this, &AtCore::findFirmware); connect(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")) { connect(d->tempTimer, &QTimer::timeout, this, &AtCore::checkTemperature); d->tempTimer->start(); } setState(IDLE); } } 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) { if (disableROC) { disableResetOnConnect(port); } d->serial = new SerialLayer(port, baud); connect(serial(), &SerialLayer::serialError, this, &AtCore::handleSerialError); if (serialInitialized() && d->serial->isWritable()) { setState(AtCore::CONNECTING); connect(serial(), &SerialLayer::receivedCommand, this, &AtCore::findFirmware); d->serialTimer->stop(); return true; } else { 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 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); } } -quint16 AtCore::serialTimerInterval() const +int AtCore::serialTimerInterval() const { if (d->serialTimer != nullptr) { return d->serialTimer->interval(); } return 0; } -void AtCore::setSerialTimerInterval(const quint16 &newTime) +void AtCore::setSerialTimerInterval(int newTime) { + if (newTime < 0) { + newTime = 0; + } if (!d->serialTimer) { //There is no timer. We need to create one. d->serialTimer = new QTimer(); connect(d->serialTimer, &QTimer::timeout, this, &AtCore::locateSerialPort); } //emit the newtime if it has changed. if (newTime != d->serialTimer->interval()) { emit serialTimerIntervalChanged(newTime); } //Start the timer. d->serialTimer->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::setRelativePosition() { pushCommand(GCode::toCommand(GCode::G91)); } void AtCore::setAbsolutePosition() { pushCommand(GCode::toCommand(GCode::G90)); } float AtCore::percentagePrinted() const { return d->percentage; } void AtCore::print(const QString &fileName, bool sdPrint) { if (state() == AtCore::CONNECTING) { qCDebug(ATCORE_CORE) << "Load a firmware plugin to print."; return; } //Start a print job. setState(AtCore::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::M23, fileName)); d->sdCardFileName = fileName; pushCommand(GCode::toCommand(GCode::M24)); setState(AtCore::BUSY); d->sdCardPrinting = true; connect(d->tempTimer, &QTimer::timeout, this, &AtCore::sdCardPrintStatus); 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. 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) { //Append command to the commandQueue 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::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::STOP); } if (firmwarePluginLoaded()) { disconnect(firmwarePlugin(), &IFirmware::readyForCommand, this, &AtCore::processQueue); disconnect(serial(), &SerialLayer::receivedCommand, this, &AtCore::newMessage); if (firmwarePlugin()->name() != QStringLiteral("Grbl")) { disconnect(d->tempTimer, &QTimer::timeout, this, &AtCore::checkTemperature); d->tempTimer->stop(); } //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); } //Do not reset the connect on disconnect when closing this will cause a reset on connect for the next connection. disconnect(serial(), &SerialLayer::serialError, this, &AtCore::handleSerialError); serial()->close(); //Clear our copy of the sdcard filelist clearSdCardFileList(); setState(AtCore::DISCONNECTED); d->serialTimer->start(); } } AtCore::STATES AtCore::state(void) { 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).value(), QVariant::fromValue(state).value()); d->printerState = state; if (state == AtCore::FINISHEDPRINT && d->sdCardPrinting) { //Clean up the sd card print d->sdCardPrinting = false; disconnect(d->tempTimer, &QTimer::timeout, this, &AtCore::sdCardPrintStatus); } emit stateChanged(d->printerState); } } void AtCore::stop() { //Stop a print job setState(AtCore::STOP); d->commandQueue.clear(); if (d->sdCardPrinting) { stopSdPrint(); } setExtruderTemp(0, 0); setBedTemp(0); home(AtCore::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::BUSY) { if (!d->sdCardPrinting) { //Stop our running print thread setState(AtCore::STOP); } } //push command through serial to bypass atcore's queue. serial()->pushCommand(GCode::toCommand(GCode::M112).toLocal8Bit()); } void AtCore::stopSdPrint() { //Stop an SdCard Print. pushCommand(GCode::toCommand(GCode::M25)); d->sdCardFileName = QString(); pushCommand(GCode::toCommand(GCode::M23, d->sdCardFileName)); AtCore::setState(AtCore::FINISHEDPRINT); AtCore::setState(AtCore::IDLE); } void AtCore::requestFirmware() { if (serialInitialized()) { qCDebug(ATCORE_CORE) << "Sending " << GCode::description(GCode::M115); serial()->pushCommand(GCode::toCommand(GCode::M115).toLocal8Bit()); } else { qCDebug(ATCORE_CORE) << "There is no open device to send commands"; } } bool AtCore::firmwarePluginLoaded() const { if (firmwarePlugin()) { return true; } else { return false; } } 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::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::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::M24)); } else { //Move back to previous coordinates. pushCommand(GCode::toCommand(GCode::G0, QString::fromLatin1(d->posString))); } setState(AtCore::BUSY); } /*~~~~~Control Slots ~~~~~~~~*/ void AtCore::home() { pushCommand(GCode::toCommand(GCode::G28)); } void AtCore::home(uchar axis) { QString args; if (axis & AtCore::X) { args.append(QStringLiteral("X0 ")); } if (axis & AtCore::Y) { args.append(QStringLiteral("Y0 ")); } if (axis & AtCore::Z) { args.append(QStringLiteral("Z0")); } pushCommand(GCode::toCommand(GCode::G28, args)); } void AtCore::setExtruderTemp(uint temp, uint extruder, bool andWait) { if (andWait) { pushCommand(GCode::toCommand(GCode::M109, QString::number(temp), QString::number(extruder))); } else { pushCommand(GCode::toCommand(GCode::M104, QString::number(extruder), QString::number(temp))); } } void AtCore::setBedTemp(uint temp, bool andWait) { if (andWait) { pushCommand(GCode::toCommand(GCode::M190, QString::number(temp))); } else { pushCommand(GCode::toCommand(GCode::M140, QString::number(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(AtCore::AXES axis, int arg) { const auto axisAsString = QMetaEnum::fromType().valueToKey(axis); move(QLatin1Char(axisAsString[0]), arg); } void AtCore::move(QLatin1Char axis, int arg) { pushCommand(GCode::toCommand(GCode::G1, QStringLiteral("%1 %2").arg(axis).arg(QString::number(arg)))); } 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()) { serial()->pushCommand(firmwarePlugin()->translate(text)); } else { 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::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(AtCore::UNITS units) { switch (units) { case AtCore::METRIC: pushCommand(GCode::toCommand(GCode::G21)); break; case AtCore::IMPERIAL: pushCommand(GCode::toCommand(GCode::G20)); break; } } QStringList AtCore::portSpeeds() const { return serial()->validBaudRates(); } void AtCore::disableMotors(uint delay) { //Disables motors if (delay) { pushCommand(GCode::toCommand(GCode::M84, QString::number(delay))); } else { pushCommand(GCode::toCommand(GCode::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::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::M30, fileName)); getSDFileList(); } else { qCDebug(ATCORE_CORE) << "Delete failed file not found:" << fileName; } } void AtCore::mountSd(uint slot) { pushCommand(GCode::toCommand(GCode::M21, QString::number(slot))); } void AtCore::umountSd(uint slot) { pushCommand(GCode::toCommand(GCode::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::M27))) { return; } pushCommand(GCode::toCommand(GCode::M27)); } void AtCore::disableResetOnConnect(const QString &port) { #if defined(Q_OS_UNIX) //should work on all unix' QProcess process; 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::DeviceNotFoundError): errorString = tr("Device not found"); break; case (QSerialPort::WriteError): errorString = tr("Unable to write to device"); break; case (QSerialPort::ReadError): errorString = tr("Unable to read from device"); break; case (QSerialPort::ResourceError): case (QSerialPort::TimeoutError): errorString = tr("The device no longer available"); closeConnection(); break; case (QSerialPort::UnsupportedOperationError): errorString = tr("Device does not support the operation"); break; case (QSerialPort::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 cac7a58..4873612 100644 --- a/src/core/atcore.h +++ b/src/core/atcore.h @@ -1,591 +1,591 @@ /* AtCore Copyright (C) <2016 - 2018> 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(),...) * - 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(quint16 serialTimerInterval READ serialTimerInterval WRITE setSerialTimerInterval NOTIFY serialTimerIntervalChanged) + 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, //! * @param port: the port to initialize * @param baud: the baud of the port * @param disableROC: atcore will attempt to disable reset on connect for this device. * @return True is connection was successful * @sa serialPorts(),serial(),closeConnection() */ Q_INVOKABLE bool initSerial(const QString &port, int baud, bool disableROC = false); /** * @brief Returns a list of valid baud speeds */ QStringList portSpeeds() const; /** * @brief Main access to the serialLayer * @return Current serialLayer * @sa initSerial(),serialPorts(),closeConnection() */ SerialLayer *serial() const; /** * @brief Close the current serial connection * @sa initSerial(),serial(),serialPorts(),AtCore::close() */ Q_INVOKABLE void closeConnection(); /** * @brief Main access to the loaded firmware plugin * @return IFirmware * to currently loaded plugin * @sa availableFirmwarePlugins(),loadFirmwarePlugin() */ Q_INVOKABLE IFirmware *firmwarePlugin() const; /** * @brief List of available firmware plugins * @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 */ AtCore::STATES state(void); /** * @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 */ - quint16 serialTimerInterval() const; + int serialTimerInterval() 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 between serial timer * @sa setSerialTimerInterval() */ - void serialTimerIntervalChanged(const quint16 newTime); + void serialTimerIntervalChanged(const int newTime); /** * @brief The Printer's State Changed * @param newState : the new state of the printer * @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); public slots: /** * @brief Set the printers state * @param state : printer state. * @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() */ Q_INVOKABLE void stop(); /** * @brief stop the printer via the emergency stop Command (M112) * @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() */ void pause(const QString &pauseActions); /** * @brief resume a paused print job. * After returning to location pause was triggered. * @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, int 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, int 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. 0 will Disable Checks. + * @param newTime: Milliseconds between checks. values <= 0 will Disable Checks. */ - void setSerialTimerInterval(const quint16 &newTime); + void setSerialTimerInterval(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 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 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() */ void stopSdPrint(); /** * @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/src/core/gcodecommands.h b/src/core/gcodecommands.h index e2e17f3..6634bee 100644 --- a/src/core/gcodecommands.h +++ b/src/core/gcodecommands.h @@ -1,144 +1,144 @@ /* AtCore Copyright (C) <2016> Authors: Lays Rodrigues Tomaz Canabrava Patrick José Pereira Chris Rizzitello 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 "atcore_export.h" /** * @brief The GCode class * Provides Descriptions and Commands strings for G and M Commands */ class ATCORE_EXPORT GCode { Q_GADGET friend class GCodeTests; public: /** * @brief The GCommands enum */ enum GCommands { G0, G1, G2, G3, G4, G10 = 10, G11, G20 = 20, G21, G22, G23, G28 = 28, G29, G30, G31, G32, G33, G90 = 90, G91, G92, G100 = 100, G130 = 130, G131, G132, G133, G161 = 161, G162 }; - Q_ENUM(GCommands); + Q_ENUM(GCommands) /** * @brief The MCommands enum */ enum MCommands { M0, M1, M2, M6 = 6, M17 = 17, M18, M20 = 20, M21, M22, M23, M24, M25, M26, M27, M28, M29, M30, M31, M32, M33, M34, M36 = 36, M37, M38, M40 = 40, M41, M42, M43, M48 = 48, M70 = 70, M72 = 72, M73, M80 = 80, M81, M82, M83, M84, M85, M92 = 92, M93, M98 = 98, M99, M101 = 101, M102, M103, M104, M105, M106, M107, M108, M109, M110, M111, M112, M113, M114, M115, M116, M117, M118, M119, M120, M121, M122, M123, M124, M126 = 126, M127, M128, M129, M130, M131, M132, M133, M134, M135, M136, M140 = 140, M141, M142, M143, M144, M146 = 146, M149 = 149, M150 = 150, M160 = 160, M163 = 163, M164 = 164, M190 = 190, M191, M200 = 200, M201, M202, M203, M204, M205, M206, M207, M208, M209, M210, M211, M212, M218 = 218, M220 = 220, M221, M222, M223, M224, M225, M226, M227, M228, M229, M230, M231, M232, M240 = 240, M241, M245 = 245, M246, M250 = 250, M251, M280 = 280, M300 = 300, M301, M302, M303, M304, M305, M306, M320 = 320, M321, M322, M323, M340 = 340, M350 = 350, M351, M355 = 355, M360 = 360, M361, M362, M363, M364, M365, M366, M370 = 370, M371, M372, M373, M374, M375, M380 = 380, M381, M400 = 400, M401, M402, M404 = 404, M405, M406, M407, M408, M420 = 420, M421, M450 = 450, M451, M452, M453, M460 = 460, M500 = 500, M501, M502, M503, M540 = 540, M550 = 550, M551, M552, M553, M554, M555, M556, M557, M558, M559, M560, M561, M562, M563, M564, M565, M566, M567, M568, M569, M570, M571, M572, M573, M574, M575, M577 = 577, M578, M579, M580 = 580, M581, M582, M583, M584, M600 = 600, M605 = 605, M665 = 665, M666 = 666, M667, M668, M700 = 700, M701, M702, M703, M710 = 710, M800 = 800, M801, M851 = 851, M906 = 906, M907, M908, M910 = 910, M911, M912, M913, M928 = 928, M997 = 997, M998, M999 }; - Q_ENUM(MCommands); + Q_ENUM(MCommands) /** * @brief Return Description of command \p gcode * @param gcode: Command to describe * @return description of GCommand */ static QString description(GCommands gcode); /** * @brief Return Description of command \p gcode * @param gcode: Command to describe * @return description of MCommand */ static QString description(MCommands gcode); /** * @brief Convert GCode::GCommands to command * @param gcode: GCode::GCommands * @param value1: Value of argument * @return Command String to send to printer */ static QString toCommand(GCommands gcode, const QString &value1 = QString()); /** * @brief Convert GCode::MCommands to command * @param gcode: GCode::MCommands * @param value1: Value of argument 1 * @param value2: Value of argument 2 * @return Command String to send to printer */ static QString toCommand(MCommands gcode, const QString &value1 = QString(), const QString &value2 = QString()); protected: static const QString commandRequiresArgument; static const QString commandNotSupported; }; diff --git a/src/core/seriallayer.cpp b/src/core/seriallayer.cpp index d905331..3151b9a 100644 --- a/src/core/seriallayer.cpp +++ b/src/core/seriallayer.cpp @@ -1,159 +1,159 @@ /* AtCore Copyright (C) <2016 - 2018> Authors: Patrick José Pereira Chris Rizzitello Tomaz Canabrava 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 "seriallayer.h" Q_LOGGING_CATEGORY(SERIAL_LAYER, "org.kde.atelier.core.serialLayer") namespace { QByteArray _return = QByteArray("\r"); QByteArray _newLine = QByteArray("\n"); QByteArray _newLineReturn = QByteArray("\n\r"); QStringList _validBaudRates = { QStringLiteral("9600"), QStringLiteral("14400"), QStringLiteral("19200"), QStringLiteral("28800"), QStringLiteral("38400"), QStringLiteral("57600"), QStringLiteral("76800"), QStringLiteral("115200"), QStringLiteral("230400"), QStringLiteral("250000"), QStringLiteral("500000"), QStringLiteral("1000000") }; } /** * @brief The SerialLayerPrivate class */ class SerialLayer::SerialLayerPrivate { public: bool _serialOpened; //!< @param _serialOpened: is serial port opened QSerialPort::SerialPortError _lastError; //!< @param _lastError: the last reported error QByteArray _rawData; //!< @param _rawData: the raw serial data QVector _rByteCommands; //!< @param _rByteCommand: received Messages QVector _sByteCommands; //!< @param _sByteCommand: sent Messages }; -SerialLayer::SerialLayer(const QString &port, uint baud, QObject *parent) : +SerialLayer::SerialLayer(const QString &port, int32_t baud, QObject *parent) : QSerialPort(parent), d(new SerialLayerPrivate()) { setPortName(port); setBaudRate(baud); if (open(QIODevice::ReadWrite)) { d->_serialOpened = true; connect(this, &QSerialPort::readyRead, this, &SerialLayer::readAllData); connect(this, &QSerialPort::errorOccurred, this, &SerialLayer::handleError); } }; void SerialLayer::readAllData() { d->_rawData.append(readAll()); //Remove any \r in the string, then split by \n. //This removes any trailing \r or \n from the commands // Proper line endings are added when the command is pushed. d->_rawData = d->_rawData.replace(_return, QByteArray()); QList tempList = d->_rawData.split(_newLine.at(0)); for (auto i = tempList.begin(); i != tempList.end(); ++i) { // Get finished line to _byteCommands if (i < tempList.end() - 1) { d->_rByteCommands.append(*i); emit receivedCommand(*i); } else { d->_rawData.clear(); d->_rawData.append(*i); } } } void SerialLayer::pushCommand(const QByteArray &comm, const QByteArray &term) { if (!isOpen()) { qCDebug(SERIAL_LAYER) << "Serial not connected !"; return; } QByteArray tmp = comm + term; write(tmp); emit pushedCommand(tmp); } void SerialLayer::pushCommand(const QByteArray &comm) { pushCommand(comm, _newLineReturn); } void SerialLayer::add(const QByteArray &comm, const QByteArray &term) { QByteArray tmp = comm + term; d->_sByteCommands.append(tmp); } void SerialLayer::add(const QByteArray &comm) { add(comm, _newLineReturn); } void SerialLayer::push() { if (!isOpen()) { qCDebug(SERIAL_LAYER) << "Serial not connected !"; return; } for (const auto &comm : qAsConst(d->_sByteCommands)) { write(comm); emit pushedCommand(comm); } d->_sByteCommands.clear(); } bool SerialLayer::commandAvailable() const { return !d->_rByteCommands.isEmpty(); } QStringList SerialLayer::validBaudRates() const { return _validBaudRates; } void SerialLayer::handleError(QSerialPort::SerialPortError error) { if (d->_lastError == error) { return; } d->_lastError = error; emit serialError(error); } diff --git a/src/core/seriallayer.h b/src/core/seriallayer.h index b43ef09..0c0609c 100644 --- a/src/core/seriallayer.h +++ b/src/core/seriallayer.h @@ -1,136 +1,136 @@ /* AtCore Copyright (C) <2016 - 2018> Authors: Patrick José Pereira Chris Rizzitello Tomaz Canabrava 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 "atcore_export.h" /** * @brief The SerialLayer class. * Provide the low level serial operations */ class ATCORE_EXPORT SerialLayer : public QSerialPort { Q_OBJECT private: class SerialLayerPrivate; SerialLayerPrivate *d; /** * @brief Read all available serial data * */ void readAllData(); signals: /** * @brief Emit signal when command is pushed * * @param comm : Command */ void pushedCommand(const QByteArray &comm); /** * @brief Emit signal when command is received * * @param comm : Command */ void receivedCommand(const QByteArray &comm); /** * @brief Emit a signal if an error has happened. * @param error: the Error */ void serialError(QSerialPort::SerialPortError error); public: /** * @brief SerialLayer Class to realize communication * * @param port : Port (/dev/ttyUSB ACM) * @param baud : Baud rate (115200) * @param parent : Parent */ - SerialLayer(const QString &port, uint baud, QObject *parent = nullptr); + SerialLayer(const QString &port, int32_t baud, QObject *parent = nullptr); /** * @brief Add command to be pushed * * @param comm : Command * @param term : Terminator */ void add(const QByteArray &comm, const QByteArray &term); /** * @brief Add command to be pushed * * @param comm : Command, default terminator will be used */ void add(const QByteArray &comm); /** * @brief handleError Handle Errors from the serial port * @param error: The reported error */ void handleError(QSerialPort::SerialPortError error); /** * @brief Push command directly * * @param comm : Command * @param term : Terminator */ void pushCommand(const QByteArray &comm, const QByteArray &term); /** * @brief Push command directly * * @param comm : Command, default terminator will be used */ void pushCommand(const QByteArray &comm); /** * @brief Push all commands used in add to serial write * */ void push(); /** * @brief Check if is a command available * * @return bool */ bool commandAvailable() const; /** * @brief Return a QStringList of valids serial baud rates * * @return QStringList */ QStringList validBaudRates() const; }; diff --git a/src/widgets/logwidget.cpp b/src/widgets/logwidget.cpp index 4ce887e..a9becbe 100644 --- a/src/widgets/logwidget.cpp +++ b/src/widgets/logwidget.cpp @@ -1,161 +1,161 @@ /* AtCore Test Client Copyright (C) <2018> Author: Chris Rizzitello - rizzitello@kde.org Patrick José Pereira 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 "logwidget.h" #include #include #include #include #include #include #include LogWidget::LogWidget(QTemporaryFile *tempFile, QWidget *parent) : QWidget(parent), logFile(tempFile) { QSize iconSize = QSize(fontMetrics().height(), fontMetrics().height()); auto *page = new QWidget; textLog = new QPlainTextEdit; textLog->setReadOnly(true); textLog->setMaximumBlockCount(1000); auto pageLayout = new QVBoxLayout; pageLayout->addWidget(textLog); page->setLayout(pageLayout); QStackedWidget *mainStack = new QStackedWidget; mainStack->insertWidget(0, page); page = new QWidget; auto textbox = new QTextEdit; textbox->setReadOnly(true); textbox->setHtml(tr("\

Special Log Entries

\

Failed to open device in read/write mode.

\

    The Device was not able to be opened.

\

       Check the device is not opened by another program.

\

       Check you have the correct permissions to open the device.

\

No plugin found for <Detected Firmware>

\

    Firmware plugins are missing or your firmware is not currently supported.

\

      Manually select the Marlin or Repetier plugin.

\

      If your firmware does not have a plugin please let us know.

\

Lots of “Waiting for firmware detect”

\

    Unable to send the firmware detect waiting for printer to restart

\

      Push the restart button on your printer or turn it on and off.

\

      Select a firmware plugin in place of auto detect.

")); pageLayout = new QVBoxLayout; pageLayout->addWidget(textbox); page->setLayout(pageLayout); mainStack->insertWidget(1, page); auto saveButton = new QPushButton(QIcon::fromTheme(QStringLiteral("document-save"), style()->standardIcon(QStyle::SP_DialogSaveButton)), tr("Save Session Log")); saveButton->setIconSize(iconSize); connect(saveButton, &QPushButton::clicked, this, &LogWidget::savePressed); auto helpButton = new QToolButton(); helpButton->setCheckable(true); helpButton->setChecked(false); helpButton->setIconSize(iconSize); helpButton->setIcon(QIcon::fromTheme(QStringLiteral("help-about"), style()->standardIcon(QStyle::SP_DialogHelpButton))); - connect(helpButton, &QToolButton::clicked, this, [this, mainStack](bool checked) { + connect(helpButton, &QToolButton::clicked, this, [mainStack](bool checked) { mainStack->setCurrentIndex(checked); }); auto buttonLayout = new QHBoxLayout; buttonLayout->addWidget(saveButton); buttonLayout->addWidget(helpButton); auto mainLayout = new QVBoxLayout; mainLayout->addWidget(mainStack); mainLayout->addLayout(buttonLayout); setLayout(mainLayout); } QString LogWidget::getTime() { return QTime::currentTime().toString(QStringLiteral("hh:mm:ss:zzz")); } void LogWidget::appendLog(const QString &msg) { QString message(QStringLiteral("[%1] %2").arg(getTime(), msg)); textLog->appendPlainText(message); writeTempFile(message); } void LogWidget::appendRLog(const QByteArray &bmsg) { QString message(QStringLiteral("[%1]< %2").arg(getTime(), QString::fromUtf8(bmsg))); textLog->appendPlainText(message); writeTempFile(message); } void LogWidget::appendSLog(const QByteArray &bmsg) { QString msg = QString::fromUtf8(bmsg); QRegExp _newLine(QChar::fromLatin1('\n')); QRegExp _return(QChar::fromLatin1('\r')); msg.replace(_newLine, QStringLiteral("\\n")); msg.replace(_return, QStringLiteral("\\r")); QString message(QStringLiteral("[%1]> %2").arg(getTime(), msg)); textLog->appendPlainText(message); writeTempFile(message); } void LogWidget::writeTempFile(QString text) { //Add text to our unsynced string list when that hits 100 sync to the temp file. unsyncedStrings.append(text); if (unsyncedStrings.count() > 100) { flushTemp(); } } void LogWidget::flushTemp() { /* A QTemporaryFile will always be opened in QIODevice::ReadWrite mode, this allows easy access to the data in the file. This function will return true upon success and will set the fileName() to the unique filename used. */ logFile->open(); logFile->seek(logFile->size()); for (const auto &string : qAsConst(unsyncedStrings)) { logFile->write(string.toLatin1()); logFile->putChar('\n'); } logFile->close(); unsyncedStrings.clear(); } void LogWidget::savePressed() { //If saving be sure we have flushed to temp. flushTemp(); // Note that if a file with the name newName already exists, copy() returns false (i.e. QFile will not overwrite it). QString fileName = QDir::homePath() + QChar::fromLatin1('/') + QFileInfo(logFile->fileName()).fileName() + QStringLiteral(".txt"); QString saveFileName = QFileDialog::getSaveFileName(this, tr("Save Log to file"), fileName); QFile::copy(logFile->fileName(), saveFileName); logFile->close(); } bool LogWidget::endsWith(const QString &string) { return textLog->toPlainText().endsWith(string); } diff --git a/src/widgets/movementwidget.h b/src/widgets/movementwidget.h index 94967c9..6514838 100644 --- a/src/widgets/movementwidget.h +++ b/src/widgets/movementwidget.h @@ -1,93 +1,93 @@ /* AtCore Test Client Copyright (C) <2018> Author: Chris Rizzitello - rizzitello@kde.org 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 #include #include "atcorewidgets_export.h" /** * @brief The MovementWidget class * This widget will provide Basic Movement Controls. Create it with "showHomeAndDisableWidgets" false if your client provides its own actions for homing and disabling the motors. */ class ATCOREWIDGETS_EXPORT MovementWidget : public QWidget { Q_OBJECT public: /** * @brief Create a Movement Widget * @param showHomeAndDisableWidgets: set False to hide the Home and Disable Motors buttons [default = true] * @param parent: Parent of this widget. */ MovementWidget(bool showHomeAndDisableWidgets = true, QWidget *parent = nullptr); signals: /** * @brief The Home All button was clicked. * This should be connected to AtCore::home() */ void homeAllPressed(); /** * @brief The Home X button was clicked. * This should be connected to AtCore::home(AtCore::X) */ void homeXPressed(); /** * @brief The Home Y button was clicked. * This should be connected to AtCore::home(AtCore::Y) */ void homeYPressed(); /** * @brief The Home Z button was clicked. * This should be connected to AtCore::home(AtCore::Z) */ void homeZPressed(); /** * @brief The Disable Motors button was clicked. * This should be connected to AtCore::disableMotors(0) */ void disableMotorsPressed(); /** * @brief An absoluteMove was requested * This should be connected to AtCore::move(axis,value) * @param axis: the axis to move * @param value: where to move */ - void absoluteMove(const QLatin1Char &axis, const double &value); + void absoluteMove(const QLatin1Char &axis, const double value); /** * @brief A relativeMove was requested from the AxisControl * This should connect to a function that does the following * AtCore::setRelativePosition() * AtCore::move(axis, value) * AtCore::setAbsolutePosition() * @param axis: the axis to move. * @param value: the value to move it by. */ - void relativeMove(const QLatin1Char &axis, const double &value); + void relativeMove(const QLatin1Char &axis, const double value); private: QComboBox *comboMoveAxis = nullptr; QDoubleSpinBox *sbMoveAxis = nullptr; }; diff --git a/src/widgets/plotwidget.h b/src/widgets/plotwidget.h index a89071f..3ef9ee0 100644 --- a/src/widgets/plotwidget.h +++ b/src/widgets/plotwidget.h @@ -1,132 +1,132 @@ /* Atelier KDE Printer Host for 3D Printing Copyright (C) <2016> Author: Patrick José Pereira - patrickjp@kde.org Tomaz Canabrava Lays Rodrigues 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 . */ #pragma once #include #include #include #include "atcorewidgets_export.h" /** * @brief PlotWidget Show a graph of the temperature over time. */ class ATCOREWIDGETS_EXPORT PlotWidget : public QWidget { Q_OBJECT public: explicit PlotWidget(QWidget *parent = nullptr); ~PlotWidget(); /** * @brief Create a new plot * @param name p_name: plot name */ void addPlot(const QString &name); /** * @brief Delete plot with name * @param name p_name: name */ void removePlot(const QString &name); /** * @brief Append point to plot * @param name p_name: plot name * @param value p_value: value */ void appendPoint(const QString &name, float value); /** * @brief plots * @return List of all named plots */ QStringList plots(); /** * @brief set The Maximum Number of points per series the plot widget stores - * @param newMax: new maximum Number (default:24) + * @param newMax: new maximum Number (default:120) */ void setMaximumPoints(const uint newMax); /** * @brief set the maximum temperature shown on the plot * @param maxTemp : number greater then 0 */ void setMaximumTemperature(const uint maxTemp); private: class plot { public: explicit plot() : _series(new QLineSeries()) { - }; + } ~plot() { // Series will be deleted with chart - }; + } void pushPoint(float value) { QDateTime now = QDateTime::currentDateTime(); _series->append(now.toMSecsSinceEpoch(), value); } void setName(const QString &name) { _name = name; _series->setName(_name); //Add 3 initial points to plot QDateTime now = QDateTime::currentDateTime(); _series->append(now.toMSecsSinceEpoch() - 2 * 60e3, 0.0); _series->append(now.toMSecsSinceEpoch() - 60e3, 0.0); _series->append(now.toMSecsSinceEpoch(), 0.0); } QLineSeries *serie() const { return _series; } QString name() const { return _name; } private: QLineSeries *_series; QString _name; }; /** * @brief Update plot list, need to run after ALL plots added */ void update(); QChartView *_chart; QDateTimeAxis *_axisX; QValueAxis *_axisY; QMap _plots; int m_maximumPoints; }; diff --git a/src/widgets/statuswidget.cpp b/src/widgets/statuswidget.cpp index 944a71b..45d430f 100644 --- a/src/widgets/statuswidget.cpp +++ b/src/widgets/statuswidget.cpp @@ -1,118 +1,118 @@ /* AtCore Test Client Copyright (C) <2018> Author: Chris Rizzitello - rizzitello@kde.org 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 "statuswidget.h" StatusWidget::StatusWidget(bool showStop, QWidget *parent) : QWidget(parent) { //first create the item for the print Progress. auto hBoxLayout = new QHBoxLayout; printingProgress = new QProgressBar; printingProgress->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); hBoxLayout->addWidget(printingProgress); if (showStop) { auto newButton = new QPushButton(style()->standardIcon(QStyle::SP_BrowserStop), QString()); connect(newButton, &QPushButton::clicked, this, [this] { emit stopPressed(); }); hBoxLayout->addWidget(newButton); } lblTime = new QLabel(QStringLiteral("00:00:00")); lblTime->setAlignment(Qt::AlignHCenter); auto newLabel = new QLabel(QStringLiteral(" / ")); lblTimeLeft = new QLabel(QStringLiteral("??:??:??")); lblTimeLeft->setAlignment(Qt::AlignHCenter); hBoxLayout->addWidget(lblTime); hBoxLayout->addWidget(newLabel); hBoxLayout->addWidget(lblTimeLeft); printProgressWidget = new QWidget(); printProgressWidget->setLayout(hBoxLayout); //Then Create the full bar. newLabel = new QLabel(tr("AtCore State:")); lblState = new QLabel(tr("Not Connected")); lblSd = new QLabel(); spacer = new QSpacerItem(10, 20, QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); hBoxLayout = new QHBoxLayout; hBoxLayout->addWidget(newLabel); hBoxLayout->addWidget(lblState); hBoxLayout->addSpacerItem(new QSpacerItem(5, 20, QSizePolicy::Fixed)); hBoxLayout->addWidget(lblSd); hBoxLayout->addSpacerItem(spacer); hBoxLayout->addWidget(printProgressWidget); setLayout(hBoxLayout); printTime = new QTime(); printTimer = new QTimer(); printTimer->setInterval(1000); printTimer->setSingleShot(false); connect(printTimer, &QTimer::timeout, this, &StatusWidget::updatePrintTime); } void StatusWidget::setSD(bool hasSd) { QString labelText = hasSd ? tr("SD") : QString(); lblSd->setText(labelText); } void StatusWidget::setState(const QString &state) { lblState->setText(state); } void StatusWidget::showPrintArea(bool visible) { printProgressWidget->setVisible(visible); if (visible) { spacer->changeSize(10, 20, QSizePolicy::Fixed, QSizePolicy::Fixed); printTime->start(); printTimer->start(); } else { printTimer->stop(); spacer->changeSize(10, 20, QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); } } void StatusWidget::updatePrintTime() { QTime temp(0, 0, 0); lblTime->setText(temp.addMSecs(printTime->elapsed()).toString(QStringLiteral("hh:mm:ss"))); } -void StatusWidget::updatePrintProgress(const float &progress) +void StatusWidget::updatePrintProgress(const int progress) { printingProgress->setValue(progress); if (progress >= 1) { QTime temp(0, 0, 0); lblTimeLeft->setText(temp.addMSecs((100 - progress) * (printTime->elapsed() / progress)).toString(QStringLiteral("hh:mm:ss"))); } else { lblTimeLeft->setText(QStringLiteral("??:??:??")); } } diff --git a/src/widgets/statuswidget.h b/src/widgets/statuswidget.h index 54a2467..4544c80 100644 --- a/src/widgets/statuswidget.h +++ b/src/widgets/statuswidget.h @@ -1,76 +1,76 @@ /* AtCore Test Client Copyright (C) <2018> Author: Chris Rizzitello - rizzitello@kde.org 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 #include #include #include #include "atcorewidgets_export.h" /** * @brief The StatusWidget class Status Bar information for atcore */ class ATCOREWIDGETS_EXPORT StatusWidget : public QWidget { Q_OBJECT public: /** * @brief Make A new Status widget * @param showStop: Set False if your client has the print job stop in another widget. * @param parent: parent of this widget. */ StatusWidget(bool showStop = true, QWidget *parent = nullptr); /** * @brief Set if the status area should show SD card inserted. * @param hasSd */ void setSD(bool hasSd); /** * @brief Set the State String * @param state: String to be shown */ void setState(const QString &state); /** * @brief Show or hide the Print progress and time * @param visible : true for show */ void showPrintArea(bool visible); /** * @brief Update the progres to the new progress * @param progress: new progress. */ - void updatePrintProgress(const float &progress); + void updatePrintProgress(const int progress); signals: void stopPressed(); private slots: void updatePrintTime(); private: QLabel *lblState = nullptr; QLabel *lblSd = nullptr; QLabel *lblTime = nullptr; QLabel *lblTimeLeft = nullptr; QTime *printTime = nullptr; QTimer *printTimer = nullptr; QSpacerItem *spacer = nullptr; QProgressBar *printingProgress = nullptr; QWidget *printProgressWidget = nullptr; };