File indexing completed on 2024-05-12 04:41:10
0001 /* AtCore KDE Libary for 3D Printers 0002 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0003 SPDX-FileCopyrightText: 2017-2018, 2020 Chris Rizzitello <rizzitello@kde.org> 0004 SPDX-FileCopyrightText: 2017-2018 Patrick José Pereira <patrickjp@kde.org> 0005 SPDX-FileCopyrightText: 2018 Tomaz Canabrava <tcanabrava@kde.org> 0006 SPDX-FileCopyrightText: 2018 Leandro Santiago <leandrosansilva@gmail.com> 0007 */ 0008 0009 #include <QCommandLineOption> 0010 #include <QCommandLineParser> 0011 #include <QLoggingCategory> 0012 #include <QRegularExpression> 0013 #include <QTextStream> 0014 #include <QTime> 0015 0016 #include "printthread.h" 0017 0018 Q_LOGGING_CATEGORY(PRINT_THREAD, "org.kde.atelier.core.printThread") 0019 /** 0020 * @brief The PrintThreadPrivate class 0021 */ 0022 class PrintThread::PrintThreadPrivate 0023 { 0024 public: 0025 AtCore *core = nullptr; //!<@param core: Pointer to AtCore 0026 QTextStream *gcodestream = nullptr; //!<@param gcodestream: Steam the job is read from 0027 float printProgress = 0; //!<@param printProgress: Progress of the print job 0028 qint64 totalSize = 0; //!<@param totalSize: total file size 0029 qint64 stillSize = 0; //!<@param stillSize: remaining file 0030 QString cline; //!<@param cline: current line 0031 AtCore::STATES state = AtCore::IDLE; //!<@param state: printer state 0032 QFile *file = nullptr; //!<@param file: gcode File to stream from 0033 QList<QCommandLineOption> options = {{QCommandLineOption(QStringLiteral("pause"))}, 0034 {QCommandLineOption(QStringLiteral("extruder temperature"))}, 0035 {QCommandLineOption(QStringLiteral("bed temperature"))}, 0036 {QCommandLineOption(QStringLiteral("print speed"))}, 0037 {QCommandLineOption(QStringLiteral("fan speed"))}, 0038 {QCommandLineOption(QStringLiteral("flow rate"))}, 0039 {QCommandLineOption(QStringLiteral("message"))}, 0040 {QCommandLineOption(QStringLiteral("command"))}}; //!<@param options: injectable commands. 0041 }; 0042 0043 PrintThread::PrintThread(AtCore *parent, const QString &fileName) 0044 : d(new PrintThreadPrivate) 0045 { 0046 d->core = parent; 0047 d->state = d->core->state(); 0048 d->file = new QFile(fileName); 0049 d->file->open(QFile::ReadOnly); 0050 d->totalSize = d->file->bytesAvailable(); 0051 d->stillSize = d->totalSize; 0052 d->gcodestream = new QTextStream(d->file); 0053 } 0054 0055 PrintThread::~PrintThread() 0056 { 0057 delete d; 0058 } 0059 0060 void PrintThread::start() 0061 { 0062 // we only want to do this when printing 0063 connect(d->core->firmwarePlugin(), &IFirmware::readyForCommand, this, &PrintThread::processJob, Qt::QueuedConnection); 0064 connect(this, &PrintThread::nextCommand, d->core, &AtCore::pushCommand, Qt::QueuedConnection); 0065 connect(this, &PrintThread::stateChanged, d->core, &AtCore::setState, Qt::QueuedConnection); 0066 connect(d->core, &AtCore::stateChanged, this, &PrintThread::setState, Qt::QueuedConnection); 0067 connect(this, &PrintThread::finished, this, &PrintThread::deleteLater); 0068 // force a command if the printer doesn't send "wait" when idle 0069 processJob(); 0070 } 0071 0072 void PrintThread::processJob() 0073 { 0074 if (d->gcodestream->atEnd()) { 0075 endPrint(); 0076 } 0077 0078 switch (d->state) { 0079 case AtCore::STARTPRINT: 0080 case AtCore::IDLE: 0081 case AtCore::BUSY: 0082 setState(AtCore::BUSY); 0083 nextLine(); 0084 while (d->cline.isEmpty() && !d->gcodestream->atEnd()) { 0085 nextLine(); 0086 } 0087 if (!d->cline.isEmpty() && d->core->state() != AtCore::PAUSE) { 0088 qCDebug(PRINT_THREAD) << "cline:" << d->cline; 0089 Q_EMIT nextCommand(d->cline); 0090 } 0091 break; 0092 0093 case AtCore::ERRORSTATE: 0094 qCDebug(PRINT_THREAD) << "Error State"; 0095 break; 0096 0097 case AtCore::STOP: { 0098 endPrint(); 0099 break; 0100 } 0101 0102 case AtCore::PAUSE: 0103 if (d->cline.startsWith(QStringLiteral(";-"))) { 0104 nextLine(); 0105 } 0106 break; 0107 0108 default: 0109 qCDebug(PRINT_THREAD) << "Unknown State"; 0110 break; 0111 } 0112 } 0113 0114 void PrintThread::endPrint() 0115 { 0116 Q_EMIT printProgressChanged(100); 0117 qCDebug(PRINT_THREAD) << "atEnd"; 0118 disconnect(d->core->firmwarePlugin(), &IFirmware::readyForCommand, this, &PrintThread::processJob); 0119 disconnect(this, &PrintThread::nextCommand, d->core, &AtCore::pushCommand); 0120 disconnect(d->core, &AtCore::stateChanged, this, &PrintThread::setState); 0121 Q_EMIT stateChanged(AtCore::FINISHEDPRINT); 0122 Q_EMIT stateChanged(AtCore::IDLE); 0123 disconnect(this, &PrintThread::stateChanged, d->core, &AtCore::setState); 0124 Q_EMIT finished(); 0125 } 0126 void PrintThread::nextLine() 0127 { 0128 d->cline = d->gcodestream->readLine(); 0129 qCDebug(PRINT_THREAD) << "Nextline:" << d->cline; 0130 d->stillSize -= d->cline.size() + 1; // remove read chars 0131 d->printProgress = float(d->totalSize - d->stillSize) * 100 / float(d->totalSize); 0132 qCDebug(PRINT_THREAD) << "progress:" << QString::number(double(d->printProgress)); 0133 Q_EMIT printProgressChanged(d->printProgress); 0134 0135 if (d->cline.startsWith(QStringLiteral(";-"))) { 0136 injectCommand(d->cline); 0137 d->cline = QLatin1String(""); 0138 return; 0139 } 0140 // Remove Comments from the gcode. 0141 // Type 1: Anything after ; is comment. 0142 // Example G28 Z; Home Axis Z 0143 if (d->cline.contains(QChar::fromLatin1(';'))) { 0144 d->cline.resize(d->cline.indexOf(QChar::fromLatin1(';'))); 0145 } 0146 // Type 2: Block Type anything between ( and ) is a comment 0147 // Example G28 (Home)Z 0148 if (d->cline.contains(QChar::fromLatin1('('))) { 0149 // Remove (.....) from the line 0150 d->cline.remove(QRegularExpression(QStringLiteral(".(?<=[(])(.*)(?=[)])."))); 0151 } 0152 0153 d->cline = d->cline.simplified(); 0154 } 0155 0156 void PrintThread::setState(const AtCore::STATES &newState) 0157 { 0158 if (d->state == AtCore::STATES::DISCONNECTED && (newState == AtCore::STATES::PAUSE || newState == AtCore::STATES::STOP)) { 0159 qCDebug(PRINT_THREAD) << "Serial not connected !"; 0160 return; 0161 } 0162 if (newState != d->state) { 0163 qCDebug(PRINT_THREAD) << QStringLiteral("State changed from [%1] to [%2]").arg(QVariant::fromValue(d->state).toString(), QVariant::fromValue(newState).toString()); 0164 disconnect(d->core, &AtCore::stateChanged, this, &PrintThread::setState); 0165 d->state = newState; 0166 Q_EMIT stateChanged(d->state); 0167 connect(d->core, &AtCore::stateChanged, this, &PrintThread::setState, Qt::QueuedConnection); 0168 } 0169 } 0170 0171 void PrintThread::injectCommand(QString &command) 0172 { 0173 // remove the ; 0174 command.remove(0, 1); 0175 command.prepend(QStringLiteral("0:")); 0176 QStringList cmd = command.split(QLatin1Char(':')); 0177 cmd.replace(1, cmd.at(1).simplified().toLower()); 0178 cmd.replace(2, cmd.at(2).simplified()); 0179 0180 static QCommandLineParser parser; 0181 if (parser.optionNames().isEmpty()) { 0182 parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); 0183 parser.addOptions(d->options); 0184 } 0185 0186 qCDebug(PRINT_THREAD) << "attempting to inject " << cmd; 0187 parser.process(cmd); 0188 0189 if (parser.isSet(QStringLiteral("pause"))) { 0190 d->core->pause(parser.positionalArguments().at(0)); 0191 } else if (parser.isSet(QStringLiteral("extruder temperature"))) { 0192 QStringList args = parser.positionalArguments().at(0).split(QLatin1Char(',')); 0193 bool wait = !QString::compare(args.at(2).simplified(), QStringLiteral("true"), Qt::CaseInsensitive); 0194 d->core->setExtruderTemp(args.at(0).toUInt(), args.at(1).toUInt(), wait); 0195 } else if (parser.isSet(QStringLiteral("bed temperature"))) { 0196 QStringList args = parser.positionalArguments().at(0).split(QLatin1Char(',')); 0197 bool wait = !QString::compare(args.at(1).simplified(), QStringLiteral("true"), Qt::CaseInsensitive); 0198 d->core->setBedTemp(args.at(0).toUInt(), wait); 0199 } else if (parser.isSet(QStringLiteral("print speed"))) { 0200 d->core->setPrinterSpeed(parser.positionalArguments().at(0).toUInt()); 0201 } else if (parser.isSet(QStringLiteral("fan speed"))) { 0202 d->core->setFanSpeed(parser.positionalArguments().at(0).toUInt(), parser.positionalArguments().at(1).toUInt()); 0203 } else if (parser.isSet(QStringLiteral("flow rate"))) { 0204 d->core->setFlowRate(parser.positionalArguments().at(0).toUInt()); 0205 } else if (parser.isSet(QStringLiteral("message"))) { 0206 d->core->showMessage(parser.positionalArguments().at(0)); 0207 } else if (parser.isSet(QStringLiteral("command"))) { 0208 d->core->pushCommand(parser.positionalArguments().at(0)); 0209 } else { 0210 qCDebug(PRINT_THREAD) << "Attempted to inject unknown command: " << parser.positionalArguments(); 0211 } 0212 }