7#include "placeholderpath.h"
9#include "sequencejob.h"
17#include <ekos_capture_debug.h>
24 {FRAME_LIGHT,
"Light"},
28 {FRAME_VIDEO,
"Video"},
32PlaceholderPath::PlaceholderPath(
const QString &seqFilename)
33 : m_seqFilename(seqFilename)
37PlaceholderPath::PlaceholderPath():
42PlaceholderPath::~PlaceholderPath()
46QString PlaceholderPath::defaultFormat(
bool useFilter,
bool useExposure,
bool useTimestamp)
51 tempFormat.
append(
"%t_%T_");
61void PlaceholderPath::processJobInfo(
SequenceJob *job)
63 QString jobTargetName = job->getCoreProperty(SequenceJob::SJ_TargetName).toString();
64 auto frameType = getFrameType(job->getFrameType());
65 auto filterType = job->getCoreProperty(SequenceJob::SJ_Filter).toString();
66 auto exposure = job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
67 const auto isDarkFlat = job->jobType() == SequenceJob::JOBTYPE_DARKFLAT;
70 frameType =
"DarkFlat";
73 QString tempTargetName = KSUtils::sanitize(jobTargetName);
78 jobTargetName = tempTargetName;
81 QString imagePrefix = jobTargetName;
83 if (imagePrefix.
isEmpty() ==
false)
86 imagePrefix += frameType;
88 if (isFilterEnabled(job->getCoreProperty(SequenceJob::SJ_PlaceholderFormat).toString()) && filterType.isEmpty() ==
false &&
89 (job->getFrameType() == FRAME_LIGHT || job->getFrameType() == FRAME_FLAT || job->getFrameType() == FRAME_NONE
94 imagePrefix += filterType;
101 if (isExpEnabled(job->getCoreProperty(SequenceJob::SJ_PlaceholderFormat).toString()))
105 double fractpart, intpart;
106 fractpart = std::modf(exposure, &intpart);
111 else if (exposure >= 1e-3)
121 job->setCoreProperty(SequenceJob::SJ_FullPrefix, imagePrefix);
123 QString signature = generateSequenceFilename(*job,
true,
true, 1,
".fits",
"",
false,
true);
124 job->setCoreProperty(SequenceJob::SJ_Signature, signature);
129 QString imagePrefix = KSUtils::sanitize(targetName);
130 QString fullPrefix = constructPrefix(job, imagePrefix);
132 job->setCoreProperty(SequenceJob::SJ_FullPrefix, fullPrefix);
137 CCDFrameType frameType = job->getFrameType();
138 auto placeholderFormat = job->getCoreProperty(SequenceJob::SJ_PlaceholderFormat).toString();
139 auto filter = job->getCoreProperty(SequenceJob::SJ_Filter).toString();
141 double exposure = job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
143 QString tempImagePrefix = imagePrefix;
144 if (tempImagePrefix.
isEmpty() ==
false)
145 tempImagePrefix +=
'_';
147 const auto isDarkFlat = job->jobType() == SequenceJob::JOBTYPE_DARKFLAT;
149 tempImagePrefix += isDarkFlat ?
"DarkFlat" : CCDFrameTypeNames[frameType];
151 if (isFilterEnabled(placeholderFormat) &&
filter.isEmpty() ==
false &&
152 (frameType == FRAME_LIGHT ||
153 frameType == FRAME_FLAT ||
154 frameType == FRAME_NONE ||
157 tempImagePrefix +=
'_';
158 tempImagePrefix +=
filter;
160 if (isExpEnabled(placeholderFormat))
162 tempImagePrefix +=
'_';
164 double exposureValue = job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
167 if (exposureValue ==
static_cast<int>(exposureValue))
173 if (exposure >= 0.001)
179 if (isTsEnabled(placeholderFormat))
181 tempImagePrefix += SequenceJob::ISOMarker;
184 return tempImagePrefix;
189 const bool batch_mode,
190 const int nextSequenceID,
194 const bool gettingSignature)
197 setGenerateFilenameSettings(job, pathPropertyMap, local, gettingSignature);
199 return generateFilenameInternal(pathPropertyMap, local, batch_mode, nextSequenceID, extension, filename, glob,
200 gettingSignature, job.isVideo());
203QString PlaceholderPath::generateOutputFilename(
const bool local,
const bool batch_mode,
const int nextSequenceID,
205 const QString &filename,
const bool glob,
const bool gettingSignature)
const
207 return generateFilenameInternal(m_PathPropertyMap, local, batch_mode, nextSequenceID, extension, filename, glob,
212 bool usePattern)
const
216 switch (propertyType(property))
222 return "(true|false)";
226 if (property == PP_PIERSIDE)
227 return "(East|West|Unknown)";
232 else if (pathPropertyMap[property].
isValid())
234 switch (propertyType(property))
241 return QString(
"%1x%2").
arg(pathPropertyMap[PP_BIN].toPoint().x()).
arg(pathPropertyMap[PP_BIN].toPoint().y());
243 if (property == PP_PIERSIDE)
245 switch (
static_cast<ISD::Mount::PierSide
>(pathPropertyMap[property].toInt()))
247 case ISD::Mount::PIER_EAST:
249 case ISD::Mount::PIER_WEST:
256 return pathPropertyMap[property].toString();
258 return pathPropertyMap[property].toString();
263 switch (propertyType(property))
280 const bool batch_mode,
281 const int nextSequenceID,
285 const bool gettingSignature,
286 const bool isVideo)
const
288 QString targetNameSanitized = KSUtils::sanitize(pathPropertyMap[PP_TARGETNAME].
toString());
291 const QString format = pathPropertyMap[PP_FORMAT].toString();
292 const bool isDarkFlat = pathPropertyMap[PP_DARKFLAT].isValid() && pathPropertyMap[PP_DARKFLAT].toBool();
293 const CCDFrameType frameType =
static_cast<CCDFrameType
>(pathPropertyMap[PP_FRAMETYPE].toUInt());
294 QString tempFilename = filename;
297 currentDir = pathPropertyMap[PP_DIRECTORY].toString();
307 currentDir = currentDir.
left(currentDir.
length() - 1);
311 pathPropertyMap[PP_SUFFIX].toUInt()));
319 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 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>[_/])?");
324 while ((i = tempFormat.
indexOf(re, i, &match)) != -1)
327 if ((
match.captured(
"name") ==
"filename") || (
match.captured(
"name") ==
"f"))
328 replacement = m_seqFilename.baseName();
329 else if ((
match.captured(
"name") ==
"Datetime") || (
match.captured(
"name") ==
"D"))
331 if (glob || gettingSignature)
334 replacement =
"\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d-\\d\\d-\\d\\d";
336 replacement =
"ISO8601";
342 else if ((
match.captured(
"name") ==
"Type") || (
match.captured(
"name") ==
"T"))
345 replacement =
"DarkFlat";
347 replacement = getFrameType(frameType);
349 else if ((
match.captured(
"name") ==
"exposure") || (
match.captured(
"name") ==
"e") ||
350 (
match.captured(
"name") ==
"exp") || (
match.captured(
"name") ==
"E"))
352 double fractpart, intpart;
353 double exposure = pathPropertyMap[PP_EXPOSURE].toDouble();
354 fractpart = std::modf(exposure, &intpart);
357 else if (exposure >= 1e-3)
362 if ((
match.captured(
"name") ==
"exposure") || (
match.captured(
"name") ==
"e"))
363 replacement +=
QString(
"_secs");
365 else if ((
match.captured(
"name") ==
"Filter") || (
match.captured(
"name") ==
"F"))
368 if (
filter.isEmpty() ==
false
369 && (frameType == FRAME_LIGHT
370 || frameType == FRAME_FLAT
371 || frameType == FRAME_VIDEO
372 || frameType == FRAME_NONE
378 else if ((
match.captured(
"name") ==
"target") || (
match.captured(
"name") ==
"t"))
380 replacement = targetNameSanitized;
382 else if (((
match.captured(
"name") ==
"temperature") || (
match.captured(
"name") ==
"C")))
384 replacement = generateReplacement(pathPropertyMap, PP_TEMPERATURE,
385 (glob || gettingSignature) && pathPropertyMap[PP_TEMPERATURE].
isValid() ==
false);
387 else if (((
match.captured(
"name") ==
"bin") || (
match.captured(
"name") ==
"B")))
389 replacement = generateReplacement(pathPropertyMap, PP_BIN,
390 (glob || gettingSignature) && pathPropertyMap[PP_BIN].
isValid() ==
false);
392 else if (((
match.captured(
"name") ==
"gain") || (
match.captured(
"name") ==
"G")))
394 replacement = generateReplacement(pathPropertyMap, PP_GAIN,
395 (glob || gettingSignature) && pathPropertyMap[PP_GAIN].
isValid() ==
false);
397 else if (((
match.captured(
"name") ==
"offset") || (
match.captured(
"name") ==
"O")))
399 replacement = generateReplacement(pathPropertyMap, PP_OFFSET,
400 (glob || gettingSignature) && pathPropertyMap[PP_OFFSET].
isValid() ==
false);
402 else if (((
match.captured(
"name") ==
"iso") || (
match.captured(
"name") ==
"I"))
403 && pathPropertyMap[PP_ISO].isValid())
405 replacement = generateReplacement(pathPropertyMap, PP_ISO,
406 (glob || gettingSignature) && pathPropertyMap[PP_ISO].
isValid() ==
false);
408 else if (((
match.captured(
"name") ==
"pierside") || (
match.captured(
"name") ==
"P")))
410 replacement = generateReplacement(pathPropertyMap, PP_PIERSIDE, glob || gettingSignature);
427 else if ((
match.captured(
"name") ==
"sequence") || (
match.captured(
"name") ==
"s"))
430 replacement =
"(?<id>\\d+)";
434 if (!
match.captured(
"level").isEmpty())
436 replacement =
QString(
"%1").
arg(nextSequenceID, level, 10,
QChar(
'0'));
441 replacement = isVideo ?
"" :
"XXX";
445 qWarning() <<
"Unknown replacement string: " <<
match.captured(
"replace");
448 tempFormat = tempFormat.
replace(
match.capturedStart(),
match.capturedLength(), replacement);
450 tempFormat = tempFormat.
replace(
match.capturedStart(
"replace"),
match.capturedLength(
"replace"), replacement);
451 i += replacement.
length();
454 if (!gettingSignature)
455 tempFilename = tempFormat + extension;
463 const bool local,
const bool gettingSignature)
465 setPathProperty(pathPropertyMap, PP_TARGETNAME, job.getCoreProperty(SequenceJob::SJ_TargetName));
466 setPathProperty(pathPropertyMap, PP_FRAMETYPE,
QVariant(job.getFrameType()));
467 setPathProperty(pathPropertyMap, PP_FILTER, job.getCoreProperty(SequenceJob::SJ_Filter));
468 setPathProperty(pathPropertyMap, PP_EXPOSURE, job.getCoreProperty(SequenceJob::SJ_Exposure));
469 setPathProperty(pathPropertyMap, PP_DIRECTORY,
470 job.getCoreProperty(local ? SequenceJob::SJ_LocalDirectory : SequenceJob::SJ_RemoteDirectory));
471 setPathProperty(pathPropertyMap, PP_FORMAT, job.getCoreProperty(SequenceJob::SJ_PlaceholderFormat));
472 setPathProperty(pathPropertyMap, PP_SUFFIX, job.getCoreProperty(SequenceJob::SJ_PlaceholderSuffix));
473 setPathProperty(pathPropertyMap, PP_DARKFLAT, job.jobType() == SequenceJob::JOBTYPE_DARKFLAT);
474 setPathProperty(pathPropertyMap, PP_BIN, job.getCoreProperty(SequenceJob::SJ_Binning));
475 setPathProperty(pathPropertyMap, PP_PIERSIDE,
QVariant(job.getPierSide()));
476 setPathProperty(pathPropertyMap, PP_ISO, job.getCoreProperty(SequenceJob::SJ_ISO));
479 if (job.getCoreProperty(SequenceJob::SJ_EnforceTemperature).toBool())
480 setPathProperty(pathPropertyMap, PP_TEMPERATURE,
QVariant(job.getTargetTemperature()));
481 else if (job.currentTemperature() != Ekos::INVALID_VALUE && !gettingSignature)
482 setPathProperty(pathPropertyMap, PP_TEMPERATURE,
QVariant(job.currentTemperature()));
484 pathPropertyMap.
remove(PP_TEMPERATURE);
486 if (job.getCoreProperty(SequenceJob::SequenceJob::SJ_Gain).toInt() >= 0)
487 setPathProperty(pathPropertyMap, PP_GAIN, job.getCoreProperty(SequenceJob::SJ_Gain));
488 else if (job.currentGain() >= 0 && !gettingSignature)
489 setPathProperty(pathPropertyMap, PP_GAIN, job.currentGain());
491 pathPropertyMap.
remove(PP_GAIN);
493 if (job.getCoreProperty(SequenceJob::SequenceJob::SJ_Offset).toInt() >= 0)
494 setPathProperty(pathPropertyMap, PP_OFFSET, job.getCoreProperty(SequenceJob::SJ_Offset));
495 else if (job.currentOffset() >= 0 && !gettingSignature)
496 setPathProperty(pathPropertyMap, PP_OFFSET, job.currentOffset());
498 pathPropertyMap.
remove(PP_OFFSET);
506 QRegularExpression re(
"(?<replace>\\%(?<name>[a-zA-Z])(?<level>\\d+)?)(?<sep>[_\\\\])+");
511 while ((i = filename.
indexOf(re, i, &match)) != -1)
513 if (
match.hasMatch())
515 i +=
match.capturedLength(
"replace");
522 QString path = generateSequenceFilename(job,
true,
true, 0,
".*",
"",
true);
523 auto sanitizedPath =
path;
526 QString idRE =
"(?<id>\\d+).*";
527 QString datetimeRE =
"\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d-\\d\\d-\\d\\d";
528 sanitizedPath.
replace(idRE,
"{IDRE}");
529 sanitizedPath.
replace(datetimeRE,
"{DATETIMERE}");
536 auto filename = path_info.fileName();
539 filename.
replace(
"{IDRE}", idRE);
540 filename.
replace(
"{DATETIMERE}", datetimeRE);
546 for (
auto &name : matchingFiles)
548 match = re.match(name);
549 if (
match.hasMatch())
550 ids <<
match.captured(
"id").toInt();
556int PlaceholderPath::getCompletedFiles(
const SequenceJob &job)
558 return getCompletedFileIds(job).
length();
561int PlaceholderPath::getCompletedFiles(
const QString &path)
563 int seqFileCount = 0;
577 sig_file =
path.
mid(index + 1);
588 sig_file = sig_file.
left(index);
590 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Scheduler::PlaceholderPath path:" <<
path <<
" sig_dir:" << sig_dir <<
" sig_file:" <<
594 QString const sig_dir(path_info.dir().path());
595 QString const sig_file(path_info.completeBaseName());
607 if (
match.hasMatch())
614int PlaceholderPath::checkSeqBoundary(
const SequenceJob &job)
616 auto ids = getCompletedFileIds(job);
618 return *std::max_element(ids.
begin(), ids.
end()) + 1;
623PlaceholderPath::PathPropertyType PlaceholderPath::propertyType(PathProperty property)
632 return PP_TYPE_STRING;
646 return PP_TYPE_DOUBLE;
649 return PP_TYPE_POINT;
664 auto match = re.match(filename);
665 if (
match.hasMatch())
669 int numberLength =
match.captured(2).size();
672 int maxIterations = 2000;
677 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)