File indexing completed on 2024-04-28 11:20:43

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-or-later
0003     SPDX-FileCopyrightText: 2011 Filipe Saraiva <filipe@kde.org>
0004 */
0005 
0006 #include "scilabsession.h"
0007 #include "scilabhighlighter.h"
0008 #include "scilabcompletionobject.h"
0009 
0010 #include <defaultvariablemodel.h>
0011 
0012 #include <QProcess>
0013 #include <KDirWatch>
0014 
0015 #include <QDebug>
0016 #include <QDir>
0017 #include <QFile>
0018 #include <QTextEdit>
0019 #include <QListIterator>
0020 #include <QIODevice>
0021 #include <QByteArray>
0022 
0023 #include <settings.h>
0024 
0025 #ifndef Q_OS_WIN
0026 #include <signal.h>
0027 #endif
0028 
0029 ScilabSession::ScilabSession( Cantor::Backend* backend) : Session(backend),
0030 m_process(nullptr),
0031 m_watch(nullptr),
0032 m_variableModel(new Cantor::DefaultVariableModel(this))
0033 {
0034 }
0035 
0036 ScilabSession::~ScilabSession()
0037 {
0038     if (m_process)
0039     {
0040         m_process->kill();
0041         m_process->deleteLater();
0042         m_process = nullptr;
0043     }
0044 }
0045 
0046 void ScilabSession::login()
0047 {
0048     qDebug()<<"login";
0049     if (m_process)
0050         return;
0051 
0052     emit loginStarted();
0053 
0054     QStringList args;
0055 
0056     args << QLatin1String("-nb");
0057 
0058     m_process = new QProcess(this);
0059     m_process->setArguments(args);
0060     m_process->setProgram(ScilabSettings::self()->path().toLocalFile());
0061 
0062     qDebug() << m_process->program();
0063 
0064     m_process->setProcessChannelMode(QProcess::SeparateChannels);
0065     m_process->start();
0066     m_process->waitForStarted();
0067 
0068     if(ScilabSettings::integratePlots()){
0069 
0070         qDebug() << "integratePlots";
0071 
0072         QString tempPath = QDir::tempPath();
0073 
0074         QString pathScilabOperations = tempPath;
0075         pathScilabOperations.prepend(QLatin1String("chdir('"));
0076         pathScilabOperations.append(QLatin1String("');\n"));
0077 
0078         qDebug() << "Processing command to change chdir in Scilab. Command " << pathScilabOperations.toLocal8Bit();
0079 
0080         m_process->write(pathScilabOperations.toLocal8Bit());
0081 
0082         m_watch = new KDirWatch(this);
0083         m_watch->setObjectName(QLatin1String("ScilabDirWatch"));
0084 
0085         m_watch->addDir(tempPath, KDirWatch::WatchFiles);
0086 
0087         qDebug() << "addDir " <<  tempPath << "? " << m_watch->contains(QLatin1String(tempPath.toLocal8Bit()));
0088 
0089         QObject::connect(m_watch, &KDirWatch::created, this, &ScilabSession::plotFileChanged);
0090     }
0091 
0092     if(!ScilabSettings::self()->autorunScripts().isEmpty()){
0093         QString autorunScripts = ScilabSettings::self()->autorunScripts().join(QLatin1String("\n"));
0094         m_process->write(autorunScripts.toLocal8Bit());
0095     }
0096 
0097     QObject::connect(m_process, &QProcess::readyReadStandardOutput, this, &ScilabSession::readOutput);
0098     QObject::connect(m_process, &QProcess::readyReadStandardError, this, &ScilabSession::readError);
0099 
0100     m_process->readAllStandardOutput();
0101     m_process->readAllStandardError();
0102 
0103     changeStatus(Cantor::Session::Done);
0104 
0105     emit loginDone();
0106 }
0107 
0108 void ScilabSession::logout()
0109 {
0110     qDebug()<<"logout";
0111 
0112     if(!m_process)
0113         return;
0114 
0115     disconnect(m_process, nullptr, this, nullptr);
0116 
0117     if(status() == Cantor::Session::Running)
0118         interrupt();
0119 
0120     m_process->write("exit\n");
0121 
0122     if(!m_process->waitForFinished(1000))
0123         m_process->kill();
0124     m_process->deleteLater();
0125     m_process = nullptr;
0126 
0127     QDir removePlotFigures;
0128     QListIterator<QString> i(m_listPlotName);
0129 
0130     while(i.hasNext()){
0131         removePlotFigures.remove(QLatin1String(i.next().toLocal8Bit().constData()));
0132     }
0133 
0134     Session::logout();
0135 }
0136 
0137 void ScilabSession::interrupt()
0138 {
0139     if(!expressionQueue().isEmpty())
0140     {
0141         qDebug()<<"interrupting " << expressionQueue().first()->command();
0142         if(m_process && m_process->state() != QProcess::NotRunning)
0143         {
0144 #ifndef Q_OS_WIN
0145             const int pid = m_process->processId();
0146             kill(pid, SIGINT);
0147 #else
0148             ; //TODO: interrupt the process on windows
0149 #endif
0150         }
0151         foreach (Cantor::Expression* expression, expressionQueue())
0152             expression->setStatus(Cantor::Expression::Interrupted);
0153         expressionQueue().clear();
0154 
0155         // Cleanup inner state and call octave prompt printing
0156         // If we move this code for interruption to Session, we need add function for
0157         // cleaning before setting Done status
0158         m_output.clear();
0159         m_process->write("\n");
0160 
0161         qDebug()<<"done interrupting";
0162     }
0163 
0164     changeStatus(Cantor::Session::Done);
0165 }
0166 
0167 Cantor::Expression* ScilabSession::evaluateExpression(const QString& cmd, Cantor::Expression::FinishingBehavior behave, bool internal)
0168 {
0169     qDebug() << "evaluating: " << cmd;
0170     ScilabExpression* expr = new ScilabExpression(this, internal);
0171 
0172     expr->setFinishingBehavior(behave);
0173     expr->setCommand(cmd);
0174     expr->evaluate();
0175 
0176     return expr;
0177 }
0178 
0179 void ScilabSession::runFirstExpression()
0180 {
0181     qDebug() <<"call runFirstExpression";
0182     qDebug() << "m_process: " << m_process;
0183     qDebug() << "status: " << (status() == Cantor::Session::Running ? "Running" : "Done");
0184 
0185     if (!m_process)
0186         return;
0187 
0188     qDebug()<<"running next expression";
0189 
0190     if(!expressionQueue().isEmpty())
0191     {
0192         auto* expr = expressionQueue().first();
0193 
0194         QString command;
0195         command.prepend(QLatin1String("\nprintf('begin-cantor-scilab-command-processing')\n"));
0196         command += expr->command();
0197         command += QLatin1String("\nprintf('terminated-cantor-scilab-command-processing')\n");
0198 
0199         connect(expr, &Cantor::Expression::statusChanged, this, &ScilabSession::currentExpressionStatusChanged);
0200         expr->setStatus(Cantor::Expression::Computing);
0201 
0202         qDebug() << "Writing command to process" << command;
0203 
0204         m_process->write(command.toLocal8Bit());
0205     }
0206 }
0207 
0208 void ScilabSession::readError()
0209 {
0210     qDebug() << "readError";
0211 
0212     QString error = QLatin1String(m_process->readAllStandardError());
0213 
0214     qDebug() << "error: " << error;
0215     if (!expressionQueue().isEmpty())
0216         expressionQueue().first()->parseError(error);
0217 }
0218 
0219 void ScilabSession::readOutput()
0220 {
0221     qDebug() << "readOutput";
0222 
0223     while(m_process->bytesAvailable() > 0)
0224         m_output.append(QString::fromLocal8Bit(m_process->readLine()));
0225 
0226     qDebug() << "output.isNull? " << m_output.isNull();
0227     qDebug() << "output: " << m_output;
0228 
0229     if(status() != Running || m_output.isNull())
0230         return;
0231 
0232     if(m_output.contains(QLatin1String("begin-cantor-scilab-command-processing")) &&
0233         m_output.contains(QLatin1String("terminated-cantor-scilab-command-processing"))){
0234 
0235         m_output.remove(QLatin1String("begin-cantor-scilab-command-processing"));
0236         m_output.remove(QLatin1String("terminated-cantor-scilab-command-processing"));
0237 
0238         expressionQueue().first()->parseOutput(m_output);
0239 
0240         m_output.clear();
0241     }
0242 }
0243 
0244 void ScilabSession::plotFileChanged(const QString& filename)
0245 {
0246     qDebug() << "plotFileChanged filename:" << filename;
0247 
0248     if (!expressionQueue().isEmpty() && (filename.contains(QLatin1String("cantor-export-scilab-figure")))){
0249          qDebug() << "Calling parsePlotFile";
0250          static_cast<ScilabExpression*>(expressionQueue().first())->parsePlotFile(filename);
0251 
0252          m_listPlotName.append(filename);
0253     }
0254 }
0255 
0256 //TODO: unify with the funcion in the base class
0257 void ScilabSession::currentExpressionStatusChanged(Cantor::Expression::Status status)
0258 {
0259     qDebug() << "currentExpressionStatusChanged: " << status;
0260 
0261     switch (status){
0262         case Cantor::Expression::Computing:
0263         case Cantor::Expression::Interrupted:
0264         case Cantor::Expression::Queued:
0265             break;
0266 
0267         case Cantor::Expression::Done:
0268         case Cantor::Expression::Error:
0269             expressionQueue().removeFirst();
0270 
0271             if (expressionQueue().isEmpty())
0272                 changeStatus(Done);
0273             else
0274                 runFirstExpression();
0275 
0276             break;
0277     }
0278 }
0279 
0280 QSyntaxHighlighter* ScilabSession::syntaxHighlighter(QObject* parent)
0281 {
0282 
0283     ScilabHighlighter *highlighter = new ScilabHighlighter(parent, this);
0284 
0285     return highlighter;
0286 }
0287 
0288 Cantor::CompletionObject* ScilabSession::completionFor(const QString& command, int index)
0289 {
0290     return new ScilabCompletionObject(command, index, this);
0291 }
0292 
0293 Cantor::DefaultVariableModel* ScilabSession::variableModel() const
0294 {
0295     return m_variableModel;
0296 }
0297