ifw-daq  3.0.1
IFW Data Acquisition modules
startDaqV2.cpp
Go to the documentation of this file.
1 /**
2  * @file
3  * @ingroup daq_libjson
4  * @copyright
5  * (c) Copyright ESO 2022
6  * All Rights Reserved
7  * ESO (eso.org) is an Intergovernmental Organisation, and therefore special legal conditions apply.
8  */
10 
11 #include <fmt/format.h>
12 
13 #include "internalParseUtils.hpp"
14 
15 using Json = nlohmann::json;
16 using JsonPointer = nlohmann::json_pointer<Json>;
17 
18 namespace daq::json {
19 
21 
22 /**
23  * Parse the common parts of PrimaryDataSource and MetadataSource.
24  *
25  * {
26  * "sourceName": <str>,
27  * "rrUri": <str>,
28  * "keywordRules": [<keywordrules]
29  * }
30  */
32  Json const& json,
33  JsonPointer const& breadcrumb) {
34  AssertIsObject<ErrorType>(json, breadcrumb);
35  out.source_name = JsonObject<ErrorType, std::string>::Get(json, "sourceName", breadcrumb);
36  out.rr_uri = JsonObject<ErrorType, std::string>::Get(json, "rrUri", breadcrumb);
37  if (json.contains("initialKeywords")) {
38  out.initial_keywords =
39  ParseInitialKeywords(json["initialKeywords"], breadcrumb / "initialKeywords");
40  }
41  if (json.contains("keywordRules")) {
42  out.keyword_rules = ParseKeywordRules(json["keywordRules"], breadcrumb / "keywordRules");
43  }
44 }
45 
46 StartDaqV2Spec ParseStartDaqV2Spec(nlohmann::json const& json) {
47  try {
48  StartDaqV2Spec result = {};
49 
50  JsonPointer breadcrumb("");
51  AssertIsObject<StartDaqV2SpecError>(json, breadcrumb);
52 
53  if (json.contains("id")) {
54  result.id = JsonObject<ErrorType, std::string>::Get(json, "id", breadcrumb, true);
55  }
56 
57  if (json.contains("filePrefix")) {
58  result.file_prefix =
59  JsonObject<ErrorType, std::string>::Get(json, "filePrefix", breadcrumb, true);
60  }
61  if (json.contains("awaitCompletionInterval")) {
62  auto value =
63  JsonObject<ErrorType, double>::Get(json, "awaitCompletionInterval", breadcrumb);
64  if (value < 0.0) {
65  throw MakeParseException<ErrorType>(breadcrumb / "awaitCompletionInterval",
66  "interval must be > 0.0s");
67  }
69  std::chrono::duration_cast<std::chrono::milliseconds>(
70  std::chrono::duration<double>(value));
71  }
72 
73  if (json.contains("mergeTarget")) {
76  json["mergeTarget"], "sourceName", breadcrumb / "mergeTarget");
77  result.merge_target = m;
78  }
79  {
80  // Sources
81  auto [json_sources, breadcrumb_sources] = GetMember<ErrorType>(
82  json, "sources", breadcrumb, [](Json const& member, JsonPointer const& breadcrumb) {
83  AssertIsArray<ErrorType>(member, breadcrumb);
84  });
85  auto index = 0u;
86  auto found_merge_target = 0u;
87  for (auto const& json_source : json_sources) {
88  auto breadcrumb_source = breadcrumb_sources / index;
89  auto source_type =
90  JsonObject<ErrorType, std::string>::Get(json_source, "type", breadcrumb_source);
91  if (source_type == "primarySource") {
93  ParseDataSource(s, json_source, breadcrumb_source);
94  if (result.merge_target && result.merge_target->source_name == s.source_name) {
95  found_merge_target++;
96  }
97  result.sources.emplace_back(std::move(s));
98  } else if (source_type == "metadataSource") {
100  ParseDataSource(s, json_source, breadcrumb_source);
101  if (result.merge_target && result.merge_target->source_name == s.source_name) {
102  found_merge_target++;
103  }
104  result.sources.emplace_back(std::move(s));
105  } else if (source_type == "fitsKeywords") {
106  auto kws = ParseFitsKeywordsSource(json_source, breadcrumb_source);
107  if (result.merge_target &&
108  result.merge_target->source_name == kws.source_name) {
109  // Merge target cannot be keywords
110  throw MakeParseException<ErrorType>(
111  breadcrumb / "mergeTarget" / "sourceName",
112  "Merge target `{}` refers to fitsKeywords which cannot be a merge "
113  "target",
114  kws.source_name);
115  }
116  result.sources.emplace_back(std::move(kws));
117  } else if (source_type == "fitsFile") {
118  auto file = ParseFitsFileSource(json_source, breadcrumb_source);
119  if (result.merge_target &&
120  result.merge_target->source_name == file.source_name) {
121  found_merge_target++;
122  }
123  result.sources.emplace_back(std::move(file));
124  } else {
125  throw MakeUnknownVariantException<ErrorType>(
126  breadcrumb_source / "type",
127  "'primarySource', 'metadataSource', 'fitsFile' or 'fitsKeywords'",
128  source_type.c_str());
129  }
130  index++;
131  }
132  if (result.merge_target) {
133  if (found_merge_target == 0) {
134  throw MakeParseException<ErrorType>(
135  breadcrumb / "mergeTarget" / "sourceName",
136  "Merge target `{}` does not match any sources",
137  result.merge_target->source_name);
138  }
139  else if (found_merge_target > 1) {
140  throw MakeParseException<ErrorType>(
141  breadcrumb / "mergeTarget" / "sourceName",
142  "Merge target `{}` matches more than one source",
143  result.merge_target->source_name);
144  }
145  }
146  }
147  if (json.contains("receivers")) {
148  result.receivers = ParseReceiverList(json["receivers"], breadcrumb / "receivers");
149  }
150  return result;
151  } catch (ErrorType const&) {
152  throw;
153  } catch (SchemaError const& e) {
154  throw ErrorType(e.what());
155  } catch (std::exception const& e) { // GCOVR_EXCL_START
156  std::throw_with_nested(
157  StartDaqV2SpecError(fmt::format("Unknown parsing error: {}", e.what())));
158  } // GCOVR_EXCL_STOP
159 }
160 
162  StartDaqV2Spec::PrimaryDataSource const& rhs) noexcept {
163  return lhs.source_name == rhs.source_name && lhs.rr_uri == rhs.rr_uri &&
164  lhs.keyword_rules == rhs.keyword_rules;
165 }
166 
168  StartDaqV2Spec::MetadataSource const& rhs) noexcept {
169  return lhs.source_name == rhs.source_name && lhs.rr_uri == rhs.rr_uri &&
170  lhs.keyword_rules == rhs.keyword_rules;
171 }
172 
174  StartDaqV2Spec::MergeTarget const& rhs) noexcept {
175  return lhs.source_name == rhs.source_name;
176 }
177 
178 bool operator==(StartDaqV2Spec const& lhs, StartDaqV2Spec const& rhs) noexcept {
179  return lhs.id == rhs.id && lhs.file_prefix == rhs.file_prefix &&
180  lhs.await_completion_interval == rhs.await_completion_interval &&
181  lhs.merge_target == rhs.merge_target && lhs.sources == rhs.sources &&
182  lhs.receivers == rhs.receivers;
183 }
184 
185 // NOLINTNEXTLINE
186 void to_json(nlohmann::json& out, StartDaqV2Spec::DataSource const& s) {
187  out = Json{{"sourceName", s.source_name}, {"rrUri", s.rr_uri}};
188  if (s.initial_keywords.has_value()) {
189  out["initialKeywords"] = *s.initial_keywords;
190  }
191  if (!s.keyword_rules.empty()) {
192  out["keywordRules"] = s.keyword_rules;
193  }
194 }
195 
196 // NOLINTNEXTLINE
197 void to_json(nlohmann::json& out, StartDaqV2Spec::PrimaryDataSource const& s) {
198  to_json(out, static_cast<StartDaqV2Spec::DataSource const&>(s));
199  out["type"] = "primarySource";
200 }
201 
202 // NOLINTNEXTLINE
203 void to_json(nlohmann::json& out, StartDaqV2Spec::MetadataSource const& s) {
204  to_json(out, static_cast<StartDaqV2Spec::DataSource const&>(s));
205  out["type"] = "metadataSource";
206 }
207 
208 // NOLINTNEXTLINE
209 void to_json(nlohmann::json& out, StartDaqV2Spec::DataSourceTypes const& s) {
210  std::visit([&](auto const& ds) { to_json(out, ds); }, s);
211 }
212 
213 // NOLINTNEXTLINE
214 void to_json(nlohmann::json& out, StartDaqV2Spec::MergeTarget const& t) {
215  out = Json{{"sourceName", t.source_name}};
216 }
217 
218 // NOLINTNEXTLINE
219 void to_json(nlohmann::json& json, StartDaqV2Spec const& spec) {
220  json = nlohmann::json{
221  {"id", spec.id}, {"filePrefix", spec.file_prefix}, {"sources", spec.sources}};
222  if (spec.await_completion_interval.has_value()) {
223  json["awaitCompletionInterval"] = std::chrono::duration_cast<std::chrono::duration<double>>(
225  .count();
226  }
227  if (spec.merge_target.has_value()) {
228  json["mergeTarget"] = *spec.merge_target;
229  }
230  json["receivers"] = spec.receivers;
231 }
232 
233 } // namespace daq::json
nlohmann::json_pointer< Json > JsonPointer
Definition: json.cpp:19
nlohmann::json Json
Definition: json.cpp:18
StartDaqV2SpecError ErrorType
Definition: startDaqV2.cpp:20
KeywordRules ParseKeywordRules(Json const &json, JsonPointer const &breadcrumb)
InitialKeywords ParseInitialKeywords(nlohmann::json const &json, nlohmann::json_pointer< nlohmann::json > const &breadcrumb)
std::optional< MergeTarget > merge_target
Definition: startDaqV2.hpp:54
nlohmann::json Json
nlohmann::json_pointer< Json > JsonPointer
std::optional< std::chrono::milliseconds > await_completion_interval
Definition: startDaqV2.hpp:55
void ParseDataSource(StartDaqV2Spec::DataSource &out, Json const &json, JsonPointer const &breadcrumb)
Parse the common parts of PrimaryDataSource and MetadataSource.
Definition: startDaqV2.cpp:31
FitsFileSource ParseFitsFileSource(Json const &json, JsonPointer const &breadcrumb)
bool operator==(KeywordFilter const &lhs, KeywordFilter const &rhs) noexcept
FitsKeywordsSource ParseFitsKeywordsSource(Json const &json, JsonPointer const &breadcrumb)
std::variant< PrimaryDataSource, MetadataSource, FitsKeywordsSource, FitsFileSource > DataSourceTypes
Definition: startDaqV2.hpp:44
ReceiverList ParseReceiverList(Json const &json, JsonPointer const &breadcrumb)
StartDaqV2Spec ParseStartDaqV2Spec(nlohmann::json const &json)
Parse StartDaqSpec.
Definition: startDaqV2.cpp:46
void to_json(nlohmann::json &out, KeywordFilter const &s)
std::vector< DataSourceTypes > sources
Definition: startDaqV2.hpp:51
Structure with a close mapping from JSON representation in the StartDaqV2 MAL request.
Definition: startDaqV2.hpp:33
static T Get(Json const &json, char const *name, JsonPointer const &breadcrumb, bool allow_empty=true)
JSON Schema error.
Definition: schemaError.hpp:18
std::optional< InitialKeywords > initial_keywords
Definition: startDaqV2.hpp:37