15 #include <fmt/format.h>
16 #include <fmt/ostream.h>
17 #include <log4cplus/loggingmacros.h>
18 #include <boost/filesystem.hpp>
23 inline constexpr
bool always_false_v =
false;
33 struct FitsTraits<std::string>{
34 static constexpr
int DATA_TYPE = TSTRING;
37 struct FitsTraits<int64_t>{
38 static constexpr
int DATA_TYPE = TLONGLONG;
41 struct FitsTraits<uint64_t>{
43 static constexpr
int DATA_TYPE = TULONG;
46 struct FitsTraits<double>{
47 static constexpr
int DATA_TYPE = TDOUBLE;
50 struct FitsTraits<bool>{
51 static constexpr
int DATA_TYPE = TLOGICAL;
61 [&](
auto const& value) {
62 using T = std::decay_t<decltype(value)>;
64 if constexpr (std::is_same_v<T, std::string>) {
66 res = fmt::format(
"HIERARCH ESO {} = '{}' / {}", kw.
name, value,
67 kw.
comment.value_or(std::string()));
68 }
else if constexpr (std::is_same_v<T, bool>) {
70 res = fmt::format(
"HIERARCH ESO {} = {} / {}", kw.
name, value ?
'T' :
'F',
71 kw.
comment.value_or(std::string()));
74 res = fmt::format(
"HIERARCH ESO {} = {} / {}", kw.
name, value,
75 kw.
comment.value_or(std::string()));
77 std::strncpy(record.data(), res.data(), record.size());
88 os <<
"FitsController(id='" << ctl.
GetId() <<
"', state=" << ctl.
GetState() <<
")";
93 std::shared_ptr<ObservableEventLog> event_log,
96 , m_out_path(properties.ocm_dppart_root)
97 , m_event_log(std::move(event_log))
98 , m_fits_create(std::move(fits_create))
99 , m_state(NotStarted{})
105 , m_logger(log4cplus::Logger::getInstance(
"daq")) {
106 using boost::filesystem::absolute;
107 using boost::filesystem::path;
109 assert(m_fits_create);
110 LOG4CPLUS_DEBUG(m_logger, fmt::format(
"{}: Settings: out-path='{}', prefix='{}', ", *
this,
113 char hostname[HOST_NAME_MAX + 1];
114 if (gethostname(&hostname[0], HOST_NAME_MAX + 1) != 0) {
115 throw std::runtime_error(
"failed to get hostname");
117 m_hostname.append(&hostname[0]);
120 auto file_path = absolute(path(m_out_path));
121 m_out_path = file_path.native();
123 m_file_path = absolute(file_path).native();
124 LOG4CPLUS_INFO(m_logger, fmt::format(
"{}: OCM target FITS file is '{}'", *
this, m_file_path));
125 m_result.info = fmt::format(
"@{}:{}", m_hostname, m_file_path);
131 LOG4CPLUS_DEBUG(m_logger, fmt::format(
"{}: Start()", *
this));
132 using boost::filesystem::absolute;
133 using boost::filesystem::path;
135 if (!std::holds_alternative<NotStarted>(m_state)) {
136 auto msg = fmt::format(
"{}: Data Acquisition has already been started", *
this);
137 LOG4CPLUS_ERROR(m_logger, msg);
138 throw std::runtime_error(msg);
142 m_file = m_fits_create(m_file_path.c_str());
147 m_state.emplace<Acquiring>();
151 LOG4CPLUS_DEBUG(m_logger, fmt::format(
"{}: Stop()", *
this));
152 if (!std::holds_alternative<Acquiring>(m_state)) {
153 auto msg = fmt::format(
"{}: Data Acquisition is not acquiring", *
this);
154 LOG4CPLUS_ERROR(m_logger, msg);
155 throw std::runtime_error(msg);
162 m_state.emplace<Stopped>();
164 LOG4CPLUS_DEBUG(m_logger, fmt::format(
"{}: Stop() completed successfully: FITS file='{}'",
166 }
catch (std::exception
const& e) {
167 auto msg = fmt::format(
"{}: Writing to FITS '{}' failed", *
this, m_file_path);
168 LOG4CPLUS_ERROR(m_logger, msg);
169 if (policy == ErrorPolicy::Strict) {
178 LOG4CPLUS_INFO(m_logger, fmt::format(
"{}: Abort()", *
this));
179 if (std::holds_alternative<NotStarted>(m_state)) {
180 LOG4CPLUS_INFO(m_logger, fmt::format(
"{}: Aborting not started data acquisition", *
this));
185 if (!std::holds_alternative<Acquiring>(m_state)) {
186 auto msg = fmt::format(
"{}: Data Acquisition is not acquiring", *
this);
187 LOG4CPLUS_ERROR(m_logger, msg);
188 throw std::runtime_error(msg);
193 boost::filesystem::remove(m_file_path);
200 if (std::holds_alternative<Stopped>(m_state) || std::holds_alternative<Aborted>(m_state)) {
201 auto msg = fmt::format(
"{}: Cannot update keywords when Data Acquisition has already "
203 LOG4CPLUS_ERROR(m_logger, msg);
204 throw std::runtime_error(msg);
210 if (std::holds_alternative<Stopped>(m_state) || std::holds_alternative<Aborted>(m_state)) {
211 auto msg = fmt::format(
"{}: Cannot add comment when Data Acquisition has already "
213 LOG4CPLUS_ERROR(m_logger, msg);
214 throw std::runtime_error(msg);
216 m_comments.emplace_back(std::move(comment));
225 if (std::holds_alternative<Stopped>(m_state)) {
234 [&](
auto const& var) {
235 using T = std::decay_t<decltype(var)>;
236 if constexpr (std::is_same_v<T, NotStarted>) {
237 s = State::NotStarted;
238 }
else if constexpr (std::is_same_v<T, Acquiring>) {
239 s = State::Acquiring;
240 }
else if constexpr (std::is_same_v<T, Stopped>) {
242 }
else if constexpr (std::is_same_v<T, Aborted>) {
245 static_assert(always_false_v<T>,
"non-exhaustive visitor!");
253 void FitsControllerImpl::WriteFitsfile() {
256 std::stable_sort(m_keywords.begin(), m_keywords.end());
258 for (
auto& kw : m_keywords) {
261 using T = std::decay_t<decltype(var)>;
262 if constexpr (std::is_same_v<T, fits::ValueKeyword>) {
265 using T = std::decay_t<decltype(value)>;
267 if constexpr (std::is_same_v<T, std::string>) {
269 (void)fits_write_key(m_file.get(),
270 FitsTraits<T>::DATA_TYPE,
272 const_cast<char*
>(value.c_str()),
273 (var.comment.has_value() ?
274 var.comment.value().c_str() :
nullptr),
276 }
else if constexpr (std::is_same_v<T, bool>) {
281 (void)fits_write_key(m_file.get(),
282 FitsTraits<T>::DATA_TYPE,
285 (var.comment.has_value() ?
286 var.comment.value().c_str() :
nullptr),
289 (void)fits_write_key(m_file.get(),
290 FitsTraits<T>::DATA_TYPE,
293 (var.comment.has_value() ?
294 var.comment.value().c_str() :
nullptr),
299 fits_get_errstatus(err, &err_text[0]);
301 throw std::runtime_error(fmt::format(
"failed to write key: {}",
306 }
else if constexpr (std::is_same_v<T, fits::EsoKeyword>) {
307 std::array<char, FLEN_CARD> record{};
308 FormatEsoKeyword(var, record);
310 (void)fits_write_record(m_file.get(),
315 fits_get_errstatus(err, &err_text[0]);
317 throw std::runtime_error(fmt::format(
"failed to write record '{:.80s}': {}",
322 static_assert(always_false_v<T>,
"non-exhaustive visitor!");