7#include "placeholderpath.h"
9#include "sequencejob.h"
17#include <ekos_capture_debug.h>
24 {FRAME_LIGHT,
"Light"},
31PlaceholderPath::PlaceholderPath(
const QString &seqFilename)
32 : m_seqFilename(seqFilename)
36PlaceholderPath::PlaceholderPath():
41PlaceholderPath::~PlaceholderPath()
45QString PlaceholderPath::defaultFormat(
bool useFilter,
bool useExposure,
bool useTimestamp)
50 tempFormat.
append(
"%t_%T_");
60void PlaceholderPath::processJobInfo(
SequenceJob *job)
62 QString jobTargetName = job->getCoreProperty(SequenceJob::SJ_TargetName).toString();
63 auto frameType = getFrameType(job->getFrameType());
64 auto filterType = job->getCoreProperty(SequenceJob::SJ_Filter).toString();
65 auto exposure = job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
66 const auto isDarkFlat = job->jobType() == SequenceJob::JOBTYPE_DARKFLAT;
69 frameType =
"DarkFlat";
72 QString tempTargetName = KSUtils::sanitize(jobTargetName);
77 jobTargetName = tempTargetName;
80 QString imagePrefix = jobTargetName;
82 if (imagePrefix.
isEmpty() ==
false)
85 imagePrefix += frameType;
87 if (isFilterEnabled(job->getCoreProperty(SequenceJob::SJ_PlaceholderFormat).toString()) && filterType.isEmpty() ==
false &&
88 (job->getFrameType() == FRAME_LIGHT || job->getFrameType() == FRAME_FLAT || job->getFrameType() == FRAME_NONE
93 imagePrefix += filterType;
100 if (isExpEnabled(job->getCoreProperty(SequenceJob::SJ_PlaceholderFormat).toString()))
104 double fractpart, intpart;
105 fractpart = std::modf(exposure, &intpart);
110 else if (exposure >= 1e-3)
120 job->setCoreProperty(SequenceJob::SJ_FullPrefix, imagePrefix);
122 QString signature = generateSequenceFilename(*job,
true,
true, 1,
".fits",
"",
false,
true);
123 job->setCoreProperty(SequenceJob::SJ_Signature, signature);
128 QString imagePrefix = KSUtils::sanitize(targetName);
129 QString fullPrefix = constructPrefix(job, imagePrefix);
131 job->setCoreProperty(SequenceJob::SJ_FullPrefix, fullPrefix);
136 CCDFrameType frameType = job->getFrameType();
137 auto placeholderFormat = job->getCoreProperty(SequenceJob::SJ_PlaceholderFormat).toString();
138 auto filter = job->getCoreProperty(SequenceJob::SJ_Filter).toString();
140 double exposure = job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
142 QString tempImagePrefix = imagePrefix;
143 if (tempImagePrefix.
isEmpty() ==
false)
144 tempImagePrefix +=
'_';
146 const auto isDarkFlat = job->jobType() == SequenceJob::JOBTYPE_DARKFLAT;
148 tempImagePrefix += isDarkFlat ?
"DarkFlat" : CCDFrameTypeNames[frameType];
150 if (isFilterEnabled(placeholderFormat) &&
filter.isEmpty() ==
false &&
151 (frameType == FRAME_LIGHT ||
152 frameType == FRAME_FLAT ||
153 frameType == FRAME_NONE ||
156 tempImagePrefix +=
'_';
157 tempImagePrefix +=
filter;
159 if (isExpEnabled(placeholderFormat))
161 tempImagePrefix +=
'_';
163 double exposureValue = job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
166 if (exposureValue ==
static_cast<int>(exposureValue))
172 if (exposure >= 0.001)
178 if (isTsEnabled(placeholderFormat))
180 tempImagePrefix += SequenceJob::ISOMarker;
183 return tempImagePrefix;
188 const bool batch_mode,
189 const int nextSequenceID,
193 const bool gettingSignature)
196 setGenerateFilenameSettings(job, pathPropertyMap, local, gettingSignature);
198 return generateFilenameInternal(pathPropertyMap, local, batch_mode, nextSequenceID, extension, filename, glob,
202QString PlaceholderPath::generateOutputFilename(
const bool local,
const bool batch_mode,
const int nextSequenceID,
204 const QString &filename,
const bool glob,
const bool gettingSignature)
const
206 return generateFilenameInternal(m_PathPropertyMap, local, batch_mode, nextSequenceID, extension, filename, glob,
211 bool usePattern)
const
215 switch (propertyType(property))
221 return "(true|false)";
225 if (property == PP_PIERSIDE)
226 return "(East|West|Unknown)";
231 else if (pathPropertyMap[property].
isValid())
233 switch (propertyType(property))
240 return QString(
"%1x%2").
arg(pathPropertyMap[PP_BIN].toPoint().x()).
arg(pathPropertyMap[PP_BIN].toPoint().y());
242 if (property == PP_PIERSIDE)
244 switch (
static_cast<ISD::Mount::PierSide
>(pathPropertyMap[property].toInt()))
246 case ISD::Mount::PIER_EAST:
248 case ISD::Mount::PIER_WEST:
255 return pathPropertyMap[property].toString();
257 return pathPropertyMap[property].toString();
262 switch (propertyType(property))
279 const bool batch_mode,
280 const int nextSequenceID,
284 const bool gettingSignature)
const
286 QString targetNameSanitized = KSUtils::sanitize(pathPropertyMap[PP_TARGETNAME].
toString());
289 const QString format = pathPropertyMap[PP_FORMAT].toString();
290 const bool isDarkFlat = pathPropertyMap[PP_DARKFLAT].isValid() && pathPropertyMap[PP_DARKFLAT].toBool();
291 const CCDFrameType frameType =
static_cast<CCDFrameType
>(pathPropertyMap[PP_FRAMETYPE].toUInt());
292 QString tempFilename = filename;
295 currentDir = pathPropertyMap[PP_DIRECTORY].toString();
305 currentDir = currentDir.
left(currentDir.
length() - 1);
316 re(
"(?<replace>\\%(?<name>(filename|f|Datetime|D|Type|T|exposure|e|exp|E|Filter|F|target|t|temperature|C|bin|B|gain|G|offset|O|iso|I|pierside|P|sequence|s))(?<level>\\d+)?)(?<sep>[_\\\\])?");
318 re(
"(?<replace>\\%(?<name>(filename|f|Datetime|D|Type|T|exposure|e|exp|E|Filter|F|target|t|temperature|C|bin|B|gain|G|offset|O|iso|I|pierside|P|sequence|s))(?<level>\\d+)?)(?<sep>[_/])?");
321 while ((i = tempFormat.
indexOf(re, i, &match)) != -1)
324 if ((
match.captured(
"name") ==
"filename") || (
match.captured(
"name") ==
"f"))
325 replacement = m_seqFilename.baseName();
326 else if ((
match.captured(
"name") ==
"Datetime") || (
match.captured(
"name") ==
"D"))
328 if (glob || gettingSignature)
331 replacement =
"\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d-\\d\\d-\\d\\d";
333 replacement =
"ISO8601";
339 else if ((
match.captured(
"name") ==
"Type") || (
match.captured(
"name") ==
"T"))
342 replacement =
"DarkFlat";
344 replacement = getFrameType(frameType);
346 else if ((
match.captured(
"name") ==
"exposure") || (
match.captured(
"name") ==
"e") ||
347 (
match.captured(
"name") ==
"exp") || (
match.captured(
"name") ==
"E"))
349 double fractpart, intpart;
350 double exposure = pathPropertyMap[PP_EXPOSURE].toDouble();
351 fractpart = std::modf(exposure, &intpart);
354 else if (exposure >= 1e-3)
359 if ((
match.captured(
"name") ==
"exposure") || (
match.captured(
"name") ==
"e"))
360 replacement +=
QString(
"_secs");
362 else if ((
match.captured(
"name") ==
"Filter") || (
match.captured(
"name") ==
"F"))
365 if (
filter.isEmpty() ==
false
366 && (frameType == FRAME_LIGHT
367 || frameType == FRAME_FLAT
368 || frameType == FRAME_NONE
374 else if ((
match.captured(
"name") ==
"target") || (
match.captured(
"name") ==
"t"))
376 replacement = targetNameSanitized;
378 else if (((
match.captured(
"name") ==
"temperature") || (
match.captured(
"name") ==
"C")))
380 replacement = generateReplacement(pathPropertyMap, PP_TEMPERATURE,
381 (glob || gettingSignature) && pathPropertyMap[PP_TEMPERATURE].
isValid() ==
false);
383 else if (((
match.captured(
"name") ==
"bin") || (
match.captured(
"name") ==
"B")))
385 replacement = generateReplacement(pathPropertyMap, PP_BIN,
386 (glob || gettingSignature) && pathPropertyMap[PP_BIN].
isValid() ==
false);
388 else if (((
match.captured(
"name") ==
"gain") || (
match.captured(
"name") ==
"G")))
390 replacement = generateReplacement(pathPropertyMap, PP_GAIN,
391 (glob || gettingSignature) && pathPropertyMap[PP_GAIN].
isValid() ==
false);
393 else if (((
match.captured(
"name") ==
"offset") || (
match.captured(
"name") ==
"O")))
395 replacement = generateReplacement(pathPropertyMap, PP_OFFSET,
396 (glob || gettingSignature) && pathPropertyMap[PP_OFFSET].
isValid() ==
false);
398 else if (((
match.captured(
"name") ==
"iso") || (
match.captured(
"name") ==
"I"))
399 && pathPropertyMap[PP_ISO].isValid())
401 replacement = generateReplacement(pathPropertyMap, PP_ISO,
402 (glob || gettingSignature) && pathPropertyMap[PP_ISO].
isValid() ==
false);
404 else if (((
match.captured(
"name") ==
"pierside") || (
match.captured(
"name") ==
"P")))
406 replacement = generateReplacement(pathPropertyMap, PP_PIERSIDE, glob || gettingSignature);
423 else if ((
match.captured(
"name") ==
"sequence") || (
match.captured(
"name") ==
"s"))
426 replacement =
"(?<id>\\d+)";
430 if (!
match.captured(
"level").isEmpty())
432 replacement =
QString(
"%1").
arg(nextSequenceID, level, 10,
QChar(
'0'));
441 qWarning() <<
"Unknown replacement string: " <<
match.captured(
"replace");
444 tempFormat = tempFormat.
replace(
match.capturedStart(),
match.capturedLength(), replacement);
446 tempFormat = tempFormat.
replace(
match.capturedStart(
"replace"),
match.capturedLength(
"replace"), replacement);
447 i += replacement.
length();
450 if (!gettingSignature)
451 tempFilename = tempFormat + extension;
459 const bool local,
const bool gettingSignature)
461 setPathProperty(pathPropertyMap, PP_TARGETNAME, job.getCoreProperty(SequenceJob::SJ_TargetName));
462 setPathProperty(pathPropertyMap, PP_FRAMETYPE,
QVariant(job.getFrameType()));
463 setPathProperty(pathPropertyMap, PP_FILTER, job.getCoreProperty(SequenceJob::SJ_Filter));
464 setPathProperty(pathPropertyMap, PP_EXPOSURE, job.getCoreProperty(SequenceJob::SJ_Exposure));
465 setPathProperty(pathPropertyMap, PP_DIRECTORY,
466 job.getCoreProperty(local ? SequenceJob::SJ_LocalDirectory : SequenceJob::SJ_RemoteDirectory));
467 setPathProperty(pathPropertyMap, PP_FORMAT, job.getCoreProperty(SequenceJob::SJ_PlaceholderFormat));
468 setPathProperty(pathPropertyMap, PP_SUFFIX, job.getCoreProperty(SequenceJob::SJ_PlaceholderSuffix));
469 setPathProperty(pathPropertyMap, PP_DARKFLAT, job.jobType() == SequenceJob::JOBTYPE_DARKFLAT);
470 setPathProperty(pathPropertyMap, PP_BIN, job.getCoreProperty(SequenceJob::SJ_Binning));
471 setPathProperty(pathPropertyMap, PP_PIERSIDE,
QVariant(job.getPierSide()));
472 setPathProperty(pathPropertyMap, PP_ISO, job.getCoreProperty(SequenceJob::SJ_ISO));
475 if (job.getCoreProperty(SequenceJob::SJ_EnforceTemperature).toBool())
476 setPathProperty(pathPropertyMap, PP_TEMPERATURE,
QVariant(job.getTargetTemperature()));
477 else if (job.currentTemperature() != Ekos::INVALID_VALUE && !gettingSignature)
478 setPathProperty(pathPropertyMap, PP_TEMPERATURE,
QVariant(job.currentTemperature()));
480 pathPropertyMap.
remove(PP_TEMPERATURE);
482 if (job.getCoreProperty(SequenceJob::SequenceJob::SJ_Gain).toInt() >= 0)
483 setPathProperty(pathPropertyMap, PP_GAIN, job.getCoreProperty(SequenceJob::SJ_Gain));
484 else if (job.currentGain() >= 0 && !gettingSignature)
485 setPathProperty(pathPropertyMap, PP_GAIN, job.currentGain());
487 pathPropertyMap.
remove(PP_GAIN);
489 if (job.getCoreProperty(SequenceJob::SequenceJob::SJ_Offset).toInt() >= 0)
490 setPathProperty(pathPropertyMap, PP_OFFSET, job.getCoreProperty(SequenceJob::SJ_Offset));
491 else if (job.currentOffset() >= 0 && !gettingSignature)
492 setPathProperty(pathPropertyMap, PP_OFFSET, job.currentOffset());
494 pathPropertyMap.
remove(PP_OFFSET);
502 QRegularExpression re(
"(?<replace>\\%(?<name>[a-zA-Z])(?<level>\\d+)?)(?<sep>[_\\\\])+");
507 while ((i = filename.
indexOf(re, i, &match)) != -1)
509 if (
match.hasMatch())
511 i +=
match.capturedLength(
"replace");
518 QString path = generateSequenceFilename(job,
true,
true, 0,
".*",
"",
true);
519 auto sanitizedPath =
path;
522 QString idRE =
"(?<id>\\d+).*";
523 QString datetimeRE =
"\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d-\\d\\d-\\d\\d";
524 sanitizedPath.
replace(idRE,
"{IDRE}");
525 sanitizedPath.
replace(datetimeRE,
"{DATETIMERE}");
532 auto filename = path_info.fileName();
535 filename.
replace(
"{IDRE}", idRE);
536 filename.
replace(
"{DATETIMERE}", datetimeRE);
542 for (
auto &name : matchingFiles)
544 match = re.match(name);
545 if (
match.hasMatch())
546 ids <<
match.captured(
"id").toInt();
552int PlaceholderPath::getCompletedFiles(
const SequenceJob &job)
554 return getCompletedFileIds(job).
length();
557int PlaceholderPath::getCompletedFiles(
const QString &path)
559 int seqFileCount = 0;
573 sig_file =
path.
mid(index + 1);
584 sig_file = sig_file.
left(index);
586 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Scheduler::PlaceholderPath path:" <<
path <<
" sig_dir:" << sig_dir <<
" sig_file:" <<
590 QString const sig_dir(path_info.dir().path());
591 QString const sig_file(path_info.completeBaseName());
603 if (
match.hasMatch())
610int PlaceholderPath::checkSeqBoundary(
const SequenceJob &job)
612 auto ids = getCompletedFileIds(job);
614 return *std::max_element(ids.
begin(), ids.
end()) + 1;
619PlaceholderPath::PathPropertyType PlaceholderPath::propertyType(PathProperty property)
628 return PP_TYPE_STRING;
642 return PP_TYPE_DOUBLE;
645 return PP_TYPE_POINT;
660 auto match = re.match(filename);
661 if (
match.hasMatch())
665 int numberLength =
match.captured(2).size();
668 int maxIterations = 2000;
673 if (--maxIterations <= 0)
Sequence Job is a container for the details required to capture a series of images.
char * toString(const EngineQuery &query)
Ekos is an advanced Astrophotography tool for Linux.
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
KIOCORE_EXPORT QString number(KIO::filesize_t size)
QString path(const QString &relativePath)
bool isValid(QStringView ifopt)
QStringView level(QStringView ifopt)
KIOCORE_EXPORT QString dir(const QString &fileClass)
QDateTime currentDateTime()
QString toString(QStringView format, QCalendar cal) const const
QString toNativeSeparators(const QString &pathName)
bool exists() const const
QString completeBaseName() const const
qsizetype length() const const
void push_back(parameter_type value)
size_type remove(const Key &key)
QString & append(QChar ch)
QString arg(Args &&... args) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
QString left(qsizetype n) const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)