7#include "placeholderpath.h"
9#include "sequencejob.h"
17#include <ekos_capture_debug.h>
21const QString PierSideStr =
"(East|West|Unknown)";
29 {FRAME_LIGHT,
"Light"},
33 {FRAME_VIDEO,
"Video"},
37PlaceholderPath::PlaceholderPath(
const QString &seqFilename)
38 : m_seqFilename(seqFilename)
42PlaceholderPath::PlaceholderPath():
47PlaceholderPath::~PlaceholderPath()
51QString PlaceholderPath::defaultFormat(
bool useFilter,
bool useExposure,
bool useTimestamp)
56 tempFormat.
append(
"%t_%T_");
66void PlaceholderPath::processJobInfo(
SequenceJob *job)
68 QString jobTargetName = job->getCoreProperty(SequenceJob::SJ_TargetName).toString();
69 auto frameType = getFrameType(job->getFrameType());
70 auto filterType = job->getCoreProperty(SequenceJob::SJ_Filter).toString();
71 auto exposure = job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
72 const auto isDarkFlat = job->jobType() == SequenceJob::JOBTYPE_DARKFLAT;
75 frameType =
"DarkFlat";
78 QString tempTargetName = KSUtils::sanitize(jobTargetName);
83 jobTargetName = tempTargetName;
86 QString imagePrefix = jobTargetName;
88 if (imagePrefix.
isEmpty() ==
false)
91 imagePrefix += frameType;
93 if (isFilterEnabled(job->getCoreProperty(SequenceJob::SJ_PlaceholderFormat).toString()) && filterType.isEmpty() ==
false &&
94 (job->getFrameType() == FRAME_LIGHT || job->getFrameType() == FRAME_FLAT || job->getFrameType() == FRAME_NONE
99 imagePrefix += filterType;
106 if (isExpEnabled(job->getCoreProperty(SequenceJob::SJ_PlaceholderFormat).toString()))
110 double fractpart, intpart;
111 fractpart = std::modf(exposure, &intpart);
116 else if (exposure >= 1e-3)
126 job->setCoreProperty(SequenceJob::SJ_FullPrefix, imagePrefix);
128 QString signature = generateSequenceFilename(*job,
true,
true, 1,
".fits",
"",
false,
true);
129 job->setCoreProperty(SequenceJob::SJ_Signature, signature);
134 QString imagePrefix = KSUtils::sanitize(targetName);
135 QString fullPrefix = constructPrefix(job, imagePrefix);
137 job->setCoreProperty(SequenceJob::SJ_FullPrefix, fullPrefix);
142 CCDFrameType frameType = job->getFrameType();
143 auto placeholderFormat = job->getCoreProperty(SequenceJob::SJ_PlaceholderFormat).toString();
144 auto filter = job->getCoreProperty(SequenceJob::SJ_Filter).toString();
146 double exposure = job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
148 QString tempImagePrefix = imagePrefix;
149 if (tempImagePrefix.
isEmpty() ==
false)
150 tempImagePrefix +=
'_';
152 const auto isDarkFlat = job->jobType() == SequenceJob::JOBTYPE_DARKFLAT;
154 tempImagePrefix += isDarkFlat ?
"DarkFlat" : CCDFrameTypeNames[frameType];
156 if (isFilterEnabled(placeholderFormat) &&
filter.isEmpty() ==
false &&
157 (frameType == FRAME_LIGHT ||
158 frameType == FRAME_FLAT ||
159 frameType == FRAME_NONE ||
162 tempImagePrefix +=
'_';
163 tempImagePrefix +=
filter;
165 if (isExpEnabled(placeholderFormat))
167 tempImagePrefix +=
'_';
169 double exposureValue = job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
172 if (exposureValue ==
static_cast<int>(exposureValue))
178 if (exposure >= 0.001)
184 if (isTsEnabled(placeholderFormat))
186 tempImagePrefix += SequenceJob::ISOMarker;
189 return tempImagePrefix;
194 const bool batch_mode,
195 const int nextSequenceID,
199 const bool gettingSignature)
202 setGenerateFilenameSettings(job, pathPropertyMap, local, gettingSignature);
204 return generateFilenameInternal(pathPropertyMap, local, batch_mode, nextSequenceID, extension, filename, glob,
205 gettingSignature, job.isVideo());
208QString PlaceholderPath::generateOutputFilename(
const bool local,
const bool batch_mode,
const int nextSequenceID,
210 const QString &filename,
const bool glob,
const bool gettingSignature)
const
212 return generateFilenameInternal(m_PathPropertyMap, local, batch_mode, nextSequenceID, extension, filename, glob,
217 bool usePattern)
const
221 switch (propertyType(property))
227 return "(true|false)";
231 if (property == PP_PIERSIDE)
237 else if (pathPropertyMap[property].
isValid())
239 switch (propertyType(property))
246 return QString(
"%1x%2").
arg(pathPropertyMap[PP_BIN].toPoint().x()).
arg(pathPropertyMap[PP_BIN].toPoint().y());
248 if (property == PP_PIERSIDE)
250 switch (
static_cast<ISD::Mount::PierSide
>(pathPropertyMap[property].toInt()))
252 case ISD::Mount::PIER_EAST:
254 case ISD::Mount::PIER_WEST:
261 return pathPropertyMap[property].toString();
263 return pathPropertyMap[property].toString();
268 switch (propertyType(property))
285 const bool batch_mode,
286 const int nextSequenceID,
290 const bool gettingSignature,
291 const bool isVideo)
const
293 QString targetNameSanitized = KSUtils::sanitize(pathPropertyMap[PP_TARGETNAME].
toString());
296 const QString format = pathPropertyMap[PP_FORMAT].toString();
297 const bool isDarkFlat = pathPropertyMap[PP_DARKFLAT].isValid() && pathPropertyMap[PP_DARKFLAT].toBool();
298 const CCDFrameType frameType =
static_cast<CCDFrameType
>(pathPropertyMap[PP_FRAMETYPE].toUInt());
299 QString tempFilename = filename;
302 currentDir = pathPropertyMap[PP_DIRECTORY].toString();
312 currentDir = currentDir.
left(currentDir.
length() - 1);
316 pathPropertyMap[PP_SUFFIX].toUInt()));
324 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>[_\\\\])?");
326 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>[_/])?");
329 while ((i = tempFormat.
indexOf(re, i, &match)) != -1)
332 if ((
match.captured(
"name") ==
"filename") || (
match.captured(
"name") ==
"f"))
333 replacement = m_seqFilename.baseName();
334 else if ((
match.captured(
"name") ==
"Datetime") || (
match.captured(
"name") ==
"D"))
336 if (glob || gettingSignature)
339 replacement =
"\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d-\\d\\d-\\d\\d";
341 replacement =
"ISO8601";
347 else if ((
match.captured(
"name") ==
"Type") || (
match.captured(
"name") ==
"T"))
350 replacement =
"DarkFlat";
352 replacement = getFrameType(frameType);
354 else if ((
match.captured(
"name") ==
"exposure") || (
match.captured(
"name") ==
"e") ||
355 (
match.captured(
"name") ==
"exp") || (
match.captured(
"name") ==
"E"))
357 double fractpart, intpart;
358 double exposure = pathPropertyMap[PP_EXPOSURE].toDouble();
359 fractpart = std::modf(exposure, &intpart);
362 else if (exposure >= 1e-3)
367 if ((
match.captured(
"name") ==
"exposure") || (
match.captured(
"name") ==
"e"))
368 replacement +=
QString(
"_secs");
370 else if ((
match.captured(
"name") ==
"Filter") || (
match.captured(
"name") ==
"F"))
373 if (
filter.isEmpty() ==
false
374 && (frameType == FRAME_LIGHT
375 || frameType == FRAME_FLAT
376 || frameType == FRAME_VIDEO
377 || frameType == FRAME_NONE
383 else if ((
match.captured(
"name") ==
"target") || (
match.captured(
"name") ==
"t"))
385 replacement = targetNameSanitized;
387 else if (((
match.captured(
"name") ==
"temperature") || (
match.captured(
"name") ==
"C")))
389 replacement = generateReplacement(pathPropertyMap, PP_TEMPERATURE,
390 (glob || gettingSignature) && pathPropertyMap[PP_TEMPERATURE].
isValid() ==
false);
392 else if (((
match.captured(
"name") ==
"bin") || (
match.captured(
"name") ==
"B")))
394 replacement = generateReplacement(pathPropertyMap, PP_BIN,
395 (glob || gettingSignature) && pathPropertyMap[PP_BIN].
isValid() ==
false);
397 else if (((
match.captured(
"name") ==
"gain") || (
match.captured(
"name") ==
"G")))
399 replacement = generateReplacement(pathPropertyMap, PP_GAIN,
400 (glob || gettingSignature) && pathPropertyMap[PP_GAIN].
isValid() ==
false);
402 else if (((
match.captured(
"name") ==
"offset") || (
match.captured(
"name") ==
"O")))
404 replacement = generateReplacement(pathPropertyMap, PP_OFFSET,
405 (glob || gettingSignature) && pathPropertyMap[PP_OFFSET].
isValid() ==
false);
407 else if (((
match.captured(
"name") ==
"iso") || (
match.captured(
"name") ==
"I"))
408 && pathPropertyMap[PP_ISO].isValid())
410 replacement = generateReplacement(pathPropertyMap, PP_ISO,
411 (glob || gettingSignature) && pathPropertyMap[PP_ISO].
isValid() ==
false);
413 else if (((
match.captured(
"name") ==
"pierside") || (
match.captured(
"name") ==
"P")))
415 replacement = generateReplacement(pathPropertyMap, PP_PIERSIDE, glob || gettingSignature);
432 else if ((
match.captured(
"name") ==
"sequence") || (
match.captured(
"name") ==
"s"))
435 replacement =
"(?<id>\\d+)";
439 if (!
match.captured(
"level").isEmpty())
441 replacement =
QString(
"%1").
arg(nextSequenceID, level, 10,
QChar(
'0'));
446 replacement = isVideo ?
"" :
"XXX";
450 qWarning() <<
"Unknown replacement string: " <<
match.captured(
"replace");
453 tempFormat = tempFormat.
replace(
match.capturedStart(),
match.capturedLength(), replacement);
455 tempFormat = tempFormat.
replace(
match.capturedStart(
"replace"),
match.capturedLength(
"replace"), replacement);
456 i += replacement.
length();
459 if (!gettingSignature)
460 tempFilename = tempFormat + extension;
468 const bool local,
const bool gettingSignature)
470 setPathProperty(pathPropertyMap, PP_TARGETNAME, job.getCoreProperty(SequenceJob::SJ_TargetName));
471 setPathProperty(pathPropertyMap, PP_FRAMETYPE,
QVariant(job.getFrameType()));
472 setPathProperty(pathPropertyMap, PP_FILTER, job.getCoreProperty(SequenceJob::SJ_Filter));
473 setPathProperty(pathPropertyMap, PP_EXPOSURE, job.getCoreProperty(SequenceJob::SJ_Exposure));
474 setPathProperty(pathPropertyMap, PP_DIRECTORY,
475 local ? job.getCoreProperty(SequenceJob::SJ_LocalDirectory) : job.getRemoteDirectory());
476 setPathProperty(pathPropertyMap, PP_FORMAT, job.getCoreProperty(SequenceJob::SJ_PlaceholderFormat));
477 setPathProperty(pathPropertyMap, PP_SUFFIX, job.getCoreProperty(SequenceJob::SJ_PlaceholderSuffix));
478 setPathProperty(pathPropertyMap, PP_DARKFLAT, job.jobType() == SequenceJob::JOBTYPE_DARKFLAT);
479 setPathProperty(pathPropertyMap, PP_BIN, job.getCoreProperty(SequenceJob::SJ_Binning));
480 setPathProperty(pathPropertyMap, PP_PIERSIDE,
QVariant(job.getPierSide()));
481 setPathProperty(pathPropertyMap, PP_ISO, job.getCoreProperty(SequenceJob::SJ_ISO));
484 if (job.getCoreProperty(SequenceJob::SJ_EnforceTemperature).toBool())
485 setPathProperty(pathPropertyMap, PP_TEMPERATURE,
QVariant(job.getTargetTemperature()));
486 else if (job.currentTemperature() != Ekos::INVALID_VALUE && !gettingSignature)
487 setPathProperty(pathPropertyMap, PP_TEMPERATURE,
QVariant(job.currentTemperature()));
489 pathPropertyMap.
remove(PP_TEMPERATURE);
491 if (job.getCoreProperty(SequenceJob::SequenceJob::SJ_Gain).toInt() >= 0)
492 setPathProperty(pathPropertyMap, PP_GAIN, job.getCoreProperty(SequenceJob::SJ_Gain));
493 else if (job.currentGain() >= 0 && !gettingSignature)
494 setPathProperty(pathPropertyMap, PP_GAIN, job.currentGain());
496 pathPropertyMap.
remove(PP_GAIN);
498 if (job.getCoreProperty(SequenceJob::SequenceJob::SJ_Offset).toInt() >= 0)
499 setPathProperty(pathPropertyMap, PP_OFFSET, job.getCoreProperty(SequenceJob::SJ_Offset));
500 else if (job.currentOffset() >= 0 && !gettingSignature)
501 setPathProperty(pathPropertyMap, PP_OFFSET, job.currentOffset());
503 pathPropertyMap.
remove(PP_OFFSET);
511 QRegularExpression re(
"(?<replace>\\%(?<name>[a-zA-Z])(?<level>\\d+)?)(?<sep>[_\\\\])+");
516 while ((i = filename.
indexOf(re, i, &match)) != -1)
518 if (
match.hasMatch())
520 i +=
match.capturedLength(
"replace");
527 QString path = generateSequenceFilename(job,
true,
true, 0,
".*",
"",
true);
528 auto sanitizedPath =
path;
531 QString idRE =
"(?<id>\\d+).*";
532 QString datetimeRE =
"\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d-\\d\\d-\\d\\d";
533 sanitizedPath.
replace(idRE,
"{IDRE}");
534 sanitizedPath.
replace(datetimeRE,
"{DATETIMERE}");
540 QString const sig_dir(path_info.dir().path());
541 if (sig_dir.contains(PierSideStr))
544 switch (
static_cast<ISD::Mount::PierSide
>(job.getPierSide()))
546 case ISD::Mount::PIER_EAST:
549 case ISD::Mount::PIER_WEST:
556 tempPath.
replace(PierSideStr, side);
561 auto filename = path_info.fileName();
564 filename.
replace(
"{IDRE}", idRE);
565 filename.
replace(
"{DATETIMERE}", datetimeRE);
571 for (
auto &name : matchingFiles)
573 match = re.match(name);
574 if (
match.hasMatch())
575 ids <<
match.captured(
"id").toInt();
581int PlaceholderPath::getCompletedFiles(
const SequenceJob &job)
583 return getCompletedFileIds(job).
length();
586int PlaceholderPath::getCompletedFiles(
const QString &path)
588 int seqFileCount = 0;
602 sig_file =
path.
mid(index + 1);
613 sig_file = sig_file.
left(index);
615 qCDebug(KSTARS_EKOS_CAPTURE) <<
"Scheduler::PlaceholderPath path:" <<
path <<
" sig_dir:" << sig_dir <<
" sig_file:" <<
619 QString const sig_dir(path_info.dir().path());
620 QString const sig_file(path_info.completeBaseName());
629 tempPath.
replace(PierSideStr,
"East");
631 int count = getCompletedFiles(newPath);
633 tempPath.
replace(PierSideStr,
"West");
635 count += getCompletedFiles(newPath);
637 tempPath.
replace(PierSideStr,
"Unknown");
639 count += getCompletedFiles(newPath);
648 if (
match.hasMatch())
655int PlaceholderPath::checkSeqBoundary(
const SequenceJob &job)
657 auto ids = getCompletedFileIds(job);
659 return *std::max_element(ids.
begin(), ids.
end()) + 1;
664PlaceholderPath::PathPropertyType PlaceholderPath::propertyType(PathProperty property)
673 return PP_TYPE_STRING;
687 return PP_TYPE_DOUBLE;
690 return PP_TYPE_POINT;
705 auto match = re.match(filename);
706 if (
match.hasMatch())
710 int numberLength =
match.captured(2).size();
713 int maxIterations = 2000;
718 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 contains(QChar ch, Qt::CaseSensitivity cs) 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)