9#include <log4cplus/loggingmacros.h>
20 fitsfile* file = source.GetFitsFile();
23 (void)fits_movabs_hdu(file, 1, &type, &status);
26 auto const max_dim = 10;
27 long naxes[max_dim] = {0};
29 fits_get_img_dim(file, &naxis, &status);
30 fits_get_img_size(file, max_dim, naxes, &status);
36 naxis = naxis < max_dim ? naxis : max_dim;
37 bool has_data =
false;
38 for (
auto axis = 0; axis < naxis; ++axis) {
39 if (naxes[axis] > 0u) {
47 auto dim_str = std::string();
48 for (
auto axis = 0; axis < naxis; ++axis) {
49 if (naxes[axis] > 0u) {
50 auto dim = naxes[axis];
52 dim_str += fmt::format(
", {}", dim);
54 dim_str += fmt::format(
"{}", dim);
58 auto msg = fmt::format(
59 "Unmerged data: Source file has data in primary HDU that cannot be merged: '{}' "
60 "produced file '{}' that was copied from '{}' has dimensions ({})",
62 source.GetFilePath().c_str(),
65 LOG4CPLUS_WARN(logger, msg);
69 "unmerged-{}-{}", source.GetName(), source.GetFilePath().filename().native()),
77 return source.GetKeywordRuleProcessor().Process(
81 auto msg = fmt::format(
"Processing keyword rules failed for JSON keywords source '{}'",
83 LOG4CPLUS_ERROR(ops.
logger, msg);
84 std::throw_with_nested(std::runtime_error(msg));
89 auto logger = log4cplus::Logger::getInstance(
"daq.dpmmerge");
92 result.reserve(literal_kws.size());
93 std::copy(std::make_move_iterator(literal_kws.begin()),
94 std::make_move_iterator(literal_kws.end()),
95 std::back_inserter(result));
98 return source.GetKeywordRuleProcessor().Process(
101 auto msg = fmt::format(
"Processing keyword rules failed for FITS file keywords source '{}'",
102 source.GetFilePath().string());
103 LOG4CPLUS_ERROR(logger, msg);
104 std::throw_with_nested(std::runtime_error(msg));
113 std::vector<SourceTypes>
const& sources) {
114 auto logger = log4cplus::Logger::getInstance(
"daq.dpmmerge");
115 constexpr const int primary_hdu_num = 1;
118 LOG4CPLUS_DEBUG(logger,
"Read keywords from " << target.GetFilePath());
119 std::for_each(std::begin(literal_kws), std::end(literal_kws), [&](
auto const& kw) {
120 LOG4CPLUS_DEBUG(logger, kw);
122 result.reserve(literal_kws.size());
123 std::copy(std::begin(literal_kws), std::end(literal_kws), std::back_inserter(result));
127 result = target.GetKeywordRuleProcessor().Process(
129 LOG4CPLUS_DEBUG(logger,
"Result after keyword processing: " << target.GetFilePath());
130 std::for_each(std::begin(result), std::end(result), [&](
auto const& kw) {
131 LOG4CPLUS_DEBUG(logger, kw);
134 auto msg = fmt::format(
"Processing keyword rules failed for target FITS file '{}'",
135 target.GetFilePath().string());
136 LOG4CPLUS_ERROR(logger, msg);
137 std::throw_with_nested(std::runtime_error(msg));
140 for (
auto const& source : sources) {
141 std::string path_or_name;
142 auto kws = std::visit(
144 using T = std::decay_t<
decltype(source)>;
145 if constexpr (std::is_same_v<T, FitsKeywordsSource>) {
146 path_or_name = fmt::format(
"{}: (keyword list)", source.GetName());
148 }
else if constexpr (std::is_same_v<T, FitsFileSource>) {
150 fmt::format(
"{}: {}", source.GetName(), source.GetFilePath().native());
155 LOG4CPLUS_DEBUG(logger,
"Updating with keywords from : " << path_or_name);
157 std::begin(kws), std::end(kws), [&](
auto const& kw) { LOG4CPLUS_DEBUG(logger, kw); });
165std::vector<fits::LiteralKeyword>
FormatKeywords(fits::KeywordVector::const_iterator begin,
166 fits::KeywordVector::const_iterator end,
168 auto const& logger = log4cplus::Logger::getInstance(
"daq.dpmmerge");
169 std::vector<fits::LiteralKeyword> result;
170 LOG4CPLUS_DEBUG(logger,
"Formatting keywords ...");
173 LOG4CPLUS_DEBUG(logger,
"Formatting keyword input: \"" << kw <<
"\"");
175 LOG4CPLUS_DEBUG(logger,
"Formatting keyword result: \"" << formatted <<
"\"");
178 LOG4CPLUS_DEBUG(logger,
"Formatting keywords done.");
182template <
class Container>
183void LogKeywords(log4cplus::Logger
const& logger, Container
const& keywords) {
184 std::for_each(std::begin(keywords), std::end(keywords), [&](
auto const& kw) {
185 LOG4CPLUS_DEBUG(logger, kw);
192 std::vector<SourceTypes>
const& sources,
194 auto const& logger = ops.
logger;
195 LOG4CPLUS_INFO(logger,
"Merge primary HDU keywords");
196 LOG4CPLUS_INFO(logger,
"Compile primary keywords");
197 constexpr const int primary_hdu_num = 1;
202 mandatory.emplace_back(std::in_place_type<fits::ValueKeyword>,
"ORIGFILE", params.
origfile);
203 mandatory.emplace_back(std::in_place_type<fits::ValueKeyword>,
"ARCFILE", params.
arcfile);
205 primary_hdu_keywords, primary_hdu_keywords.end(), mandatory.begin(), mandatory.end());
207 LOG4CPLUS_INFO(logger,
"Format keywords");
209 std::begin(primary_hdu_keywords), std::end(primary_hdu_keywords), ops.
keyword_formatter);
211 LOG4CPLUS_INFO(logger,
"Sort keywords");
213 LOG4CPLUS_DEBUG(logger,
"Sorted keywords");
220 LOG4CPLUS_INFO(logger,
"Clear keywords to make room for writing back sorted keywords.");
222 LOG4CPLUS_INFO(logger,
"Writing keywords");
223 std::optional<ssize_t> remaining_size;
225 if (remaining_size) {
226 if (*remaining_size < 0) {
227 auto needed = -*remaining_size;
229 auto msg = fmt::format(
230 "Writing keywords required resizing of primary HDU: Add space for at least "
232 "keywords to avoid resize",
236 auto msg = fmt::format(
"Primary HDU keyword space remaining: {} ", *remaining_size);
237 LOG4CPLUS_INFO(logger, msg);
241 LOG4CPLUS_INFO(logger,
"Writing keywords SKIPPED (dry-run)");
252 auto const& logger = ops.
logger;
255 "Merging HDU extensions from " << source.GetName() <<
"(" << source.GetFilePath() <<
")");
261 fitsfile* source_fits = source.GetFitsFile();
262 fitsfile* target_fits = target.GetFitsFile();
265 fits_get_num_hdus(source_fits, &num_hdus, &status);
268 fmt::format(
"Failed to get number of HDUs from '{}'", target.GetFilePath().c_str());
269 LOG4CPLUS_ERROR(logger, msg);
276 LOG4CPLUS_INFO(ops.
logger,
277 "Note: No HDU extensions to merge from " << source.GetFilePath());
285 int previous =
false;
287 int following =
true;
288 fits_copy_file(source_fits, target_fits, previous, current, following, &status);
290 auto const msg =
"FITS function fits_copy_file failed";
291 LOG4CPLUS_ERROR(logger, msg);
295 LOG4CPLUS_INFO(logger,
296 "Merging HDU extensions from " << source.GetName() <<
"("
297 << source.GetFilePath()
298 <<
") SKIPPED (dry-run)");
304 std::vector<SourceTypes>
const& sources,
306 auto const& logger = ops.
logger;
307 LOG4CPLUS_INFO(logger,
"Merging HDU extensions");
309 for (
auto const& source_var : sources) {
310 if (!std::holds_alternative<FitsFileSource>(source_var)) {
313 FitsFileSource const& source = std::get<FitsFileSource>(source_var);
317 std::throw_with_nested(
318 std::runtime_error(fmt::format(
"Failed to copy HDU extensions from '{}' to '{}'",
319 source.GetFilePath().c_str(),
320 target.GetFilePath().c_str())));
323 LOG4CPLUS_INFO(logger,
"Merging HDU extensions completed successfully");
327 auto const& logger = ops.
logger;
328 LOG4CPLUS_INFO(logger,
"Updating checksums for all HDUs");
330 fitsfile* target_fits = target.GetFitsFile();
332 fits_get_num_hdus(target_fits, &num_hdus, &status);
335 fmt::format(
"Failed to get number of HDUs from '{}'", target.GetFilePath().c_str());
336 LOG4CPLUS_ERROR(logger, msg);
341 for (
int hdu_num = 1; hdu_num <= num_hdus; ++hdu_num) {
342 LOG4CPLUS_DEBUG(logger,
"Updating checksum for HDU " << hdu_num);
346 LOG4CPLUS_INFO(logger,
"Updating checkum keywords SKIPPED (dry-run)");
348 LOG4CPLUS_INFO(logger,
"Updating checkum keywords completed for all HDUs successfully");
354 std::vector<SourceTypes>
const& sources,
356 auto const& logger = ops.
logger;
373 LOG4CPLUS_INFO(logger,
"Starting merge operation");
377 std::throw_with_nested(std::runtime_error(
"Failed to merge primary HDU keywords"));
383 std::throw_with_nested(std::runtime_error(
"Failed to merge primary HDU keywords"));
388 std::throw_with_nested(std::runtime_error(
"Failed to update checksums"));
391 LOG4CPLUS_INFO(logger,
"Completed successfully");
Contains functions and data structures related to cfitsio.
@ User
Default is to keep only user-keywords.
@ All
Default rule is to keep all keywords (useful for in-place merge)
std::string const & GetLocation() const noexcept
bool AlertUnmergeable() const noexcept
fits::KeywordVector const & GetKeywords() const &noexcept
virtual void SortKeywords(std::vector< fits::LiteralKeyword > &keywords)=0
Sort keywords.
Interface to reporter (implementations exist for JSON or human readable)
virtual void PostAlert(std::string const &id, std::string const &message)=0
Post event.
Represents errors from cfitsio.
Represents the literal 80-character FITS keyword record.
fits::KeywordVector CompilePrimaryHduKeywords(Operations ops, TargetSource &target, std::vector< SourceTypes > const &sources)
Compiles unique keywords and formats primary HDU keywords.
KeywordFormatter & keyword_formatter
fits::KeywordVector CompileKeywords(Operations ops, FitsKeywordsSource const &source)
log4cplus::Logger const & logger
void CheckUnmergedPrimaryHduData(log4cplus::Logger const &logger, StatusReporter &status_reporter, FitsFileSource const &source)
Check for data in primary HDU which would not be merged and produce alert.
void UpdateChecksums(Operations ops, TargetSource &target, bool dry_run)
KeywordSorter & keyword_sorter
void CopyExtensions(Operations ops, TargetSource &target, FitsFileSource const &source, bool dry_run)
Copy all extensions from source to target.
void Merge(Operations ops, Params const ¶ms, TargetSource &target, std::vector< SourceTypes > const &sources, bool dry_run)
Merge sources into the target target.
void LogKeywords(log4cplus::Logger const &logger, Container const &keywords)
void MergeHduExtensions(Operations ops, TargetSource &target, std::vector< SourceTypes > const &sources, bool dry_run)
StatusReporter & status_reporter
void MergePrimaryHduKeywords(Operations ops, Params const ¶ms, TargetSource &target, std::vector< SourceTypes > const &sources, bool dry_run)
std::vector< fits::LiteralKeyword > FormatKeywords(fits::KeywordVector::const_iterator begin, fits::KeywordVector::const_iterator end, KeywordFormatter &fmt)
void SelectHduNum(fitsfile *ptr, int hdu_num)
Select current HDU number.
void InsertKeywords(KeywordVector &keywords, KeywordVector::iterator position, KeywordVector::const_iterator from_first, KeywordVector::const_iterator from_last)
Insert keywords.
void WriteKeywords(fitsfile *ptr, int hdu_num, std::vector< LiteralKeyword > const &keywords, std::optional< ssize_t > *remaining_size)
Write keywords to HDU identified by number hdu_num.
void WriteChecksum(fitsfile *ptr, int hdu_num)
Write or update checksum keywords DATASUM and CHECKSUM to HDU specified by hdu_num.
void DeleteAllKeywords(fitsfile *ptr, int hdu_num)
Delete all keywords from HDU.
std::vector< KeywordVariant > KeywordVector
Vector of keywords.
void UpdateKeywords(KeywordVector &to, KeywordVector const &from, ConflictPolicy policy=ConflictPolicy::Replace)
Updates to with keywords from from.
std::vector< LiteralKeyword > ReadKeywords(fitsfile *ptr, int hdu_num)
Read keywords from HDU identifed by absolute position hdu_num.
@ Skip
Skip keyword that conflicts.