14#include <fmt/format.h>
15#include <fmt/ostream.h>
16#include <log4cplus/loggingmacros.h>
17#include <mal/rr/qos/ConnectionTime.hpp>
18#include <nlohmann/json.hpp>
30using boost::enable_current_exception;
31using boost::make_exceptional_future;
34using std::chrono::duration_cast;
35using std::chrono::milliseconds;
39auto MakeDaqifException(std::string
const&
id,
40 std::string
const& msg,
41 std::exception
const& exception) {
44 return boost::enable_current_exception(
45 daqif::DaqException(
id, fmt::format(
"{}", nested_msg)));
48 return boost::enable_current_exception(
49 daqif::DaqException(
id, fmt::format(
"{}\n{}", msg, nested_msg)));
53std::vector<daq::DaqContext::Source>
ParseSource(std::string
const& str) {
54 std::vector<daq::DaqContext::Source> sources;
57 sources.reserve(raw_sources.size());
58 for (
auto const& raw : raw_sources) {
59 sources.push_back({raw.name, raw.rr_uri});
65void ValidateFilePrefix(
char const* file_prefix) {
66 auto file_regex = std::regex(R
"(^[-a-zA-Z0-9_\.]*$)");
68 if (!std::regex_match(file_prefix, file_regex)) {
69 throw std::invalid_argument(
70 fmt::format(
"file_prefix \"{}\" contains illegal characters, allowed: [a-zA-Z-0-9-_.]",
91 using std::chrono::duration_cast;
92 using std::chrono::milliseconds;
93 using Seconds = std::chrono::duration<double>;
96 if (json_properties.empty()) {
101 auto json = nlohmann::json::parse(json_properties);
102 if (!json.is_object()) {
103 throw boost::enable_current_exception(std::invalid_argument(
104 fmt::format(
"expected type object but got type {}", json.type_name())));
106 if (json.contains(
"keywords")) {
109 if (json.contains(
"awaitInterval")) {
110 auto& value = json[
"awaitInterval"];
111 if (!value.is_number()) {
112 throw boost::enable_current_exception(std::invalid_argument(
113 fmt::format(
"'awaitInterval': unsupported type: {}", value.type_name())));
115 auto await_interval = value.get<
double>();
116 if (await_interval < 0.0) {
117 throw boost::enable_current_exception(std::invalid_argument(
118 fmt::format(
"'awaitInterval' must be positive number, got {}", await_interval)));
120 properties.
await_interval = duration_cast<milliseconds>(Seconds(value.get<
double>()));
130 : name(std::move(name)), rr_uri(std::move(rr_uri)) {
133 os <<
"name: '" << s.
name <<
"', rr_uri='" << s.
rr_uri <<
"'";
138 auto start = s.find_first_not_of(
' ');
139 auto name_end_pos = s.find_first_of(
'@');
141 if (name_end_pos == std::string_view::npos) {
142 throw boost::enable_current_exception(
143 std::invalid_argument(
"separator '@' not found in expected format 'name@rr-uri'"));
145 auto name = s.substr(start, name_end_pos - start);
147 throw boost::enable_current_exception(
148 std::invalid_argument(
"name part in 'name@rr-uri' is empty"));
151 start = name_end_pos + 1;
152 if (start >= s.size()) {
153 throw boost::enable_current_exception(
154 std::invalid_argument(
"invalid format string, expected format 'name@rr-uri'"));
156 auto rr_uri_end_pos = s.find_first_of(
" ,", start);
157 if (name_end_pos == std::string_view::npos) {
158 throw boost::enable_current_exception(std::invalid_argument(
"separator ',' not found"));
161 auto rr_uri = s.substr(start, rr_uri_end_pos - start);
162 if (rr_uri.empty()) {
163 throw boost::enable_current_exception(
164 std::invalid_argument(
"rr_uri part in 'name@rr-uri' is empty"));
166 return ParsedSource(std::string(name), std::string(rr_uri));
175 std::vector<ParsedSource> result;
176 std::size_t begin = 0;
178 while (begin < s.size()) {
179 const auto end = s.find_first_of(
' ', begin);
182 result.emplace_back(
ParseSourceUri(s.substr(begin, end - begin)));
185 if (end == std::string_view::npos) {
198 std::string proc_name,
199 std::string output_path,
200 std::shared_ptr<daq::ObservableEventLog> event_log)
202 , m_executor(m_io_ctx)
205 , m_proc_name(std::move(proc_name))
206 , m_output_path(std::move(output_path))
207 , m_event_log(std::move(event_log))
208 , m_log_observer_connection()
209 , m_log_observer(log4cplus::Logger::getInstance(
server::LOGGER_NAME_EVENTLOG))
210 , m_logger(log4cplus::Logger::getInstance(
server::LOGGER_NAME)) {
211 m_log_observer_connection =
212 m_event_log->ConnectObserver(std::reference_wrapper(m_log_observer));
213 if (m_proc_name.empty()) {
214 throw boost::enable_current_exception(
215 std::invalid_argument(
"OcmDaqService: Process name cannot be empty"));
220 m_log_observer_connection.disconnect();
223boost::future<std::shared_ptr<::daqif::DaqReply>>
225 const std::string& file_prefix,
226 const std::string& primary_sources,
227 const std::string& metadata_sources,
228 const std::string& json_properties) {
231 [=, self = shared_from_this()]()
mutable {
234 fmt::format(
"Request received: "
235 "StartDaq(id='{0}', file_prefix='{1}', "
236 "primary_sources='{2}', metadata_sources='{3}', "
237 "json_properties='{4}'",
245 ValidateFilePrefix(file_prefix.c_str());
246 }
catch (std::exception
const& e) {
247 self->m_event_log->AddEvent(
249 return boost::make_exceptional_future<std::shared_ptr<::daqif::DaqReply>>(
250 daqif::DaqException(
id, e.what()));
257 }
catch (std::exception
const& e) {
259 fmt::format(
"Failed to parse StartDaq JSON properties: {}", e.what());
260 self->m_event_log->AddEvent(
daq::ErrorEvent(
id, msg, std::nullopt,
"user"));
261 return boost::make_exceptional_future<std::shared_ptr<::daqif::DaqReply>>(
262 daqif::DaqException(
id, msg));
266 auto validated_id = id;
267 if (validated_id.empty()) {
269 validated_id = context.
file_id;
272 fmt::format(
"StartDaq(id='{0}'): Created and assigned DAQ id='{0}'",
276 if (self->m_mgr.HaveDaq(validated_id)) {
279 fmt::format(
"StartDaq(id='{0}'): DAQ with id='{0}' already exist",
281 return boost::make_exceptional_future<std::shared_ptr<daqif::DaqReply>>(
282 daqif::DaqException(
id,
283 "Data acquisition with same id already exist"));
288 context.
id = validated_id;
297 return self->StartDaq(context,
"StartDaq");
298 }
catch (daqif::DaqException
const&) {
301 }
catch (std::invalid_argument
const& e) {
302 LOG4CPLUS_INFO(self->m_logger,
303 fmt::format(
"StartDaq(id='{}'): Invalid argument error while "
304 "processing request: {}",
307 return boost::make_exceptional_future<std::shared_ptr<daqif::DaqReply>>(
308 daqif::DaqException(validated_id, e.what()));
309 }
catch (std::exception
const& e) {
310 LOG4CPLUS_INFO(self->m_logger,
311 fmt::format(
"StartDaq(id='{}'): Error while"
312 "processing request: {}",
315 return boost::make_exceptional_future<std::shared_ptr<daqif::DaqReply>>(
316 daqif::DaqException(validated_id, e.what()));
320 fmt::format(
"StartDaq(id='{}'): Unknown error while processing request",
322 return boost::make_exceptional_future<std::shared_ptr<daqif::DaqReply>>(
323 daqif::DaqException(validated_id,
"Uknown error"));
330boost::future<std::shared_ptr<::daqif::DaqReply>>
332 return boost::async(m_executor,
333 [=, self = shared_from_this()]()
mutable {
336 fmt::format(
"Request received: "
337 "StartDaqV2(specification=\n'{0}')",
343 nlohmann::json::parse(specification));
348 UpdateFrom(context, parsed);
352 ValidateDaqContext(context);
357 LOG4CPLUS_DEBUG(self->m_logger,
358 "Resulting specification after parsing: \n"
363 return self->StartDaq(context,
"StartDaqV2");
364 }
catch (daqif::DaqException
const&) {
367 }
catch (nlohmann::json::exception
const& e) {
368 throw MakeDaqifException(
"",
"JSON parsing error", e);
370 throw MakeDaqifException(
"",
"JSON Schema error", e);
371 }
catch (std::invalid_argument
const& e) {
372 throw MakeDaqifException(
"",
"Invalid argument", e);
373 }
catch (std::exception
const& e) {
374 throw MakeDaqifException(
"",
"", e);
383 [self = shared_from_this(),
id]() {
385 fmt::format(
"Request received: "
389 return self->StopDaq(
id,
false);
394boost::future<std::shared_ptr<::daqif::DaqReply>>
396 return boost::async(m_executor,
397 [self = shared_from_this(),
id]() {
398 self->m_event_log->AddEvent(
400 fmt::format(
"Request received: "
401 "ForceStopDaq(id='{0}')",
404 return self->StopDaq(
id,
true);
409boost::future<std::shared_ptr<::daqif::DaqReply>>
413 [function, weak_self = std::weak_ptr(shared_from_this()),
id = context.
id](
414 boost::future<daq::State> f) -> std::shared_ptr<daqif::DaqReply> {
415 auto self = weak_self.lock();
417 LOG4CPLUS_WARN(LOGGER_NAME,
418 fmt::format(
"{}(id='{}'): StartDaqAsync is "
419 "complete but MAL service has "
420 "been abandoned. Throwing exception.",
423 throw boost::enable_current_exception(
424 daqif::DaqException(id,
"Service has been abandoned"));
428 auto rep = self->m_mal.createDataEntity<daqif::DaqReply>();
431 rep->setError(false);
434 auto what = self->MakeExceptionMessageWithStatus(
id, std::current_exception());
435 LOG4CPLUS_ERROR(self->m_logger,
436 fmt::format(
"{}(id='{}'): StartDaqAsync "
437 "completed with failure: {}",
441 throw boost::enable_current_exception(
442 daqif::DaqException(
id, fmt::format(
"{}() failed: {}", function, what)));
447boost::future<std::shared_ptr<::daqif::DaqReply>>
451 [self = shared_from_this(),
id, forced]() {
457 [weak_self = std::weak_ptr(self->shared_from_this()),
458 id](boost::future<daq::Status> f) -> std::shared_ptr<daqif::DaqReply> {
459 auto self = weak_self.lock();
461 LOG4CPLUS_WARN(LOGGER_NAME,
462 fmt::format(
"StopDaq(id='{}'): StopDaqAsync is "
463 "complete but MAL service has "
464 "been abandoned. Throwing exception.",
466 throw boost::enable_current_exception(
467 daqif::DaqException(id,
"Service has been abandoned"));
471 auto rep = self->m_mal.createDataEntity<daqif::DaqReply>();
474 rep->setError(false);
477 auto what = self->MakeExceptionMessageWithStatus(
478 id, std::current_exception());
479 LOG4CPLUS_INFO(self->m_logger,
480 fmt::format(
"StopDaq(id='{}'): "
481 "completed with failure: {}",
484 throw boost::enable_current_exception(daqif::DaqException(
485 id, fmt::format(
"Stop failed\n\n{}", what)));
494 return boost::async(m_executor,
495 [self = shared_from_this(),
id]() {
496 self->m_event_log->AddEvent(
498 fmt::format(
"Request received: "
499 "AbortDaq(id='{0}')",
502 return self->AbortDaq(
id,
false);
507boost::future<std::shared_ptr<::daqif::DaqReply>>
509 return boost::async(m_executor,
510 [self = shared_from_this(),
id]() {
511 self->m_event_log->AddEvent(
513 fmt::format(
"Request received: "
514 "ForceAbortDaq(id='{0}')",
517 return self->AbortDaq(
id,
true);
522boost::future<std::shared_ptr<::daqif::DaqReply>>
526 [self = shared_from_this(),
id, forced]() {
532 [weak_self = std::weak_ptr(self->shared_from_this()),
id, forced](
533 boost::future<daq::Status> f) -> std::shared_ptr<daqif::DaqReply> {
534 auto self = weak_self.lock();
538 fmt::format(
"AbortDaq(id='{}', forced={}): AbortDaqAsync is "
539 "complete but MAL service has "
540 "been abandoned. Throwing exception.",
543 throw boost::enable_current_exception(
544 daqif::DaqException(id,
"Service has been abandoned"));
547 auto result = f.get();
550 fmt::format(
"AbortDaq(id='{}', forced={}): "
551 "AbortDaqAsync Completed successfully",
554 auto rep = self->m_mal.createDataEntity<daqif::DaqReply>();
557 rep->setError(HasError(result));
560 fmt::format(
"AbortDaq(id='{}', forced={}): "
561 "AbortDaqAsync Completed, returning reply now.",
566 auto what = self->MakeExceptionMessageWithStatus(
567 id, std::current_exception());
568 LOG4CPLUS_ERROR(self->m_logger,
569 fmt::format(
"AbortDaq(id='{}', forced={}): "
570 "AbortDaqAsync Completed "
571 "with fatal error:\n{}",
575 throw boost::enable_current_exception(daqif::DaqException(
576 id, fmt::format(
"Abort failed\n\n{}", what)));
584boost::future<std::shared_ptr<::daqif::DaqReply>>
588 [self = shared_from_this(),
id, keywords]() -> std::shared_ptr<::daqif::DaqReply> {
589 self->m_event_log->AddEvent(
591 fmt::format(
"Request received: "
592 "UpdateKeywords(id='{0}', keywords='{1}')",
599 }
catch (nlohmann::json::exception
const& e) {
602 fmt::format(
"UpdateKeywords(id='{}', ...): Failed to parse JSON",
id));
603 throw boost::enable_current_exception(
604 daqif::DaqException(
id, fmt::format(
"Invalid JSON string: {}", e.what())));
605 }
catch (std::invalid_argument
const& e) {
609 "UpdateKeywords(id='{}', ...): JSON could be parsed but was invalid "
612 throw boost::enable_current_exception(
613 daqif::DaqException(
id, fmt::format(
"Invalid JSON schema: {}", e.what())));
614 }
catch (std::exception
const& e) {
618 "UpdateKeywords(id='{}', ...): std::exception: '{}'",
id, e.what()));
619 throw boost::enable_current_exception(
620 daqif::DaqException(
id, fmt::format(
"std::exception: {}", e.what())));
622 throw boost::enable_current_exception(daqif::DaqException(
id,
"unknown error"));
625 self->m_mgr.UpdateKeywords(
id, parsed_keywords);
626 auto rep = self->m_mal.createDataEntity<daqif::DaqReply>();
628 rep->setError(
false);
631 LOG4CPLUS_ERROR(self->m_logger,
632 fmt::format(
"UpdateKeywords(id='{}'): {}",
id, e.what()));
633 throw boost::enable_current_exception(daqif::DaqException(
id, e.what()));
638 "UpdateKeywords(id='{}'): Invalid data acquisition id: ",
id, e.what()));
639 throw boost::enable_current_exception(daqif::DaqException(
id, e.what()));
640 }
catch (std::exception
const& e) {
644 "UpdateKeywords(id='{}', ...): std::exception: '{}'",
id, e.what()));
645 throw boost::enable_current_exception(
646 daqif::DaqException(
id, fmt::format(
"std::exception: {}", e.what())));
648 throw boost::enable_current_exception(daqif::DaqException(
id,
"unknown error"));
656 [self = shared_from_this(),
id]() {
657 self->m_event_log->AddEvent(
659 fmt::format(
"Request received: "
660 "GetStatus(id='{0}')",
664 LOG4CPLUS_INFO(self->m_logger, fmt::format(
"GetStatus(id='{}'): Enter",
id));
665 auto status = self->m_mgr.GetStatus(
id);
666 auto rep = self->m_mal.createDataEntity<daqif::DaqStatus>();
671 fmt::format(
"GetStatus(id='{}'): Set result -> {}",
id, status.state));
672 return boost::make_ready_future<std::shared_ptr<daqif::DaqStatus>>(rep);
673 }
catch (std::invalid_argument
const&) {
676 fmt::format(
"GetStatus(id='{}'): Invalid data acquisition id",
id));
677 return boost::make_exceptional_future<std::shared_ptr<daqif::DaqStatus>>(
678 boost::enable_current_exception(daqif::DaqException(
679 id, fmt::format(
"No data acquisition with id='{}'",
id))));
688 [self = shared_from_this()]() -> std::vector<std::shared_ptr<::daqif::DaqStatus>> {
693 auto daqs = self->m_mgr.GetDaqControllers();
694 std::vector<std::shared_ptr<daq::DaqController const>> active;
695 std::vector<std::shared_ptr<daqif::DaqStatus>> reply;
696 std::copy_if(daqs.begin(), daqs.end(), std::back_inserter(active), [](
auto daq_ctl) {
697 return !daq::IsFinalState(daq_ctl->GetState());
699 std::transform(active.begin(),
701 std::back_inserter(reply),
702 [&
mal = self->m_mal](
auto daq_ctl) {
703 auto mal_status = mal.createDataEntity<daqif::DaqStatus>();
704 *mal_status << daq_ctl->GetStatus()->GetStatus();
712 const std::string&
id, daqif::DaqState state, daqif::DaqSubState substate,
double timeout) {
713 using Seconds = std::chrono::duration<double>;
717 [=, self = shared_from_this()]() {
719 format(
"Request received: "
720 "AwaitDaqState(id='{}', "
721 "state={}, substate={}, "
729 return boost::make_exceptional_future<daq::Result<daq::Status>>(
730 std::invalid_argument(
731 format(
"Invalid argument `timeout`. Must be > 0", timeout)));
734 return boost::make_exceptional_future<daq::Result<daq::Status>>(
735 std::invalid_argument(fmt::format(
736 "Invalid state combination: {} and {}", state, substate)));
739 return self->m_mgr.AwaitDaqStateAsync(
740 id, daq_state, duration_cast<milliseconds>(Seconds(timeout)));
745 [
id, self = shared_from_this()](boost::future<daq::Result<daq::Status>> fut) {
747 auto [timeout, status] = fut.get();
748 auto mal_reply = self->m_mal.createDataEntity<daqif::AwaitDaqReply>();
750 mal_reply->setTimeout(timeout);
751 auto mal_status_ptr = mal_reply->getStatus();
752 assert(mal_status_ptr);
753 *mal_status_ptr << status;
755 fmt::format(
"Request completed: {}",
756 (timeout ?
"condition not yet satisfied (timeout)"
757 :
"condition satisfied")),
760 }
catch (std::exception
const& e) {
761 auto what = self->MakeExceptionMessageWithStatus(
id, std::current_exception());
764 fmt::format(
"Await state completed exceptionally\n\n{}", what),
766 throw boost::enable_current_exception(
767 daqif::DaqException(
id, fmt::format(
"Await state failed\n\n{}", what)));
770 id,
"Request completed exceptionally: Unknown exception", std::nullopt);
771 throw boost::enable_current_exception(
772 daqif::DaqException(
id,
"Uknown exception"));
778OcmDaqService::MakeExceptionMessageWithStatus(std::string
const&
id,
779 std::exception_ptr
const& exception)
const {
781 auto alerts_msg = std::string(
"n/a");
784 alerts_msg = fmt::format(
"{}", status.alerts);
785 }
catch (std::exception
const& e) {
786 LOG4CPLUS_WARN(m_logger, fmt::format(
"GetStatus({}) failed:",
id, e.what()));
788 return fmt::format(
"Errors(s):\n{}\nActive alert(s):\n{}", nested_msg, alerts_msg);
798 if (context.
id.empty()) {
811 for (
auto const& variant : spec.
sources) {
812 if (
auto const* ds = std::get_if<PrimaryDataSource>(&variant); ds !=
nullptr) {
813 context.
prim_sources.push_back({ds->source_name, ds->rr_uri});
814 }
else if (
auto const* ds = std::get_if<MetadataSource>(&variant); ds !=
nullptr) {
815 context.
meta_sources.push_back({ds->source_name, ds->rr_uri});
816 }
else if (
auto const* ds = std::get_if<FitsKeywordsSource>(&variant); ds !=
nullptr) {
817 context.
results.emplace_back(ds->source_name, ds->keywords);
818 }
else if (
auto const* ds = std::get_if<FitsFileSource>(&variant); ds !=
nullptr) {
819 context.
results.emplace_back(ds->source_name, ds->location);
821 LOG4CPLUS_ERROR(m_logger,
"Unknown variant encountered");
827 auto msg = fmt::format(
"DAQ with id='{0}' already exist", context.
id);
828 LOG4CPLUS_INFO(m_logger, msg);
829 throw boost::enable_current_exception(std::invalid_argument(msg));
Manager owns DaqController and FitsController (active data acquisitions) instances and multiplexes re...
virtual boost::future< State > StartDaqAsync(DaqContext ctx)=0
Start DaqController identified by id.
virtual Status GetStatus(std::string_view id) const =0
Get status.
virtual std::string MakeDaqId(std::chrono::system_clock::time_point *time=nullptr) const =0
Creates a new unique identifier based on the instrument id and current time.
virtual bool HaveDaq(std::string_view id, std::string_view file_id={}) const DAQ_NOEXCEPT=0
Query existing data acquisition by id and optional file_id.
Adapter object intended to be used in contexts without direct access to the output-stream object.
std::string Str() const
Convenience function for constructing a std::string from the exception.
Indicates keyword is invalid for some reason.
Contains data structure for FITS keywords.
Contains support functions for daqif.
Contains State support functions for daqif.
boost::future< std::vector< std::shared_ptr<::daqif::DaqStatus > > > GetActiveList() override
OcmDaqService(boost::asio::io_context &io_ctx, mal::Mal &mal, daq::Manager &mgr, std::string proc_name, std::string output_path, std::shared_ptr< daq::ObservableEventLog > event_log)
boost::future< std::shared_ptr<::daqif::DaqReply > > StopDaq(const std::string &id) override
boost::future< std::shared_ptr<::daqif::AwaitDaqReply > > AwaitDaqState(const std::string &id, daqif::DaqState state, daqif::DaqSubState substate, double timeout) override
boost::future< std::shared_ptr<::daqif::DaqReply > > AbortDaq(const std::string &id) override
boost::future< std::shared_ptr<::daqif::DaqReply > > StartDaqV2(const std::string &specification) override
boost::future< std::shared_ptr<::daqif::DaqReply > > UpdateKeywords(const std::string &id, const std::string &keywords) override
boost::future< std::shared_ptr<::daqif::DaqReply > > StartDaq(const std::string &id, const std::string &file_prefix, const std::string &primary_sources, const std::string &metadata_sources, const std::string &properties) override
boost::future< std::shared_ptr<::daqif::DaqReply > > ForceStopDaq(const std::string &id) override
boost::future< std::shared_ptr<::daqif::DaqStatus > > GetStatus(const std::string &id) override
boost::future< std::shared_ptr<::daqif::DaqReply > > ForceAbortDaq(const std::string &id) override
std::vector< KeywordVariant > KeywordVector
Vector of keywords.
std::vector< KeywordVariant > ParseJsonKeywords(char const *keywords)
Parse and return FITS keywords.
std::optional< std::chrono::milliseconds > await_completion_interval
DpSpec::SourceTypes ParseSource(Json const &json, JsonPointer const &breadcrumb)
StartDaqV2Spec ParseStartDaqV2Spec(nlohmann::json const &json)
Parse StartDaqSpec.
std::vector< DataSourceTypes > sources
Structure with a close mapping from JSON representation in the StartDaqV2 MAL request.
daqif::FullState MakeState(State state) noexcept
Converts daq::State to DaqSubstate.
std::string_view ToString(daqif::DaqState state) noexcept
@ Strict
Any error is considered fatal and may lead to the operation being aborted.
@ Tolerant
Errors that can be ignored with partial completion of a command will be tolerated and is reported as ...
bool IsStateValid(DaqState state, DaqSubState substate)
Validate state combination.
std::ostream & operator<<(std::ostream &os, ParsedSource const &s)
daq::DaqContext ParseStartDaqContext(std::string const &json_properties)
Parse the JSON properties user provides with StartDaq.
ParsedSource ParseSourceUri(std::string_view s)
Parse user provided string in the format "<name>@<rr-uri>".
std::vector< ParsedSource > ParseSourceUris(std::string_view s)
Parse user provided string in the format "<name>@<rr-uri>[ <name>@...]".
Declaration of OcmDaqService.
daq::DaqContext ParseStartDaqContext(std::string const &properties)
Parse the JSON properties user provides with StartDaq.
Contains declaration for for DaqController.
bool operator==(ParsedSource const &rhs) const
Event related to an action being requested or performed.
Structure carrying context needed to start a Data Acquisition and construct a Data Product Specificat...
std::vector< Source > meta_sources
DpParts results
Results from Data Acquisition (FITS files and keywords).
std::string process_name
User defined process name.
std::vector< daq::fits::KeywordVariant > keywords
Keyword list provided by OCM to Data Product.
std::vector< Source > prim_sources
std::chrono::milliseconds await_interval
Interval (and thus duration) of the requests sent to primary sources to await end of recording.
std::optional< json::StartDaqV2Spec > specification
Optional specification, if DAQ was started using StartDaqV2.
std::string file_id
Data Product FileId as specified by OLAS ICD.
std::string dp_name_prefix
Data product file name prefix.
std::chrono::system_clock::time_point creation_time
Time when DAQ was created.
std::string id
DAQ identfier, possibly provided by user.
Exception indicating the DAQ id was invalid.
Event directly related to user action, such as a command to do something.