412 lines
9.9 KiB
C++
412 lines
9.9 KiB
C++
#include "scriptengine.h"
|
|
#include <QDir>
|
|
#include <QFileInfo>
|
|
#include <QDebug>
|
|
#include "loadrunable.h"
|
|
#include "rawimage.h"
|
|
#include "loadrunable.h"
|
|
|
|
namespace Script
|
|
{
|
|
|
|
ScriptEngine::ScriptEngine(QObject *parent) : QObject(parent)
|
|
, _jsEngine(new QJSEngine(this))
|
|
, _database(new Database(this))
|
|
, _pool(new QThreadPool(this))
|
|
{
|
|
QJSValue engine = _jsEngine->newQObject(this);
|
|
_jsEngine->globalObject().setProperty("engine", engine);
|
|
_database->init(QLatin1String("scriptengine"));
|
|
_semaphore.release(_pool->maxThreadCount());
|
|
}
|
|
|
|
void ScriptEngine::setParams(const QString &scriptPath, const QList<QPair<QString, QString>> &paths, const QString &outputDir)
|
|
{
|
|
_scriptPath = scriptPath;
|
|
_paths = paths;
|
|
_outputDir = outputDir + "/";
|
|
}
|
|
|
|
void ScriptEngine::reportError(const QString &message)
|
|
{
|
|
_jsEngine->throwError(message);
|
|
}
|
|
|
|
const QString &ScriptEngine::outputDir() const
|
|
{
|
|
return _outputDir;
|
|
}
|
|
|
|
void ScriptEngine::interrupt()
|
|
{
|
|
_jsEngine->setInterrupted(true);
|
|
}
|
|
|
|
void ScriptEngine::logError(const QString &message)
|
|
{
|
|
emit newMessage(message, true);
|
|
}
|
|
|
|
void ScriptEngine::log(const QString &message)
|
|
{
|
|
emit newMessage(message, false);
|
|
}
|
|
|
|
void ScriptEngine::mark(File *file)
|
|
{
|
|
_database->mark(file->absoluteFilePath());
|
|
}
|
|
|
|
void ScriptEngine::unmark(File *file)
|
|
{
|
|
_database->unmark(file->absoluteFilePath());
|
|
}
|
|
|
|
bool ScriptEngine::isMarked(const File *file) const
|
|
{
|
|
return _database->isMarked(file->absoluteFilePath());
|
|
}
|
|
|
|
void ScriptEngine::setMaxThread(int maxthread)
|
|
{
|
|
int newval = std::max(std::min(QThread::idealThreadCount(), maxthread), 1);
|
|
int oldval = _pool->maxThreadCount();
|
|
if(newval > oldval)
|
|
_semaphore.release(newval - oldval);
|
|
else if(newval < oldval)
|
|
_semaphore.acquire(oldval - newval);
|
|
_pool->setMaxThreadCount(newval);
|
|
}
|
|
|
|
void ScriptEngine::sync()
|
|
{
|
|
_pool->waitForDone();
|
|
}
|
|
|
|
bool ScriptEngine::convert(File *file, QString &outpath, const QString &format, const QVariantMap ¶ms, bool async)
|
|
{
|
|
QString path;
|
|
QDir dir(_outputDir);
|
|
QFileInfo info(outpath);
|
|
QString suffix = info.suffix();
|
|
if(info.isAbsolute())
|
|
path = info.absolutePath();
|
|
else
|
|
path = dir.absoluteFilePath(outpath);
|
|
|
|
info.setFile(path);
|
|
outpath = info.absolutePath() + "/" + info.completeBaseName() + "." + format.toLower();
|
|
if(async)
|
|
{
|
|
_semaphore.acquire();
|
|
_pool->start(new ConvertRunable(file->absoluteFilePath(), outpath, format, params, &_semaphore));
|
|
}
|
|
else
|
|
{
|
|
ConvertRunable crun(file->absoluteFilePath(), outpath, format, params, nullptr);
|
|
crun.run();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
QJSValue ScriptEngine::newObject()
|
|
{
|
|
return _jsEngine->newObject();
|
|
}
|
|
|
|
QJSValue ScriptEngine::newArray(uint size)
|
|
{
|
|
return _jsEngine->newArray(size);
|
|
}
|
|
|
|
void ScriptEngine::run()
|
|
{
|
|
QJSValue jsPaths = _jsEngine->newArray(_paths.size());
|
|
for(qsizetype i=0; i<_paths.size(); i++)
|
|
jsPaths.setProperty(i, _jsEngine->newQObject(new File(_paths[i].first, _paths[i].second, this)));
|
|
|
|
_jsEngine->globalObject().setProperty("files", jsPaths);
|
|
|
|
QFile scriptFile(_scriptPath);
|
|
if(!scriptFile.open(QIODevice::ReadOnly))
|
|
{
|
|
emit newMessage("Failed to open " + _scriptPath, true);
|
|
emit finished();
|
|
return;
|
|
}
|
|
|
|
QTextStream stream(&scriptFile);
|
|
QString contents = stream.readAll();
|
|
scriptFile.close();
|
|
QJSValue result = _jsEngine->evaluate(contents, _scriptPath);
|
|
qDebug() << result.isError() << result.toString();
|
|
_pool->waitForDone();
|
|
if(result.isError())
|
|
{
|
|
QString error = result.property("name").toString() + " on line " + result.property("lineNumber").toString() + " : " + result.toString();
|
|
error += "\n" + result.property("stack").toString();
|
|
emit newMessage(error, true);
|
|
}
|
|
|
|
emit finished();
|
|
}
|
|
|
|
void File::loadFitsKeywords()
|
|
{
|
|
if(!_fitsKeywordsLoaded)
|
|
{
|
|
_fitsKeywordsLoaded = true;
|
|
ImageInfoData info;
|
|
if(suffix() == "xisf")
|
|
{
|
|
readXISFHeader(_path, info);
|
|
}
|
|
else if(suffix() == "fits")
|
|
{
|
|
readFITSHeader(_path, info);
|
|
}
|
|
else return;
|
|
|
|
for(auto &record : info.fitsHeader)
|
|
{
|
|
_fitsKeywords.append(record.key);
|
|
_fitsRecords.insert(record.key, record);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool File::mkpath(const QString &path) const
|
|
{
|
|
QFileInfo info(path);
|
|
if(!info.isRelative())
|
|
{
|
|
_engine->logError("Destination path is not relative");
|
|
return false;
|
|
}
|
|
QDir dir(_engine->outputDir());
|
|
if(dir.mkpath(info.path()))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
_engine->logError("Failed to create dir " + info.path());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
File::File(const QString &path, Script::ScriptEngine *engine) : File(path, QString(), engine)
|
|
{
|
|
}
|
|
|
|
File::File(const QString &path, const QString &root, ScriptEngine *engine) :
|
|
_engine(engine),
|
|
_path(path),
|
|
_root(root),
|
|
_info(path)
|
|
{
|
|
}
|
|
|
|
QString File::fileName() const
|
|
{
|
|
return _info.fileName();
|
|
}
|
|
|
|
QString File::absoluteFilePath() const
|
|
{
|
|
return _info.absoluteFilePath();
|
|
}
|
|
|
|
QString File::absolutePath() const
|
|
{
|
|
return _info.absolutePath();
|
|
}
|
|
|
|
QString File::relativeFilePath() const
|
|
{
|
|
QDir dir(_root);
|
|
return dir.relativeFilePath(_info.absoluteFilePath());
|
|
}
|
|
|
|
QString File::relativePath() const
|
|
{
|
|
QDir dir(_root);
|
|
return dir.relativeFilePath(_info.absolutePath());
|
|
}
|
|
|
|
QString File::baseName() const
|
|
{
|
|
return _info.baseName();
|
|
}
|
|
|
|
QString File::completeBaseName() const
|
|
{
|
|
return _info.completeBaseName();
|
|
}
|
|
|
|
QString File::suffix() const
|
|
{
|
|
return _info.suffix();
|
|
}
|
|
|
|
qint64 File::size() const
|
|
{
|
|
return _info.size();
|
|
}
|
|
|
|
QStringList File::fitsKeywords()
|
|
{
|
|
loadFitsKeywords();
|
|
return _fitsKeywords;
|
|
}
|
|
|
|
QString File::fitsValue(const QString &key)
|
|
{
|
|
loadFitsKeywords();
|
|
if(_fitsRecords.contains(key))
|
|
return _fitsRecords[key].value.toString();
|
|
else
|
|
return QString();
|
|
}
|
|
|
|
QJSValue File::fitsValues(const QString &key)
|
|
{
|
|
loadFitsKeywords();
|
|
if(_fitsRecords.contains(key))
|
|
{
|
|
QList<FITSRecord> values = _fitsRecords.values(key);
|
|
QJSValue array = _engine->newArray(values.size());
|
|
for(qsizetype i=0; i<values.size(); i++)
|
|
array.setProperty(i, values[i].value.toString());
|
|
return array;
|
|
}
|
|
else
|
|
return QString();
|
|
}
|
|
|
|
QJSValue File::fitsRecords()
|
|
{
|
|
loadFitsKeywords();
|
|
|
|
QJSValue array = _engine->newArray(_fitsRecords.size());
|
|
uint i = 0;
|
|
for(auto &record : _fitsRecords)
|
|
{
|
|
QJSValue item = _engine->newObject();
|
|
item.setProperty("key", QString::fromUtf8(record.key));
|
|
item.setProperty("value", record.value.toString());
|
|
item.setProperty("comment", QString::fromUtf8(record.comment));
|
|
item.setProperty("xisf", record.xisf);
|
|
array.setProperty(i++, item);
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
bool File::isMarked() const
|
|
{
|
|
return _engine->isMarked(this);
|
|
}
|
|
|
|
File* File::copy(const QString &newpath) const
|
|
{
|
|
if(mkpath(newpath))
|
|
{
|
|
if(QFile::copy(_path, _engine->outputDir() + newpath))
|
|
return new File(_engine->outputDir() + newpath, _engine);
|
|
_engine->logError("Failed copy to " + newpath);
|
|
return nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool File::move(const QString &newpath)
|
|
{
|
|
if(mkpath(newpath))
|
|
{
|
|
if(QFile::rename(_path, _engine->outputDir() + newpath))
|
|
{
|
|
_path = _engine->outputDir() + newpath;
|
|
return true;
|
|
}
|
|
_engine->logError("Failed move to " + newpath);
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
File* File::convert(const QString &outpath, const QString &format, const QVariantMap ¶ms)
|
|
{
|
|
QString path = outpath;
|
|
if(_engine->convert(this, path, format, params, false))
|
|
return new File(path, _engine);
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
File* File::convertAsync(const QString &outpath, const QString &format, const QVariantMap ¶ms)
|
|
{
|
|
QString path = outpath;
|
|
if(_engine->convert(this, path, format, params, true))
|
|
return new File(path, _engine);
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
QJSValue File::stats()
|
|
{
|
|
if(_stats.isUndefined())
|
|
{
|
|
ImageInfoData info;
|
|
std::shared_ptr<RawImage> rawImage;
|
|
loadImage(_path, info, rawImage);
|
|
rawImage->calcStats();
|
|
RawImage::Stats stats = rawImage->imageStats();
|
|
_stats = _engine->newObject();
|
|
_stats.setProperty("mean", stats.m_mean[0]);
|
|
_stats.setProperty("stddev", stats.m_stdDev[0]);
|
|
_stats.setProperty("median", stats.m_median[0]);
|
|
_stats.setProperty("min", stats.m_min[0]);
|
|
_stats.setProperty("max", stats.m_max[0]);
|
|
_stats.setProperty("mad", stats.m_mean[0]);
|
|
}
|
|
return _stats;
|
|
}
|
|
|
|
ScriptEngineThread::ScriptEngineThread(QObject *parent) : QObject(parent)
|
|
{
|
|
_thread = new QThread();
|
|
_thread->setObjectName("ScriptEngine");
|
|
_engine = new ScriptEngine;
|
|
_engine->moveToThread(_thread);
|
|
connect(_engine, &ScriptEngine::finished, _thread, &QThread::quit);
|
|
connect(_engine, &ScriptEngine::newMessage, this, &ScriptEngineThread::newMessage);
|
|
connect(_thread, &QThread::started, _engine, &ScriptEngine::run);
|
|
connect(_thread, &QThread::finished, _engine, &ScriptEngine::deleteLater);
|
|
connect(_engine, &ScriptEngine::destroyed, [this](){ _engine = nullptr; });
|
|
connect(_thread, &QThread::finished, _thread, &QThread::deleteLater);
|
|
connect(_thread, &QThread::finished, this, &ScriptEngineThread::finished);
|
|
}
|
|
|
|
ScriptEngineThread::~ScriptEngineThread()
|
|
{
|
|
if(_engine)_engine->interrupt();
|
|
}
|
|
|
|
void ScriptEngineThread::setParams(const QString &scriptPath, const QList<QPair<QString, QString>> &paths, const QString &outputDir)
|
|
{
|
|
_engine->setParams(scriptPath, paths, outputDir);
|
|
}
|
|
|
|
void ScriptEngineThread::start()
|
|
{
|
|
_thread->start();
|
|
}
|
|
|
|
void ScriptEngineThread::interrupt()
|
|
{
|
|
if(_engine)_engine->interrupt();
|
|
}
|
|
|
|
}
|