819 lines
24 KiB
C++
819 lines
24 KiB
C++
#include "scriptengine.h"
|
|
#include <QDir>
|
|
#include <QFileInfo>
|
|
#include <QDebug>
|
|
#include <QInputDialog>
|
|
#include <QJsonValue>
|
|
#include "loadrunable.h"
|
|
#include "rawimage.h"
|
|
#include "loadrunable.h"
|
|
#include "batchprocessing.h"
|
|
#include <fitsio2.h>
|
|
#include "libXISF/libxisf.h"
|
|
#ifdef PLATESOLVER
|
|
#include "solver.h"
|
|
#endif // PLATESOLVER
|
|
|
|
namespace Script
|
|
{
|
|
|
|
ScriptEngine::ScriptEngine(BatchProcessing *parent)
|
|
: _jsEngine(new QJSEngine(this))
|
|
, _database(new Database(this))
|
|
, _parent(parent)
|
|
, _pool(new QThreadPool(this))
|
|
{
|
|
QJSValue core = _jsEngine->newQObject(this);
|
|
_jsEngine->globalObject().setProperty("core", core);
|
|
QJSValue fitsRecordObject = _jsEngine->newQMetaObject(&FITSRecordModify::staticMetaObject);
|
|
_jsEngine->globalObject().setProperty("FITSRecordModify", fitsRecordObject);
|
|
_database->init(QLatin1String("scriptengine"));
|
|
_semaphore.release(_pool->maxThreadCount());
|
|
_pool->setThreadPriority(QThread::LowPriority);
|
|
|
|
#ifdef PLATESOLVER
|
|
_solver = new Solver(this);
|
|
#endif // PLATESOLVER
|
|
}
|
|
|
|
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()
|
|
{
|
|
#ifdef PLATESOLVER
|
|
if(_solver)_solver->abort();
|
|
#endif
|
|
_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();
|
|
}
|
|
|
|
QJSValue ScriptEngine::getString(const QString &label, const QString &text) const
|
|
{
|
|
QJSValue ret;
|
|
QMetaObject::invokeMethod(_parent, "getString", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QJSValue, ret), Q_ARG(QString, label), Q_ARG(QString, text));
|
|
return ret;
|
|
}
|
|
|
|
QJSValue ScriptEngine::getInt(const QString &label, int value)
|
|
{
|
|
QJSValue ret;
|
|
QMetaObject::invokeMethod(_parent, "getInt", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QJSValue, ret), Q_ARG(QString, label), Q_ARG(int, value));
|
|
return ret;
|
|
}
|
|
|
|
QJSValue ScriptEngine::getFloat(const QString &label, double value, int decimals) const
|
|
{
|
|
QJSValue ret;
|
|
QMetaObject::invokeMethod(_parent, "getFloat", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QJSValue, ret), Q_ARG(QString, label), Q_ARG(double, value), Q_ARG(int, decimals));
|
|
return ret;
|
|
}
|
|
|
|
QJSValue ScriptEngine::getItem(const QStringList &items, const QString &label, int current) const
|
|
{
|
|
QJSValue ret;
|
|
QMetaObject::invokeMethod(_parent, "getItem", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QJSValue, ret), Q_ARG(QStringList, items), Q_ARG(QString, label), Q_ARG(int, current));
|
|
return ret;
|
|
}
|
|
|
|
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);
|
|
|
|
QString f = format.toLower();
|
|
if(f != "xisf" && f != "fits" && f != "png" && f != "bmp" && f != "jpg" && f != "tiff")
|
|
{
|
|
logError("Output format must be one of xisf fits jpg png bmp tiff");
|
|
return false;
|
|
}
|
|
|
|
info.setFile(path);
|
|
outpath = info.absolutePath() + "/" + info.completeBaseName() + "." + f;
|
|
if(async)
|
|
{
|
|
_semaphore.acquire();
|
|
_pool->start(new ConvertRunable(file->absoluteFilePath(), outpath, f, params, &_semaphore));
|
|
}
|
|
else
|
|
{
|
|
ConvertRunable crun(file->absoluteFilePath(), outpath, f, params, nullptr);
|
|
crun.run();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#ifdef PLATESOLVER
|
|
void ScriptEngine::setSolverProfile(int index)
|
|
{
|
|
if(_solver && index >= SSolver::Parameters::DEFAULT && index < SSolver::Parameters::BIG_STARS)
|
|
{
|
|
_solver->setParameters((SSolver::Parameters::ParametersProfile)index);
|
|
}
|
|
}
|
|
|
|
void ScriptEngine::setSolverProfile(const QVariantMap &profile)
|
|
{
|
|
if(_solver)
|
|
{
|
|
SSolver::Parameters params = SSolver::Parameters::convertFromMap(profile);
|
|
_solver->setParameters(params);
|
|
}
|
|
}
|
|
|
|
QJSValue ScriptEngine::getSolverProfile() const
|
|
{
|
|
if(_solver)
|
|
{
|
|
QMap<QString, QVariant> params = SSolver::Parameters::convertToMap(_solver->getProfile());
|
|
QJSValue ret = _jsEngine->newObject();
|
|
for(auto i = params.begin(); i != params.end(); i++)
|
|
{
|
|
switch(i.value().metaType().id())
|
|
{
|
|
case QMetaType::Int:
|
|
ret.setProperty(i.key(), i.value().toInt());
|
|
break;
|
|
case QMetaType::Double:
|
|
ret.setProperty(i.key(), i.value().toDouble());
|
|
break;
|
|
case QMetaType::Bool:
|
|
ret.setProperty(i.key(), i.value().toBool());
|
|
break;
|
|
case QMetaType::QString:
|
|
ret.setProperty(i.key(), i.value().toString());
|
|
break;
|
|
default:
|
|
qWarning() << "unhandled metatype" << i.key() << i.value();
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
else
|
|
{
|
|
return QJSValue();
|
|
}
|
|
}
|
|
|
|
void ScriptEngine::setStartingSolution(const QJSValue &solution)
|
|
{
|
|
if(solution.isObject())
|
|
{
|
|
if(solution.hasProperty("ra") && solution.hasProperty("dec") && solution.property("ra").isNumber() && solution.property("dec").isNumber())
|
|
_solver->setSearchPosition(solution.property("ra").toNumber(), solution.property("dec").toNumber());
|
|
|
|
if(solution.hasProperty("pixscale") && solution.property("pixscale").isNumber())
|
|
{
|
|
double scale = solution.property("pixscale").toNumber();
|
|
_solver->setSearchScale(scale * 0.8, scale * 1.2, SSolver::ScaleUnits::ARCSEC_PER_PIX);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_solver->clearStartingPositionAndScale();
|
|
}
|
|
}
|
|
|
|
QJSValue ScriptEngine::solveImage(File *file, bool updateHeader)
|
|
{
|
|
QString path = file->absoluteFilePath();
|
|
QJSValue ret = newObject();
|
|
|
|
if(_solver->loadImage(path))
|
|
{
|
|
if(_solver->solveImage(true))
|
|
{
|
|
auto solution = _solver->getSolution();
|
|
ret.setProperty("fieldWidth", solution.fieldWidth);
|
|
ret.setProperty("fieldHeight", solution.fieldHeight);
|
|
ret.setProperty("ra", solution.ra);
|
|
ret.setProperty("dec", solution.dec);
|
|
ret.setProperty("orientation", solution.orientation);
|
|
ret.setProperty("pixscale", solution.pixscale);
|
|
ret.setProperty("parity", solution.parity == FITSImage::Parity::POSITIVE);
|
|
ret.setProperty("raError", solution.raError);
|
|
ret.setProperty("decError", solution.decError);
|
|
if(updateHeader)
|
|
{
|
|
QString error;
|
|
if(!_solver->updateHeader(error))
|
|
logError(error);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logError("Failed to plate solve image " + path);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logError("Failed to load image " + path);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QJSValue ScriptEngine::extractStars(File *file, bool hfr)
|
|
{
|
|
QJSValue ret;
|
|
QString path = file->absoluteFilePath();
|
|
if(_solver->loadImage(path))
|
|
{
|
|
if(_solver->extractSources(hfr, true))
|
|
{
|
|
auto stars = _solver->getStars();
|
|
ret = newArray(stars.size());
|
|
int i = 0;
|
|
for(auto &star : stars)
|
|
{
|
|
QJSValue starj = newObject();
|
|
starj.setProperty("x", star.x);
|
|
starj.setProperty("y", star.y);
|
|
starj.setProperty("mag", star.mag);
|
|
starj.setProperty("flux", star.flux);
|
|
starj.setProperty("peak", star.peak);
|
|
starj.setProperty("HFR", star.HFR);
|
|
starj.setProperty("a", star.a);
|
|
starj.setProperty("b", star.b);
|
|
starj.setProperty("theta", star.theta);
|
|
starj.setProperty("ra", star.ra);
|
|
starj.setProperty("dec", star.dec);
|
|
starj.setProperty("numPixels", star.numPixels);
|
|
ret.setProperty(i++, starj);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logError("Failed to extract sources from " + path);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logError("Failed to load image " + path);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif // PLATESOLVER
|
|
|
|
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().toLower() == "xisf")
|
|
{
|
|
readXISFHeader(_path, info);
|
|
}
|
|
else if(suffix().toLower() == "fits" || suffix().toLower() == "fit")
|
|
{
|
|
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::modifyFITSRecords(const FITSRecordModify *modify)
|
|
{
|
|
_fitsKeywordsLoaded = false;
|
|
_fitsKeywords.clear();
|
|
|
|
if(QRegularExpression("(fits?|fz|fts)", QRegularExpression::CaseInsensitiveOption).match(suffix()).hasMatch())
|
|
{
|
|
fitsfile *file;
|
|
int status = 0;
|
|
QString path = makeMaxPath(_path);
|
|
fits_open_diskfile(&file, path.toLocal8Bit().data(), READWRITE, &status);
|
|
int num = 0;
|
|
fits_get_num_hdus(file, &num, &status);
|
|
if(status)
|
|
{
|
|
if(_engine)_engine->newMessage("Failed to open FITS file", true);
|
|
return false;
|
|
}
|
|
int imgtype;
|
|
int naxis;
|
|
long naxes[3] = {0};
|
|
int type = -1;
|
|
for(int i=1; i <= num; i++)
|
|
{
|
|
fits_movabs_hdu(file, i, IMAGE_HDU, &status);
|
|
fits_get_hdu_type(file, &type, &status);
|
|
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);
|
|
if(type == IMAGE_HDU && naxis >= 2 && naxis <= 3 && status == 0)
|
|
break;
|
|
if(i == num)return false;
|
|
}
|
|
|
|
for(auto &remove : modify->_remove)
|
|
{
|
|
int status = 0;//we ignore errors from here
|
|
fits_delete_key(file, remove.toLatin1().data(), &status);
|
|
}
|
|
for(auto &record : modify->_update)
|
|
{
|
|
switch(record.value.typeId())
|
|
{
|
|
case QMetaType::Bool:
|
|
{
|
|
int val = record.value.toBool();
|
|
fits_update_key(file, TLOGICAL, record.key.data(), &val, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
|
break;
|
|
}
|
|
case QMetaType::Int:
|
|
case QMetaType::UInt:
|
|
{
|
|
long long val = record.value.toLongLong();
|
|
fits_update_key(file, TLONGLONG, record.key.data(), &val, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
|
break;
|
|
}
|
|
case QMetaType::QString:
|
|
{
|
|
QByteArray val = record.value.toString().toLatin1();
|
|
fits_update_key(file, TSTRING, record.key.data(), val.isEmpty() ? nullptr : val.data(), record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
|
break;
|
|
}
|
|
case QMetaType::Float:
|
|
case QMetaType::Double:
|
|
{
|
|
double val = record.value.toDouble();
|
|
fits_update_key(file, TDOUBLE, record.key.data(), &val, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
|
break;
|
|
}
|
|
default:
|
|
if(_engine)_engine->newMessage("Unknown type for KEY " + record.key, true);
|
|
return false;
|
|
break;
|
|
}
|
|
if(status)
|
|
{
|
|
char error[100];
|
|
fits_get_errstatus(status, error);
|
|
if(_engine)_engine->newMessage(QString("Error when updating KEY %1 %2").arg(record.key).arg(error), true);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for(auto &record : modify->_add)
|
|
{
|
|
switch(record.value.typeId())
|
|
{
|
|
case QMetaType::Bool:
|
|
{
|
|
int val = record.value.toBool();
|
|
fits_write_key(file, TLOGICAL, record.key.data(), &val, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
|
break;
|
|
}
|
|
case QMetaType::Int:
|
|
case QMetaType::UInt:
|
|
{
|
|
long long val = record.value.toLongLong();
|
|
fits_write_key(file, TLONGLONG, record.key.data(), &val, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
|
break;
|
|
}
|
|
case QMetaType::QString:
|
|
{
|
|
QByteArray val = record.value.toString().toLatin1();
|
|
fits_write_key(file, TSTRING, record.key.data(), val.isEmpty() ? nullptr : val.data(), record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
|
break;
|
|
}
|
|
case QMetaType::Float:
|
|
case QMetaType::Double:
|
|
{
|
|
double val = record.value.toDouble();
|
|
fits_write_key(file, TDOUBLE, record.key.data(), &val, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
|
break;
|
|
}
|
|
default:
|
|
if(_engine)_engine->newMessage("Unknown type for KEY " + record.key, true);
|
|
return false;
|
|
break;
|
|
}
|
|
if(status)
|
|
{
|
|
char error[100];
|
|
fits_get_errstatus(status, error);
|
|
if(_engine)_engine->newMessage(QString("Error when adding KEY {} {}").arg(record.key).arg(error), true);
|
|
return false;
|
|
}
|
|
}
|
|
fits_close_file(file, &status);
|
|
|
|
return status == 0;
|
|
}
|
|
else if(suffix().toLower() == "xisf")
|
|
{
|
|
try
|
|
{
|
|
LibXISF::XISFModify modifyXISF;
|
|
QString in = makeMaxPath(absoluteFilePath());
|
|
QString out = in + "~";
|
|
modifyXISF.open(in.toLocal8Bit().data());
|
|
qDebug() << "modify" << in << out;
|
|
|
|
for(auto &remove : modify->_remove)
|
|
modifyXISF.removeFITSKeyword(0, remove.toStdString());
|
|
|
|
for(auto &record : modify->_update)
|
|
modifyXISF.updateFITSKeyword(0, {record.key.toStdString(), record.value.toString().toStdString(), record.comment.toStdString()}, true);
|
|
|
|
for(auto &record : modify->_add)
|
|
modifyXISF.addFITSKeyword(0, {record.key.toStdString(), record.value.toString().toStdString(), record.comment.toStdString()});
|
|
|
|
modifyXISF.save(out.toLocal8Bit().toStdString());
|
|
modifyXISF.close();
|
|
std::filesystem::rename(out.toLocal8Bit().toStdString(), in.toLocal8Bit().toStdString());
|
|
return true;
|
|
}
|
|
catch(std::filesystem::filesystem_error &err)
|
|
{
|
|
return false;
|
|
}
|
|
catch(LibXISF::Error &err)
|
|
{
|
|
if(_engine)_engine->newMessage("Failed to modify file " + _path + " " + err.what(), true);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
#ifdef PLATESOLVER
|
|
QJSValue File::solve(bool updateHeader)
|
|
{
|
|
if(_solution.isUndefined() || updateHeader)
|
|
_solution = _engine->solveImage(this, updateHeader);
|
|
|
|
return _solution;
|
|
}
|
|
|
|
QJSValue File::extractStars(bool hfr)
|
|
{
|
|
if(_stars.isUndefined())
|
|
_stars = _engine->extractStars(this, hfr);
|
|
|
|
return _stars;
|
|
}
|
|
#endif // PLATESOLVER
|
|
|
|
ScriptEngineThread::ScriptEngineThread(BatchProcessing *parent) : QObject(parent)
|
|
{
|
|
_thread = new QThread();
|
|
_thread->setObjectName("ScriptEngine");
|
|
_engine = new ScriptEngine(parent);
|
|
_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();
|
|
}
|
|
|
|
void FITSRecordModify::removeKeyword(const QString &key)
|
|
{
|
|
if(!_remove.contains(key))
|
|
_remove.append(key);
|
|
}
|
|
|
|
void FITSRecordModify::updateKeyword(const QString &key, const QVariant &value, const QString &comment)
|
|
{
|
|
_update.append({key.toLatin1(), value, comment.toLatin1()});
|
|
}
|
|
|
|
void FITSRecordModify::addKeyword(const QString &key, const QVariant &value, const QString &comment)
|
|
{
|
|
_update.append({key.toLatin1(), value, comment.toLatin1()});
|
|
}
|
|
|
|
}
|